35
35
//! This lint is **warn** by default.
36
36
use clippy_utils:: diagnostics:: span_lint_and_help;
37
37
use clippy_utils:: source:: { indent_of, snippet, snippet_block} ;
38
- use rustc_ast:: ast;
38
+ use rustc_ast:: { ast, Block , Label } ;
39
39
use rustc_lint:: { EarlyContext , EarlyLintPass , LintContext } ;
40
40
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
41
41
use rustc_span:: Span ;
@@ -46,6 +46,7 @@ declare_clippy_lint! {
46
46
/// that contain a `continue` statement in either their main blocks or their
47
47
/// `else`-blocks, when omitting the `else`-block possibly with some
48
48
/// rearrangement of code can make the code easier to understand.
49
+ /// The lint also checks if the last statement in the loop is a `continue`
49
50
///
50
51
/// ### Why is this bad?
51
52
/// Having explicit `else` blocks for `if` statements
@@ -110,6 +111,45 @@ declare_clippy_lint! {
110
111
/// # break;
111
112
/// }
112
113
/// ```
114
+ ///
115
+ /// ```rust
116
+ /// fn foo() -> std::io::ErrorKind { unimplemented!() }
117
+ /// loop {
118
+ /// match foo() {
119
+ /// std::io::ErrorKind::NotFound => {
120
+ /// eprintln!("not found");
121
+ /// continue
122
+ /// }
123
+ /// std::io::ErrorKind::TimedOut => {
124
+ /// eprintln!("timeout");
125
+ /// continue
126
+ /// }
127
+ /// _ => {
128
+ /// eprintln!("other error");
129
+ /// continue
130
+ /// }
131
+ /// }
132
+ /// }
133
+ /// ```
134
+ /// Could be rewritten as
135
+ ///
136
+ ///
137
+ /// ```rust
138
+ /// fn foo() -> std::io::ErrorKind { unimplemented!() }
139
+ /// loop {
140
+ /// match foo() {
141
+ /// std::io::ErrorKind::NotFound => {
142
+ /// eprintln!("not found");
143
+ /// }
144
+ /// std::io::ErrorKind::TimedOut => {
145
+ /// eprintln!("timeout");
146
+ /// }
147
+ /// _ => {
148
+ /// eprintln!("other error");
149
+ /// }
150
+ /// }
151
+ /// }
152
+ /// ```
113
153
#[ clippy:: version = "pre 1.29.0" ]
114
154
pub NEEDLESS_CONTINUE ,
115
155
pedantic,
@@ -361,24 +401,79 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
361
401
)
362
402
}
363
403
364
- fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast:: Expr ) {
365
- if_chain ! {
366
- if let ast:: ExprKind :: Loop ( loop_block, ..) = & expr. kind;
367
- if let Some ( last_stmt) = loop_block. stmts. last( ) ;
368
- if let ast:: StmtKind :: Expr ( inner_expr) | ast:: StmtKind :: Semi ( inner_expr) = & last_stmt. kind;
369
- if let ast:: ExprKind :: Continue ( _) = inner_expr. kind;
370
- then {
371
- span_lint_and_help(
372
- cx,
373
- NEEDLESS_CONTINUE ,
374
- last_stmt. span,
375
- MSG_REDUNDANT_CONTINUE_EXPRESSION ,
376
- None ,
377
- DROP_CONTINUE_EXPRESSION_MSG ,
378
- ) ;
404
+ fn check_last_stmt_in_if < F > ( b : & ast:: Expr , func : & F )
405
+ where
406
+ F : Fn ( Option < & ast:: Label > , Span ) ,
407
+ {
408
+ match & b. kind {
409
+ ast:: ExprKind :: If ( _, then_block, else_block) => {
410
+ check_last_stmt ( then_block, func) ;
411
+ if let Some ( else_block) = else_block {
412
+ check_last_stmt_in_if ( else_block, func) ;
413
+ }
414
+ } ,
415
+ ast:: ExprKind :: Continue ( ..) => {
416
+ unreachable ! ( )
417
+ } ,
418
+ ast:: ExprKind :: Block ( b, _) => {
419
+ check_last_stmt ( b, func) ;
420
+ } ,
421
+ _ => { } ,
422
+ }
423
+ }
424
+
425
+ fn check_last_stmt < F > ( b : & Block , func : & F )
426
+ where
427
+ F : Fn ( Option < & ast:: Label > , Span ) ,
428
+ {
429
+ if let Some ( last_stmt) = b. stmts . last ( ) &&
430
+ let ast:: StmtKind :: Expr ( inner_expr) | ast:: StmtKind :: Semi ( inner_expr) = & last_stmt. kind {
431
+ match & inner_expr. kind {
432
+ ast:: ExprKind :: Continue ( continue_label) => {
433
+ func ( continue_label. as_ref ( ) , last_stmt. span ) ;
434
+ } ,
435
+ ast:: ExprKind :: If ( _, _, _) => {
436
+ check_last_stmt_in_if ( inner_expr, func) ;
437
+ }
438
+ ast:: ExprKind :: Match ( _, arms) => {
439
+ for arm in arms {
440
+ match & arm. body . kind {
441
+ ast:: ExprKind :: Continue ( continue_label) => {
442
+ func ( continue_label. as_ref ( ) , arm. body . span ) ;
443
+ }
444
+ ast:: ExprKind :: Block ( b, _) => {
445
+ check_last_stmt ( b, func) ;
446
+
447
+ }
448
+ _ => { }
449
+ }
450
+
451
+ }
452
+ }
453
+ ast:: ExprKind :: Block ( b, _) => {
454
+ check_last_stmt ( b, func) ;
455
+ }
456
+ _ => { } ,
379
457
}
380
458
}
459
+ }
460
+
461
+ fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast:: Expr ) {
381
462
with_loop_block ( expr, |loop_block, label| {
463
+ let p = |continue_label : Option < & Label > , span : Span | {
464
+ if compare_labels ( label, continue_label) {
465
+ span_lint_and_help (
466
+ cx,
467
+ NEEDLESS_CONTINUE ,
468
+ span,
469
+ MSG_REDUNDANT_CONTINUE_EXPRESSION ,
470
+ None ,
471
+ DROP_CONTINUE_EXPRESSION_MSG ,
472
+ ) ;
473
+ }
474
+ } ;
475
+ check_last_stmt ( loop_block, & p) ;
476
+
382
477
for ( i, stmt) in loop_block. stmts . iter ( ) . enumerate ( ) {
383
478
with_if_expr ( stmt, |if_expr, cond, then_block, else_expr| {
384
479
let data = & LintData {
0 commit comments