Skip to content

Commit 36f2ac8

Browse files
committed
Teach suspicious_else_formatting about if .. {..} {..}
1 parent 80c07d4 commit 36f2ac8

File tree

3 files changed

+158
-64
lines changed

3 files changed

+158
-64
lines changed

clippy_lints/src/formatting.rs

+53-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,12 @@ 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) && !in_macro(then.span)
157+
{
146158
// this will be a span from the closing ‘}’ of the “then” block (excluding) to
147159
// the
148160
// “if” of the “else if” block (excluding)
@@ -154,14 +166,23 @@ fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
154166
let else_pos = else_snippet.find("else").expect("there must be a `else` here");
155167

156168
if else_snippet[else_pos..].contains('\n') {
169+
let else_desc = if unsugar_if(else_).is_some() {
170+
"if"
171+
} else {
172+
"{..}"
173+
};
174+
157175
span_note_and_lint(
158176
cx,
159177
SUSPICIOUS_ELSE_FORMATTING,
160178
else_span,
161-
"this is an `else if` but the formatting might hide it",
179+
&format!("this is an `else {}` but the formatting might hide it", else_desc),
162180
else_span,
163-
"to remove this lint, remove the `else` or remove the new line between `else` \
164-
and `if`",
181+
&format!(
182+
"to remove this lint, remove the `else` or remove the new line between \
183+
`else` and `{}`",
184+
else_desc,
185+
),
165186
);
166187
}
167188
}
@@ -200,32 +221,47 @@ fn check_array(cx: &EarlyContext<'_>, expr: &ast::Expr) {
200221
}
201222
}
202223

203-
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs.
204-
fn check_consecutive_ifs(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
224+
fn check_missing_else(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
205225
if !differing_macro_contexts(first.span, second.span)
206226
&& !in_macro(first.span)
207227
&& unsugar_if(first).is_some()
208-
&& unsugar_if(second).is_some()
228+
&& (is_block(second) || unsugar_if(second).is_some())
209229
{
210230
// where the else would be
211231
let else_span = first.span.between(second.span);
212232

213233
if let Some(else_snippet) = snippet_opt(cx, else_span) {
214234
if !else_snippet.contains('\n') {
235+
let (looks_like, next_thing) = if unsugar_if(second).is_some() {
236+
("an `else if`", "the second `if`")
237+
} else {
238+
("an `else {..}`", "the next block")
239+
};
240+
215241
span_note_and_lint(
216242
cx,
217243
SUSPICIOUS_ELSE_FORMATTING,
218244
else_span,
219-
"this looks like an `else if` but the `else` is missing",
245+
&format!("this looks like {} but the `else` is missing", looks_like),
220246
else_span,
221-
"to remove this lint, add the missing `else` or add a new line before the second \
222-
`if`",
247+
&format!(
248+
"to remove this lint, add the missing `else` or add a new line before {}",
249+
next_thing,
250+
),
223251
);
224252
}
225253
}
226254
}
227255
}
228256

257+
fn is_block(expr: &ast::Expr) -> bool {
258+
if let ast::ExprKind::Block(..) = expr.node {
259+
true
260+
} else {
261+
false
262+
}
263+
}
264+
229265
/// Match `if` or `if let` expressions and return the `then` and `else` block.
230266
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
231267
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)