Make COLLAPSIBLE_IF consider if let

This commit is contained in:
mcarton 2016-06-21 23:53:45 +02:00
parent f6ba217c1c
commit ea76ac5562
No known key found for this signature in database
GPG key ID: 5E427C794CBA45E8
3 changed files with 126 additions and 29 deletions

View file

@ -53,37 +53,64 @@ impl EarlyLintPass for CollapsibleIf {
}
}
fn check_if(cx: &EarlyContext, e: &ast::Expr) {
if let ast::ExprKind::If(ref check, ref then, ref else_) = e.node {
if let Some(ref else_) = *else_ {
if_let_chain! {[
let ast::ExprKind::Block(ref block) = else_.node,
block.stmts.is_empty(),
let Some(ref else_) = block.expr,
let ast::ExprKind::If(_, _, _) = else_.node
], {
fn check_if(cx: &EarlyContext, expr: &ast::Expr) {
match expr.node {
ast::ExprKind::If(ref check, ref then, ref else_) => {
if let Some(ref else_) = *else_ {
check_collapsible_maybe_if_let(cx, else_);
} else {
check_collapsible_no_if_let(cx, expr, check, then);
}
}
ast::ExprKind::IfLet(_, _, _, Some(ref else_)) => {
check_collapsible_maybe_if_let(cx, else_);
}
_ => (),
}
}
fn check_collapsible_maybe_if_let(cx: &EarlyContext, else_: &ast::Expr) {
if_let_chain! {[
let ast::ExprKind::Block(ref block) = else_.node,
block.stmts.is_empty(),
let Some(ref else_) = block.expr,
], {
match else_.node {
ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) => {
span_lint_and_then(cx,
COLLAPSIBLE_IF,
block.span,
"this `else { if .. }` block can be collapsed", |db| {
db.span_suggestion(block.span, "try", snippet_block(cx, else_.span, "..").into_owned());
});
}}
} else if let Some(&ast::Expr { node: ast::ExprKind::If(ref check_inner, ref content, None), span: sp, .. }) =
single_stmt_of_block(then) {
if e.span.expn_id != sp.expn_id {
return;
}
span_lint_and_then(cx, COLLAPSIBLE_IF, e.span, "this if statement can be collapsed", |db| {
db.span_suggestion(e.span,
"try",
format!("if {} && {} {}",
check_to_string(cx, check),
check_to_string(cx, check_inner),
snippet_block(cx, content.span, "..")));
});
_ => (),
}
}
}}
}
fn check_collapsible_no_if_let(
cx: &EarlyContext,
expr: &ast::Expr,
check: &ast::Expr,
then: &ast::Block,
) {
if_let_chain! {[
let Some(inner) = single_stmt_of_block(then),
let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node,
], {
if expr.span.expn_id != inner.span.expn_id {
return;
}
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
db.span_suggestion(expr.span,
"try",
format!("if {} && {} {}",
check_to_string(cx, check),
check_to_string(cx, check_inner),
snippet_block(cx, content.span, "..")));
});
}}
}
fn requires_brackets(e: &ast::Expr) -> bool {

View file

@ -26,9 +26,10 @@ fn main() {
// Collaspe `else { if .. }` to `else if ..`
if x == "hello" {
print!("Hello ");
} else { //~ERROR: this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if y == "world"
} else {
//~^ ERROR: this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if y == "world"
if y == "world" {
println!("world!")
}
@ -36,9 +37,21 @@ fn main() {
if x == "hello" {
print!("Hello ");
} else { //~ERROR this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if y == "world"
} else {
//~^ ERROR: this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if let Some(42)
if let Some(42) = Some(42) {
println!("world!")
}
}
if x == "hello" {
print!("Hello ");
} else {
//~^ ERROR this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if y == "world"
if y == "world" {
println!("world")
}
@ -47,6 +60,62 @@ fn main() {
}
}
if x == "hello" {
print!("Hello ");
} else {
//~^ ERROR this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if let Some(42)
if let Some(42) = Some(42) {
println!("world")
}
else {
println!("!")
}
}
if let Some(42) = Some(42) {
print!("Hello ");
} else {
//~^ ERROR this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if let Some(42)
if let Some(42) = Some(42) {
println!("world")
}
else {
println!("!")
}
}
if let Some(42) = Some(42) {
print!("Hello ");
} else {
//~^ ERROR this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if x == "hello"
if x == "hello" {
println!("world")
}
else {
println!("!")
}
}
if let Some(42) = Some(42) {
print!("Hello ");
} else {
//~^ ERROR this `else { if .. }`
//~| HELP try
//~| SUGGESTION } else if let Some(42)
if let Some(42) = Some(42) {
println!("world")
}
else {
println!("!")
}
}
// Works because any if with an else statement cannot be collapsed.
if x == "hello" {
if y == "world" {

View file

@ -8,6 +8,7 @@
#![allow(unused_variables)]
#![allow(cyclomatic_complexity)]
#![allow(blacklisted_name)]
#![allow(collapsible_if)]
fn bar<T>(_: T) {}
fn foo() -> bool { unimplemented!() }