1
1
use clippy_utils:: diagnostics:: span_lint_and_help;
2
2
use clippy_utils:: source:: { indent_of, snippet, snippet_block} ;
3
- use rustc_ast:: ast;
3
+ use rustc_ast:: { Block , Label , ast} ;
4
4
use rustc_lint:: { EarlyContext , EarlyLintPass , LintContext } ;
5
5
use rustc_session:: declare_lint_pass;
6
6
use rustc_span:: Span ;
@@ -11,6 +11,7 @@ declare_clippy_lint! {
11
11
/// that contain a `continue` statement in either their main blocks or their
12
12
/// `else`-blocks, when omitting the `else`-block possibly with some
13
13
/// rearrangement of code can make the code easier to understand.
14
+ /// The lint also checks if the last statement in the loop is a `continue`
14
15
///
15
16
/// ### Why is this bad?
16
17
/// Having explicit `else` blocks for `if` statements
@@ -75,6 +76,45 @@ declare_clippy_lint! {
75
76
/// # break;
76
77
/// }
77
78
/// ```
79
+ ///
80
+ /// ```rust
81
+ /// fn foo() -> std::io::ErrorKind { std::io::ErrorKind::NotFound }
82
+ /// for _ in 0..10 {
83
+ /// match foo() {
84
+ /// std::io::ErrorKind::NotFound => {
85
+ /// eprintln!("not found");
86
+ /// continue
87
+ /// }
88
+ /// std::io::ErrorKind::TimedOut => {
89
+ /// eprintln!("timeout");
90
+ /// continue
91
+ /// }
92
+ /// _ => {
93
+ /// eprintln!("other error");
94
+ /// continue
95
+ /// }
96
+ /// }
97
+ /// }
98
+ /// ```
99
+ /// Could be rewritten as
100
+ ///
101
+ ///
102
+ /// ```rust
103
+ /// fn foo() -> std::io::ErrorKind { std::io::ErrorKind::NotFound }
104
+ /// for _ in 0..10 {
105
+ /// match foo() {
106
+ /// std::io::ErrorKind::NotFound => {
107
+ /// eprintln!("not found");
108
+ /// }
109
+ /// std::io::ErrorKind::TimedOut => {
110
+ /// eprintln!("timeout");
111
+ /// }
112
+ /// _ => {
113
+ /// eprintln!("other error");
114
+ /// }
115
+ /// }
116
+ /// }
117
+ /// ```
78
118
#[ clippy:: version = "pre 1.29.0" ]
79
119
pub NEEDLESS_CONTINUE ,
80
120
pedantic,
@@ -144,15 +184,15 @@ impl EarlyLintPass for NeedlessContinue {
144
184
///
145
185
/// - The expression is a `continue` node.
146
186
/// - The expression node is a block with the first statement being a `continue`.
147
- fn needless_continue_in_else ( else_expr : & ast:: Expr , label : Option < & ast :: Label > ) -> bool {
187
+ fn needless_continue_in_else ( else_expr : & ast:: Expr , label : Option < & Label > ) -> bool {
148
188
match else_expr. kind {
149
189
ast:: ExprKind :: Block ( ref else_block, _) => is_first_block_stmt_continue ( else_block, label) ,
150
190
ast:: ExprKind :: Continue ( l) => compare_labels ( label, l. as_ref ( ) ) ,
151
191
_ => false ,
152
192
}
153
193
}
154
194
155
- fn is_first_block_stmt_continue ( block : & ast :: Block , label : Option < & ast :: Label > ) -> bool {
195
+ fn is_first_block_stmt_continue ( block : & Block , label : Option < & Label > ) -> bool {
156
196
block. stmts . first ( ) . is_some_and ( |stmt| match stmt. kind {
157
197
ast:: StmtKind :: Semi ( ref e) | ast:: StmtKind :: Expr ( ref e) => {
158
198
if let ast:: ExprKind :: Continue ( ref l) = e. kind {
@@ -166,7 +206,7 @@ fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>)
166
206
}
167
207
168
208
/// If the `continue` has a label, check it matches the label of the loop.
169
- fn compare_labels ( loop_label : Option < & ast :: Label > , continue_label : Option < & ast :: Label > ) -> bool {
209
+ fn compare_labels ( loop_label : Option < & Label > , continue_label : Option < & Label > ) -> bool {
170
210
match ( loop_label, continue_label) {
171
211
// `loop { continue; }` or `'a loop { continue; }`
172
212
( _, None ) => true ,
@@ -181,7 +221,7 @@ fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::
181
221
/// the AST object representing the loop block of `expr`.
182
222
fn with_loop_block < F > ( expr : & ast:: Expr , mut func : F )
183
223
where
184
- F : FnMut ( & ast :: Block , Option < & ast :: Label > ) ,
224
+ F : FnMut ( & Block , Option < & Label > ) ,
185
225
{
186
226
if let ast:: ExprKind :: While ( _, loop_block, label)
187
227
| ast:: ExprKind :: ForLoop {
@@ -205,7 +245,7 @@ where
205
245
/// - The `else` expression.
206
246
fn with_if_expr < F > ( stmt : & ast:: Stmt , mut func : F )
207
247
where
208
- F : FnMut ( & ast:: Expr , & ast:: Expr , & ast :: Block , & ast:: Expr ) ,
248
+ F : FnMut ( & ast:: Expr , & ast:: Expr , & Block , & ast:: Expr ) ,
209
249
{
210
250
match stmt. kind {
211
251
ast:: StmtKind :: Semi ( ref e) | ast:: StmtKind :: Expr ( ref e) => {
@@ -231,14 +271,14 @@ struct LintData<'a> {
231
271
/// The condition expression for the above `if`.
232
272
if_cond : & ' a ast:: Expr ,
233
273
/// The `then` block of the `if` statement.
234
- if_block : & ' a ast :: Block ,
274
+ if_block : & ' a Block ,
235
275
/// The `else` block of the `if` statement.
236
276
/// Note that we only work with `if` exprs that have an `else` branch.
237
277
else_expr : & ' a ast:: Expr ,
238
278
/// The 0-based index of the `if` statement in the containing loop block.
239
279
stmt_idx : usize ,
240
280
/// The statements of the loop block.
241
- loop_block : & ' a ast :: Block ,
281
+ loop_block : & ' a Block ,
242
282
}
243
283
244
284
const MSG_REDUNDANT_CONTINUE_EXPRESSION : & str = "this `continue` expression is redundant" ;
@@ -329,23 +369,61 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
329
369
)
330
370
}
331
371
332
- fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast:: Expr ) {
333
- if let ast:: ExprKind :: Loop ( loop_block, loop_label, ..) = & expr. kind
334
- && let Some ( last_stmt) = loop_block. stmts . last ( )
372
+ fn check_last_stmt_in_expr < F > ( inner_expr : & ast:: Expr , func : & F )
373
+ where
374
+ F : Fn ( Option < & Label > , Span ) ,
375
+ {
376
+ match & inner_expr. kind {
377
+ ast:: ExprKind :: Continue ( continue_label) => {
378
+ func ( continue_label. as_ref ( ) , inner_expr. span ) ;
379
+ } ,
380
+ ast:: ExprKind :: If ( _, then_block, else_block) => {
381
+ check_last_stmt_in_block ( then_block, func) ;
382
+ if let Some ( else_block) = else_block {
383
+ check_last_stmt_in_expr ( else_block, func) ;
384
+ }
385
+ } ,
386
+ ast:: ExprKind :: Match ( _, arms, _) => {
387
+ for arm in arms {
388
+ if let Some ( expr) = & arm. body {
389
+ check_last_stmt_in_expr ( expr, func) ;
390
+ }
391
+ }
392
+ } ,
393
+ ast:: ExprKind :: Block ( b, _) => {
394
+ check_last_stmt_in_block ( b, func) ;
395
+ } ,
396
+ _ => { } ,
397
+ }
398
+ }
399
+
400
+ fn check_last_stmt_in_block < F > ( b : & Block , func : & F )
401
+ where
402
+ F : Fn ( Option < & Label > , Span ) ,
403
+ {
404
+ if let Some ( last_stmt) = b. stmts . last ( )
335
405
&& let ast:: StmtKind :: Expr ( inner_expr) | ast:: StmtKind :: Semi ( inner_expr) = & last_stmt. kind
336
- && let ast:: ExprKind :: Continue ( continue_label) = inner_expr. kind
337
- && compare_labels ( loop_label. as_ref ( ) , continue_label. as_ref ( ) )
338
406
{
339
- span_lint_and_help (
340
- cx,
341
- NEEDLESS_CONTINUE ,
342
- last_stmt. span ,
343
- MSG_REDUNDANT_CONTINUE_EXPRESSION ,
344
- None ,
345
- DROP_CONTINUE_EXPRESSION_MSG ,
346
- ) ;
407
+ check_last_stmt_in_expr ( inner_expr, func) ;
347
408
}
409
+ }
410
+
411
+ fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast:: Expr ) {
348
412
with_loop_block ( expr, |loop_block, label| {
413
+ let p = |continue_label : Option < & Label > , span : Span | {
414
+ if compare_labels ( label, continue_label) {
415
+ span_lint_and_help (
416
+ cx,
417
+ NEEDLESS_CONTINUE ,
418
+ span,
419
+ MSG_REDUNDANT_CONTINUE_EXPRESSION ,
420
+ None ,
421
+ DROP_CONTINUE_EXPRESSION_MSG ,
422
+ ) ;
423
+ }
424
+ } ;
425
+ check_last_stmt_in_block ( loop_block, & p) ;
426
+
349
427
for ( i, stmt) in loop_block. stmts . iter ( ) . enumerate ( ) {
350
428
with_if_expr ( stmt, |if_expr, cond, then_block, else_expr| {
351
429
let data = & LintData {
@@ -400,7 +478,7 @@ fn erode_from_back(s: &str) -> String {
400
478
if ret. is_empty ( ) { s. to_string ( ) } else { ret }
401
479
}
402
480
403
- fn span_of_first_expr_in_block ( block : & ast :: Block ) -> Option < Span > {
481
+ fn span_of_first_expr_in_block ( block : & Block ) -> Option < Span > {
404
482
block. stmts . first ( ) . map ( |stmt| stmt. span )
405
483
}
406
484
0 commit comments