3535//! This lint is **warn** by default.
3636use clippy_utils:: diagnostics:: span_lint_and_help;
3737use clippy_utils:: source:: { indent_of, snippet, snippet_block} ;
38- use rustc_ast:: ast;
38+ use rustc_ast:: { ast, Block , Label } ;
3939use rustc_lint:: { EarlyContext , EarlyLintPass , LintContext } ;
4040use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
4141use rustc_span:: Span ;
@@ -46,6 +46,7 @@ declare_clippy_lint! {
4646 /// that contain a `continue` statement in either their main blocks or their
4747 /// `else`-blocks, when omitting the `else`-block possibly with some
4848 /// 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`
4950 ///
5051 /// ### Why is this bad?
5152 /// Having explicit `else` blocks for `if` statements
@@ -110,6 +111,45 @@ declare_clippy_lint! {
110111 /// # break;
111112 /// }
112113 /// ```
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+ /// ```
113153 #[ clippy:: version = "pre 1.29.0" ]
114154 pub NEEDLESS_CONTINUE ,
115155 pedantic,
@@ -361,24 +401,79 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
361401 )
362402}
363403
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+ _ => { } ,
379457 }
380458 }
459+ }
460+
461+ fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast:: Expr ) {
381462 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+
382477 for ( i, stmt) in loop_block. stmts . iter ( ) . enumerate ( ) {
383478 with_if_expr ( stmt, |if_expr, cond, then_block, else_expr| {
384479 let data = & LintData {
0 commit comments