Auto merge of #3341 - HMPerson1:possibly_missing_else, r=phansch

Teach `suspicious_else_formatting` about `if .. {..} {..}`

We essentially treat bare blocks `{..}` identically to `if .. {..}`, except for different lint messages.

Fixes #3044
This commit is contained in:
bors 2018-12-22 13:26:22 +00:00
commit d9cc71fc42
3 changed files with 160 additions and 64 deletions

View file

@ -31,8 +31,8 @@ declare_clippy_lint! {
"suspicious formatting of `*=`, `-=` or `!=`" "suspicious formatting of `*=`, `-=` or `!=`"
} }
/// **What it does:** Checks for formatting of `else if`. It lints if the `else` /// **What it does:** Checks for formatting of `else`. It lints if the `else`
/// and `if` are not on the same line or the `else` seems to be missing. /// is followed immediately by a newline or the `else` seems to be missing.
/// ///
/// **Why is this bad?** This is probably some refactoring remnant, even if the /// **Why is this bad?** This is probably some refactoring remnant, even if the
/// code is correct, it might look confusing. /// code is correct, it might look confusing.
@ -42,19 +42,29 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust,ignore /// ```rust,ignore
/// if foo { /// if foo {
/// } { // looks like an `else` is missing here
/// }
///
/// if foo {
/// } if bar { // looks like an `else` is missing here /// } if bar { // looks like an `else` is missing here
/// } /// }
/// ///
/// if foo { /// if foo {
/// } else /// } else
/// ///
/// { // this is the `else` block of the previous `if`, but should it be?
/// }
///
/// if foo {
/// } else
///
/// if bar { // this is the `else` block of the previous `if`, but should it be? /// if bar { // this is the `else` block of the previous `if`, but should it be?
/// } /// }
/// ``` /// ```
declare_clippy_lint! { declare_clippy_lint! {
pub SUSPICIOUS_ELSE_FORMATTING, pub SUSPICIOUS_ELSE_FORMATTING,
style, style,
"suspicious formatting of `else if`" "suspicious formatting of `else`"
} }
/// **What it does:** Checks for possible missing comma in an array. It lints if /// **What it does:** Checks for possible missing comma in an array. It lints if
@ -96,7 +106,7 @@ impl EarlyLintPass for Formatting {
match (&w[0].node, &w[1].node) { match (&w[0].node, &w[1].node) {
(&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second)) (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second))
| (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => { | (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
check_consecutive_ifs(cx, first, second); check_missing_else(cx, first, second);
}, },
_ => (), _ => (),
} }
@ -105,7 +115,7 @@ impl EarlyLintPass for Formatting {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
check_assign(cx, expr); check_assign(cx, expr);
check_else_if(cx, expr); check_else(cx, expr);
check_array(cx, expr); check_array(cx, expr);
} }
} }
@ -139,10 +149,18 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &ast::Expr) {
} }
} }
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else if`. /// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { fn check_else(cx: &EarlyContext<'_>, expr: &ast::Expr) {
if let Some((then, &Some(ref else_))) = unsugar_if(expr) { if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
if unsugar_if(else_).is_some() && !differing_macro_contexts(then.span, else_.span) && !in_macro(then.span) { if (is_block(else_) || unsugar_if(else_).is_some())
&& !differing_macro_contexts(then.span, else_.span)
&& !in_macro(then.span)
{
// workaround for rust-lang/rust#43081
if expr.span.lo().0 == 0 && expr.span.hi().0 == 0 {
return;
}
// this will be a span from the closing } of the “then” block (excluding) to // this will be a span from the closing } of the “then” block (excluding) to
// the // the
// “if” of the “else if” block (excluding) // “if” of the “else if” block (excluding)
@ -154,14 +172,19 @@ fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
let else_pos = else_snippet.find("else").expect("there must be a `else` here"); let else_pos = else_snippet.find("else").expect("there must be a `else` here");
if else_snippet[else_pos..].contains('\n') { if else_snippet[else_pos..].contains('\n') {
let else_desc = if unsugar_if(else_).is_some() { "if" } else { "{..}" };
span_note_and_lint( span_note_and_lint(
cx, cx,
SUSPICIOUS_ELSE_FORMATTING, SUSPICIOUS_ELSE_FORMATTING,
else_span, else_span,
"this is an `else if` but the formatting might hide it", &format!("this is an `else {}` but the formatting might hide it", else_desc),
else_span, else_span,
"to remove this lint, remove the `else` or remove the new line between `else` \ &format!(
and `if`", "to remove this lint, remove the `else` or remove the new line between \
`else` and `{}`",
else_desc,
),
); );
} }
} }
@ -200,32 +223,47 @@ fn check_array(cx: &EarlyContext<'_>, expr: &ast::Expr) {
} }
} }
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs. fn check_missing_else(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
fn check_consecutive_ifs(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
if !differing_macro_contexts(first.span, second.span) if !differing_macro_contexts(first.span, second.span)
&& !in_macro(first.span) && !in_macro(first.span)
&& unsugar_if(first).is_some() && unsugar_if(first).is_some()
&& unsugar_if(second).is_some() && (is_block(second) || unsugar_if(second).is_some())
{ {
// where the else would be // where the else would be
let else_span = first.span.between(second.span); let else_span = first.span.between(second.span);
if let Some(else_snippet) = snippet_opt(cx, else_span) { if let Some(else_snippet) = snippet_opt(cx, else_span) {
if !else_snippet.contains('\n') { if !else_snippet.contains('\n') {
let (looks_like, next_thing) = if unsugar_if(second).is_some() {
("an `else if`", "the second `if`")
} else {
("an `else {..}`", "the next block")
};
span_note_and_lint( span_note_and_lint(
cx, cx,
SUSPICIOUS_ELSE_FORMATTING, SUSPICIOUS_ELSE_FORMATTING,
else_span, else_span,
"this looks like an `else if` but the `else` is missing", &format!("this looks like {} but the `else` is missing", looks_like),
else_span, else_span,
"to remove this lint, add the missing `else` or add a new line before the second \ &format!(
`if`", "to remove this lint, add the missing `else` or add a new line before {}",
next_thing,
),
); );
} }
} }
} }
} }
fn is_block(expr: &ast::Expr) -> bool {
if let ast::ExprKind::Block(..) = expr.node {
true
} else {
false
}
}
/// Match `if` or `if let` expressions and return the `then` and `else` block. /// Match `if` or `if let` expressions and return the `then` and `else` block.
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> { fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
match expr.node { match expr.node {

View file

@ -16,7 +16,11 @@
fn foo() -> bool { true } fn foo() -> bool { true }
fn main() { fn main() {
// weird `else if` formatting: // weird `else` formatting:
if foo() {
} {
}
if foo() { if foo() {
} if foo() { } if foo() {
} }
@ -41,6 +45,17 @@ fn main() {
let _ = 0; let _ = 0;
}; };
if foo() {
} else
{
}
if foo() {
}
else
{
}
if foo() { if foo() {
} else } else
if foo() { // the span of the above error should continue here if foo() { // the span of the above error should continue here
@ -53,6 +68,20 @@ fn main() {
} }
// those are ok: // those are ok:
if foo() {
}
{
}
if foo() {
} else {
}
if foo() {
}
else {
}
if foo() { if foo() {
} }
if foo() { if foo() {

View file

@ -1,90 +1,119 @@
error: this looks like an `else if` but the `else` is missing error: this looks like an `else {..}` but the `else` is missing
--> $DIR/formatting.rs:21:6 --> $DIR/formatting.rs:21:6
| |
21 | } if foo() { 21 | } {
| ^ | ^
| |
= note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
= note: to remove this lint, add the missing `else` or add a new line before the second `if` = note: to remove this lint, add the missing `else` or add a new line before the next block
error: this looks like an `else if` but the `else` is missing error: this looks like an `else if` but the `else` is missing
--> $DIR/formatting.rs:28:10 --> $DIR/formatting.rs:25:6
| |
28 | } if foo() { 25 | } if foo() {
| ^ | ^
| |
= note: to remove this lint, add the missing `else` or add a new line before the second `if` = note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this looks like an `else if` but the `else` is missing error: this looks like an `else if` but the `else` is missing
--> $DIR/formatting.rs:36:10 --> $DIR/formatting.rs:32:10
| |
36 | } if foo() { 32 | } if foo() {
| ^ | ^
| |
= note: to remove this lint, add the missing `else` or add a new line before the second `if` = note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this looks like an `else if` but the `else` is missing
--> $DIR/formatting.rs:40:10
|
40 | } if foo() {
| ^
|
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
error: this is an `else {..}` but the formatting might hide it
--> $DIR/formatting.rs:49:6
|
49 | } else
| ______^
50 | | {
| |____^
|
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
error: this is an `else {..}` but the formatting might hide it
--> $DIR/formatting.rs:54:6
|
54 | }
| ______^
55 | | else
56 | | {
| |____^
|
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
error: this is an `else if` but the formatting might hide it error: this is an `else if` but the formatting might hide it
--> $DIR/formatting.rs:45:6 --> $DIR/formatting.rs:60:6
| |
45 | } else 60 | } else
| ______^ | ______^
46 | | if foo() { // the span of the above error should continue here 61 | | if foo() { // the span of the above error should continue here
| |____^ | |____^
| |
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if` = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
error: this is an `else if` but the formatting might hide it error: this is an `else if` but the formatting might hide it
--> $DIR/formatting.rs:50:6 --> $DIR/formatting.rs:65:6
| |
50 | } 65 | }
| ______^ | ______^
51 | | else 66 | | else
52 | | if foo() { // the span of the above error should continue here 67 | | if foo() { // the span of the above error should continue here
| |____^ | |____^
| |
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if` = note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)`
--> $DIR/formatting.rs:77:6 --> $DIR/formatting.rs:106:6
| |
77 | a =- 35; 106 | a =- 35;
| ^^^^ | ^^^^
| |
= note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings` = note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings`
= note: to remove this lint, use either `-=` or `= -` = note: to remove this lint, use either `-=` or `= -`
error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)`
--> $DIR/formatting.rs:78:6 --> $DIR/formatting.rs:107:6
| |
78 | a =* &191; 107 | a =* &191;
| ^^^^ | ^^^^
| |
= note: to remove this lint, use either `*=` or `= *` = note: to remove this lint, use either `*=` or `= *`
error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)` error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)`
--> $DIR/formatting.rs:81:6 --> $DIR/formatting.rs:110:6
| |
81 | b =! false; 110 | b =! false;
| ^^^^ | ^^^^
| |
= note: to remove this lint, use either `!=` or `= !` = note: to remove this lint, use either `!=` or `= !`
error: possibly missing a comma here error: possibly missing a comma here
--> $DIR/formatting.rs:90:19 --> $DIR/formatting.rs:119:19
| |
90 | -1, -2, -3 // <= no comma here 119 | -1, -2, -3 // <= no comma here
| ^ | ^
| |
= note: `-D clippy::possible-missing-comma` implied by `-D warnings` = note: `-D clippy::possible-missing-comma` implied by `-D warnings`
= note: to remove this lint, add a comma or write the expr in a single line = note: to remove this lint, add a comma or write the expr in a single line
error: possibly missing a comma here error: possibly missing a comma here
--> $DIR/formatting.rs:94:19 --> $DIR/formatting.rs:123:19
| |
94 | -1, -2, -3 // <= no comma here 123 | -1, -2, -3 // <= no comma here
| ^ | ^
| |
= note: to remove this lint, add a comma or write the expr in a single line = note: to remove this lint, add a comma or write the expr in a single line
error: aborting due to 10 previous errors error: aborting due to 13 previous errors