@@ -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/// ```
5464declare_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.
230268fn unsugar_if ( expr : & ast:: Expr ) -> Option < ( & P < ast:: Block > , & Option < P < ast:: Expr > > ) > {
231269 match expr. node {
0 commit comments