Skip to content

Commit d9cc71f

Browse files
committed
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
2 parents 19c7885 + 05ae391 commit d9cc71f

File tree

3 files changed

+160
-64
lines changed

3 files changed

+160
-64
lines changed

clippy_lints/src/formatting.rs

+55-17
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ declare_clippy_lint! {
3131
"suspicious formatting of `*=`, `-=` or `!=`"
3232
}
3333

34-
/// **What it does:** Checks for formatting of `else if`. It lints if the `else`
35-
/// and `if` are not on the same line or the `else` seems to be missing.
34+
/// **What it does:** Checks for formatting of `else`. It lints if the `else`
35+
/// is followed immediately by a newline or the `else` seems to be missing.
3636
///
3737
/// **Why is this bad?** This is probably some refactoring remnant, even if the
3838
/// code is correct, it might look confusing.
@@ -42,19 +42,29 @@ declare_clippy_lint! {
4242
/// **Example:**
4343
/// ```rust,ignore
4444
/// if foo {
45+
/// } { // looks like an `else` is missing here
46+
/// }
47+
///
48+
/// if foo {
4549
/// } if bar { // looks like an `else` is missing here
4650
/// }
4751
///
4852
/// if foo {
4953
/// } else
5054
///
55+
/// { // this is the `else` block of the previous `if`, but should it be?
56+
/// }
57+
///
58+
/// if foo {
59+
/// } else
60+
///
5161
/// if bar { // this is the `else` block of the previous `if`, but should it be?
5262
/// }
5363
/// ```
5464
declare_clippy_lint! {
5565
pub SUSPICIOUS_ELSE_FORMATTING,
5666
style,
57-
"suspicious formatting of `else if`"
67+
"suspicious formatting of `else`"
5868
}
5969

6070
/// **What it does:** Checks for possible missing comma in an array. It lints if
@@ -96,7 +106,7 @@ impl EarlyLintPass for Formatting {
96106
match (&w[0].node, &w[1].node) {
97107
(&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second))
98108
| (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
99-
check_consecutive_ifs(cx, first, second);
109+
check_missing_else(cx, first, second);
100110
},
101111
_ => (),
102112
}
@@ -105,7 +115,7 @@ impl EarlyLintPass for Formatting {
105115

106116
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
107117
check_assign(cx, expr);
108-
check_else_if(cx, expr);
118+
check_else(cx, expr);
109119
check_array(cx, expr);
110120
}
111121
}
@@ -139,10 +149,18 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &ast::Expr) {
139149
}
140150
}
141151

142-
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else if`.
143-
fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
152+
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
153+
fn check_else(cx: &EarlyContext<'_>, expr: &ast::Expr) {
144154
if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
145-
if unsugar_if(else_).is_some() && !differing_macro_contexts(then.span, else_.span) && !in_macro(then.span) {
155+
if (is_block(else_) || unsugar_if(else_).is_some())
156+
&& !differing_macro_contexts(then.span, else_.span)
157+
&& !in_macro(then.span)
158+
{
159+
// workaround for rust-lang/rust#43081
160+
if expr.span.lo().0 == 0 && expr.span.hi().0 == 0 {
161+
return;
162+
}
163+
146164
// this will be a span from the closing ‘}’ of the “then” block (excluding) to
147165
// the
148166
// “if” of the “else if” block (excluding)
@@ -154,14 +172,19 @@ fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
154172
let else_pos = else_snippet.find("else").expect("there must be a `else` here");
155173

156174
if else_snippet[else_pos..].contains('\n') {
175+
let else_desc = if unsugar_if(else_).is_some() { "if" } else { "{..}" };
176+
157177
span_note_and_lint(
158178
cx,
159179
SUSPICIOUS_ELSE_FORMATTING,
160180
else_span,
161-
"this is an `else if` but the formatting might hide it",
181+
&format!("this is an `else {}` but the formatting might hide it", else_desc),
162182
else_span,
163-
"to remove this lint, remove the `else` or remove the new line between `else` \
164-
and `if`",
183+
&format!(
184+
"to remove this lint, remove the `else` or remove the new line between \
185+
`else` and `{}`",
186+
else_desc,
187+
),
165188
);
166189
}
167190
}
@@ -200,32 +223,47 @@ fn check_array(cx: &EarlyContext<'_>, expr: &ast::Expr) {
200223
}
201224
}
202225

203-
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs.
204-
fn check_consecutive_ifs(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
226+
fn check_missing_else(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
205227
if !differing_macro_contexts(first.span, second.span)
206228
&& !in_macro(first.span)
207229
&& unsugar_if(first).is_some()
208-
&& unsugar_if(second).is_some()
230+
&& (is_block(second) || unsugar_if(second).is_some())
209231
{
210232
// where the else would be
211233
let else_span = first.span.between(second.span);
212234

213235
if let Some(else_snippet) = snippet_opt(cx, else_span) {
214236
if !else_snippet.contains('\n') {
237+
let (looks_like, next_thing) = if unsugar_if(second).is_some() {
238+
("an `else if`", "the second `if`")
239+
} else {
240+
("an `else {..}`", "the next block")
241+
};
242+
215243
span_note_and_lint(
216244
cx,
217245
SUSPICIOUS_ELSE_FORMATTING,
218246
else_span,
219-
"this looks like an `else if` but the `else` is missing",
247+
&format!("this looks like {} but the `else` is missing", looks_like),
220248
else_span,
221-
"to remove this lint, add the missing `else` or add a new line before the second \
222-
`if`",
249+
&format!(
250+
"to remove this lint, add the missing `else` or add a new line before {}",
251+
next_thing,
252+
),
223253
);
224254
}
225255
}
226256
}
227257
}
228258

259+
fn is_block(expr: &ast::Expr) -> bool {
260+
if let ast::ExprKind::Block(..) = expr.node {
261+
true
262+
} else {
263+
false
264+
}
265+
}
266+
229267
/// Match `if` or `if let` expressions and return the `then` and `else` block.
230268
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
231269
match expr.node {

tests/ui/formatting.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
fn foo() -> bool { true }
1717

1818
fn main() {
19-
// weird `else if` formatting:
19+
// weird `else` formatting:
20+
if foo() {
21+
} {
22+
}
23+
2024
if foo() {
2125
} if foo() {
2226
}
@@ -41,6 +45,17 @@ fn main() {
4145
let _ = 0;
4246
};
4347

48+
if foo() {
49+
} else
50+
{
51+
}
52+
53+
if foo() {
54+
}
55+
else
56+
{
57+
}
58+
4459
if foo() {
4560
} else
4661
if foo() { // the span of the above error should continue here
@@ -53,6 +68,20 @@ fn main() {
5368
}
5469

5570
// those are ok:
71+
if foo() {
72+
}
73+
{
74+
}
75+
76+
if foo() {
77+
} else {
78+
}
79+
80+
if foo() {
81+
}
82+
else {
83+
}
84+
5685
if foo() {
5786
}
5887
if foo() {

tests/ui/formatting.stderr

+75-46
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,119 @@
1-
error: this looks like an `else if` but the `else` is missing
1+
error: this looks like an `else {..}` but the `else` is missing
22
--> $DIR/formatting.rs:21:6
33
|
4-
21 | } if foo() {
4+
21 | } {
55
| ^
66
|
77
= note: `-D clippy::suspicious-else-formatting` implied by `-D warnings`
8+
= note: to remove this lint, add the missing `else` or add a new line before the next block
9+
10+
error: this looks like an `else if` but the `else` is missing
11+
--> $DIR/formatting.rs:25:6
12+
|
13+
25 | } if foo() {
14+
| ^
15+
|
816
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
917

1018
error: this looks like an `else if` but the `else` is missing
11-
--> $DIR/formatting.rs:28:10
19+
--> $DIR/formatting.rs:32:10
1220
|
13-
28 | } if foo() {
21+
32 | } if foo() {
1422
| ^
1523
|
1624
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
1725

1826
error: this looks like an `else if` but the `else` is missing
19-
--> $DIR/formatting.rs:36:10
27+
--> $DIR/formatting.rs:40:10
2028
|
21-
36 | } if foo() {
29+
40 | } if foo() {
2230
| ^
2331
|
2432
= note: to remove this lint, add the missing `else` or add a new line before the second `if`
2533

34+
error: this is an `else {..}` but the formatting might hide it
35+
--> $DIR/formatting.rs:49:6
36+
|
37+
49 | } else
38+
| ______^
39+
50 | | {
40+
| |____^
41+
|
42+
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
43+
44+
error: this is an `else {..}` but the formatting might hide it
45+
--> $DIR/formatting.rs:54:6
46+
|
47+
54 | }
48+
| ______^
49+
55 | | else
50+
56 | | {
51+
| |____^
52+
|
53+
= note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}`
54+
2655
error: this is an `else if` but the formatting might hide it
27-
--> $DIR/formatting.rs:45:6
56+
--> $DIR/formatting.rs:60:6
2857
|
29-
45 | } else
58+
60 | } else
3059
| ______^
31-
46 | | if foo() { // the span of the above error should continue here
60+
61 | | if foo() { // the span of the above error should continue here
3261
| |____^
3362
|
3463
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
3564

3665
error: this is an `else if` but the formatting might hide it
37-
--> $DIR/formatting.rs:50:6
66+
--> $DIR/formatting.rs:65:6
3867
|
39-
50 | }
68+
65 | }
4069
| ______^
41-
51 | | else
42-
52 | | if foo() { // the span of the above error should continue here
70+
66 | | else
71+
67 | | if foo() { // the span of the above error should continue here
4372
| |____^
4473
|
4574
= note: to remove this lint, remove the `else` or remove the new line between `else` and `if`
4675

4776
error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)`
48-
--> $DIR/formatting.rs:77:6
49-
|
50-
77 | a =- 35;
51-
| ^^^^
52-
|
53-
= note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings`
54-
= note: to remove this lint, use either `-=` or `= -`
77+
--> $DIR/formatting.rs:106:6
78+
|
79+
106 | a =- 35;
80+
| ^^^^
81+
|
82+
= note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings`
83+
= note: to remove this lint, use either `-=` or `= -`
5584

5685
error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)`
57-
--> $DIR/formatting.rs:78:6
58-
|
59-
78 | a =* &191;
60-
| ^^^^
61-
|
62-
= note: to remove this lint, use either `*=` or `= *`
86+
--> $DIR/formatting.rs:107:6
87+
|
88+
107 | a =* &191;
89+
| ^^^^
90+
|
91+
= note: to remove this lint, use either `*=` or `= *`
6392

6493
error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)`
65-
--> $DIR/formatting.rs:81:6
66-
|
67-
81 | b =! false;
68-
| ^^^^
69-
|
70-
= note: to remove this lint, use either `!=` or `= !`
94+
--> $DIR/formatting.rs:110:6
95+
|
96+
110 | b =! false;
97+
| ^^^^
98+
|
99+
= note: to remove this lint, use either `!=` or `= !`
71100

72101
error: possibly missing a comma here
73-
--> $DIR/formatting.rs:90:19
74-
|
75-
90 | -1, -2, -3 // <= no comma here
76-
| ^
77-
|
78-
= note: `-D clippy::possible-missing-comma` implied by `-D warnings`
79-
= note: to remove this lint, add a comma or write the expr in a single line
102+
--> $DIR/formatting.rs:119:19
103+
|
104+
119 | -1, -2, -3 // <= no comma here
105+
| ^
106+
|
107+
= note: `-D clippy::possible-missing-comma` implied by `-D warnings`
108+
= note: to remove this lint, add a comma or write the expr in a single line
80109

81110
error: possibly missing a comma here
82-
--> $DIR/formatting.rs:94:19
83-
|
84-
94 | -1, -2, -3 // <= no comma here
85-
| ^
86-
|
87-
= note: to remove this lint, add a comma or write the expr in a single line
111+
--> $DIR/formatting.rs:123:19
112+
|
113+
123 | -1, -2, -3 // <= no comma here
114+
| ^
115+
|
116+
= note: to remove this lint, add a comma or write the expr in a single line
88117

89-
error: aborting due to 10 previous errors
118+
error: aborting due to 13 previous errors
90119

0 commit comments

Comments
 (0)