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 { std::io::ErrorKind::NotFound }
117
+ /// for _ in 0..10 {
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 { std::io::ErrorKind::NotFound }
139
+ /// for _ in 0..10 {
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,58 @@ 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
- ) ;
379
- }
404
+ fn check_last_stmt_in_expr < F > ( inner_expr : & ast:: Expr , func : & F )
405
+ where
406
+ F : Fn ( Option < & ast:: Label > , Span ) ,
407
+ {
408
+ match & inner_expr. kind {
409
+ ast:: ExprKind :: Continue ( continue_label) => {
410
+ func ( continue_label. as_ref ( ) , inner_expr. span ) ;
411
+ } ,
412
+ ast:: ExprKind :: If ( _, then_block, else_block) => {
413
+ check_last_stmt_in_block ( then_block, func) ;
414
+ if let Some ( else_block) = else_block {
415
+ check_last_stmt_in_expr ( else_block, func) ;
416
+ }
417
+ } ,
418
+ ast:: ExprKind :: Match ( _, arms) => {
419
+ for arm in arms {
420
+ check_last_stmt_in_expr ( & arm. body , func) ;
421
+ }
422
+ } ,
423
+ ast:: ExprKind :: Block ( b, _) => {
424
+ check_last_stmt_in_block ( b, func) ;
425
+ } ,
426
+ _ => { } ,
427
+ }
428
+ }
429
+
430
+ fn check_last_stmt_in_block < F > ( b : & Block , func : & F )
431
+ where
432
+ F : Fn ( Option < & ast:: Label > , Span ) ,
433
+ {
434
+ if let Some ( last_stmt) = b. stmts . last ( ) &&
435
+ let ast:: StmtKind :: Expr ( inner_expr) | ast:: StmtKind :: Semi ( inner_expr) = & last_stmt. kind {
436
+ check_last_stmt_in_expr ( inner_expr, func) ;
380
437
}
438
+ }
439
+
440
+ fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast:: Expr ) {
381
441
with_loop_block ( expr, |loop_block, label| {
442
+ let p = |continue_label : Option < & Label > , span : Span | {
443
+ if compare_labels ( label, continue_label) {
444
+ span_lint_and_help (
445
+ cx,
446
+ NEEDLESS_CONTINUE ,
447
+ span,
448
+ MSG_REDUNDANT_CONTINUE_EXPRESSION ,
449
+ None ,
450
+ DROP_CONTINUE_EXPRESSION_MSG ,
451
+ ) ;
452
+ }
453
+ } ;
454
+ check_last_stmt_in_block ( loop_block, & p) ;
455
+
382
456
for ( i, stmt) in loop_block. stmts . iter ( ) . enumerate ( ) {
383
457
with_if_expr ( stmt, |if_expr, cond, then_block, else_expr| {
384
458
let data = & LintData {
0 commit comments