Skip to content

Commit 89e81b8

Browse files
committed
Teach suspicious_else_formatting about if .. {..} {..}
1 parent b1d0343 commit 89e81b8

File tree

3 files changed

+159
-64
lines changed

3 files changed

+159
-64
lines changed

clippy_lints/src/formatting.rs

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

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

6171
/// **What it does:** Checks for possible missing comma in an array. It lints if
@@ -98,7 +108,7 @@ impl EarlyLintPass for Formatting {
98108
match (&w[0].node, &w[1].node) {
99109
(&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second)) |
100110
(&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
101-
check_consecutive_ifs(cx, first, second);
111+
check_missing_else(cx, first, second);
102112
},
103113
_ => (),
104114
}
@@ -107,7 +117,7 @@ impl EarlyLintPass for Formatting {
107117

108118
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
109119
check_assign(cx, expr);
110-
check_else_if(cx, expr);
120+
check_else(cx, expr);
111121
check_array(cx, expr);
112122
}
113123
}
@@ -141,10 +151,12 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &ast::Expr) {
141151
}
142152
}
143153

144-
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else if`.
145-
fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
154+
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
155+
fn check_else(cx: &EarlyContext<'_>, expr: &ast::Expr) {
146156
if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
147-
if unsugar_if(else_).is_some() && !differing_macro_contexts(then.span, else_.span) && !in_macro(then.span) {
157+
if (is_block(else_) || unsugar_if(else_).is_some())
158+
&& !differing_macro_contexts(then.span, else_.span) && !in_macro(then.span)
159+
{
148160
// this will be a span from the closing ‘}’ of the “then” block (excluding) to
149161
// the
150162
// “if” of the “else if” block (excluding)
@@ -158,14 +170,23 @@ fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
158170
.expect("there must be a `else` here");
159171

160172
if else_snippet[else_pos..].contains('\n') {
173+
let else_desc = if unsugar_if(else_).is_some() {
174+
"if"
175+
} else {
176+
"{..}"
177+
};
178+
161179
span_note_and_lint(
162180
cx,
163181
SUSPICIOUS_ELSE_FORMATTING,
164182
else_span,
165-
"this is an `else if` but the formatting might hide it",
183+
&format!("this is an `else {}` but the formatting might hide it", else_desc),
166184
else_span,
167-
"to remove this lint, remove the `else` or remove the new line between `else` \
168-
and `if`",
185+
&format!(
186+
"to remove this lint, remove the `else` or remove the new line between \
187+
`else` and `{}`",
188+
else_desc,
189+
),
169190
);
170191
}
171192
}
@@ -199,30 +220,46 @@ fn check_array(cx: &EarlyContext<'_>, expr: &ast::Expr) {
199220
}
200221
}
201222

202-
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs.
203-
fn check_consecutive_ifs(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
223+
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for missing `else`.
224+
fn check_missing_else(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
204225
if !differing_macro_contexts(first.span, second.span) && !in_macro(first.span) && unsugar_if(first).is_some()
205-
&& unsugar_if(second).is_some()
226+
&& (is_block(second) || unsugar_if(second).is_some())
206227
{
207228
// where the else would be
208229
let else_span = first.span.between(second.span);
209230

210231
if let Some(else_snippet) = snippet_opt(cx, else_span) {
211232
if !else_snippet.contains('\n') {
233+
let (looks_like, next_thing) = if unsugar_if(second).is_some() {
234+
("an `else if`", "the second `if`")
235+
} else {
236+
("an `else {..}`", "the next block")
237+
};
238+
212239
span_note_and_lint(
213240
cx,
214241
SUSPICIOUS_ELSE_FORMATTING,
215242
else_span,
216-
"this looks like an `else if` but the `else` is missing",
243+
&format!("this looks like {} but the `else` is missing", looks_like),
217244
else_span,
218-
"to remove this lint, add the missing `else` or add a new line before the second \
219-
`if`",
245+
&format!(
246+
"to remove this lint, add the missing `else` or add a new line before {}",
247+
next_thing,
248+
),
220249
);
221250
}
222251
}
223252
}
224253
}
225254

255+
fn is_block(expr: &ast::Expr) -> bool {
256+
if let ast::ExprKind::Block(..) = expr.node {
257+
true
258+
} else {
259+
false
260+
}
261+
}
262+
226263
/// Match `if` or `if let` expressions and return the `then` and `else` block.
227264
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
228265
match expr.node {

tests/ui/formatting.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
fn foo() -> bool { true }
2121

2222
fn main() {
23-
// weird `else if` formatting:
23+
// weird `else` formatting:
24+
if foo() {
25+
} {
26+
}
27+
2428
if foo() {
2529
} if foo() {
2630
}
@@ -45,6 +49,17 @@ fn main() {
4549
let _ = 0;
4650
};
4751

52+
if foo() {
53+
} else
54+
{
55+
}
56+
57+
if foo() {
58+
}
59+
else
60+
{
61+
}
62+
4863
if foo() {
4964
} else
5065
if foo() { // the span of the above error should continue here
@@ -57,6 +72,20 @@ fn main() {
5772
}
5873

5974
// those are ok:
75+
if foo() {
76+
}
77+
{
78+
}
79+
80+
if foo() {
81+
} else {
82+
}
83+
84+
if foo() {
85+
}
86+
else {
87+
}
88+
6089
if foo() {
6190
}
6291
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:25:6
33
|
4-
25 | } if foo() {
4+
25 | } {
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:29:6
12+
|
13+
29 | } 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:32:10
19+
--> $DIR/formatting.rs:36:10
1220
|
13-
32 | } if foo() {
21+
36 | } 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:40:10
27+
--> $DIR/formatting.rs:44:10
2028
|
21-
40 | } if foo() {
29+
44 | } 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:53:6
36+
|
37+
53 | } else
38+
| ______^
39+
54 | | {
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:58:6
46+
|
47+
58 | }
48+
| ______^
49+
59 | | else
50+
60 | | {
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:49:6
56+
--> $DIR/formatting.rs:64:6
2857
|
29-
49 | } else
58+
64 | } else
3059
| ______^
31-
50 | | if foo() { // the span of the above error should continue here
60+
65 | | 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:54:6
66+
--> $DIR/formatting.rs:69:6
3867
|
39-
54 | }
68+
69 | }
4069
| ______^
41-
55 | | else
42-
56 | | if foo() { // the span of the above error should continue here
70+
70 | | else
71+
71 | | 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:81:6
49-
|
50-
81 | 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:110:6
78+
|
79+
110 | 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:82:6
58-
|
59-
82 | a =* &191;
60-
| ^^^^
61-
|
62-
= note: to remove this lint, use either `*=` or `= *`
86+
--> $DIR/formatting.rs:111:6
87+
|
88+
111 | 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:85:6
66-
|
67-
85 | b =! false;
68-
| ^^^^
69-
|
70-
= note: to remove this lint, use either `!=` or `= !`
94+
--> $DIR/formatting.rs:114:6
95+
|
96+
114 | 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:94:19
74-
|
75-
94 | -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:123:19
103+
|
104+
123 | -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:98:19
83-
|
84-
98 | -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:127:19
112+
|
113+
127 | -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)