@@ -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,13 @@ 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+ {
146159 // this will be a span from the closing ‘}’ of the “then” block (excluding) to
147160 // the
148161 // “if” of the “else if” block (excluding)
@@ -154,14 +167,19 @@ fn check_else_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
154167 let else_pos = else_snippet. find ( "else" ) . expect ( "there must be a `else` here" ) ;
155168
156169 if else_snippet[ else_pos..] . contains ( '\n' ) {
170+ let else_desc = if unsugar_if ( else_) . is_some ( ) { "if" } else { "{..}" } ;
171+
157172 span_note_and_lint (
158173 cx,
159174 SUSPICIOUS_ELSE_FORMATTING ,
160175 else_span,
161- "this is an `else if ` but the formatting might hide it" ,
176+ & format ! ( "this is an `else {} ` but the formatting might hide it" , else_desc ) ,
162177 else_span,
163- "to remove this lint, remove the `else` or remove the new line between `else` \
164- and `if`",
178+ & format ! (
179+ "to remove this lint, remove the `else` or remove the new line between \
180+ `else` and `{}`",
181+ else_desc,
182+ ) ,
165183 ) ;
166184 }
167185 }
@@ -200,32 +218,47 @@ fn check_array(cx: &EarlyContext<'_>, expr: &ast::Expr) {
200218 }
201219}
202220
203- /// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs.
204- fn check_consecutive_ifs ( cx : & EarlyContext < ' _ > , first : & ast:: Expr , second : & ast:: Expr ) {
221+ fn check_missing_else ( cx : & EarlyContext < ' _ > , first : & ast:: Expr , second : & ast:: Expr ) {
205222 if !differing_macro_contexts ( first. span , second. span )
206223 && !in_macro ( first. span )
207224 && unsugar_if ( first) . is_some ( )
208- && unsugar_if ( second) . is_some ( )
225+ && ( is_block ( second ) || unsugar_if ( second) . is_some ( ) )
209226 {
210227 // where the else would be
211228 let else_span = first. span . between ( second. span ) ;
212229
213230 if let Some ( else_snippet) = snippet_opt ( cx, else_span) {
214231 if !else_snippet. contains ( '\n' ) {
232+ let ( looks_like, next_thing) = if unsugar_if ( second) . is_some ( ) {
233+ ( "an `else if`" , "the second `if`" )
234+ } else {
235+ ( "an `else {..}`" , "the next block" )
236+ } ;
237+
215238 span_note_and_lint (
216239 cx,
217240 SUSPICIOUS_ELSE_FORMATTING ,
218241 else_span,
219- "this looks like an `else if` but the `else` is missing" ,
242+ & format ! ( "this looks like {} but the `else` is missing" , looks_like ) ,
220243 else_span,
221- "to remove this lint, add the missing `else` or add a new line before the second \
222- `if`",
244+ & format ! (
245+ "to remove this lint, add the missing `else` or add a new line before {}" ,
246+ next_thing,
247+ ) ,
223248 ) ;
224249 }
225250 }
226251 }
227252}
228253
254+ fn is_block ( expr : & ast:: Expr ) -> bool {
255+ if let ast:: ExprKind :: Block ( ..) = expr. node {
256+ true
257+ } else {
258+ false
259+ }
260+ }
261+
229262/// Match `if` or `if let` expressions and return the `then` and `else` block.
230263fn unsugar_if ( expr : & ast:: Expr ) -> Option < ( & P < ast:: Block > , & Option < P < ast:: Expr > > ) > {
231264 match expr. node {
0 commit comments