11use clippy_utils:: diagnostics:: span_lint_and_help;
2- use clippy_utils:: source:: { indent_of, snippet, snippet_block} ;
3- use rustc_ast:: { Block , Label , ast} ;
4- use rustc_lint:: { EarlyContext , EarlyLintPass , LintContext } ;
2+ use clippy_utils:: source:: { indent_of, snippet, snippet_block, snippet_with_context} ;
3+ use clippy_utils:: { higher, is_from_proc_macro} ;
4+ use rustc_ast:: Label ;
5+ use rustc_errors:: Applicability ;
6+ use rustc_hir:: { Block , Expr , ExprKind , LoopSource , StmtKind } ;
7+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
58use rustc_session:: declare_lint_pass;
69use rustc_span:: Span ;
710
@@ -127,9 +130,9 @@ declare_clippy_lint! {
127130
128131declare_lint_pass ! ( NeedlessContinue => [ NEEDLESS_CONTINUE ] ) ;
129132
130- impl EarlyLintPass for NeedlessContinue {
131- fn check_expr ( & mut self , cx : & EarlyContext < ' _ > , expr : & ast :: Expr ) {
132- if !expr. span . from_expansion ( ) {
133+ impl < ' tcx > LateLintPass < ' tcx > for NeedlessContinue {
134+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
135+ if !expr. span . in_external_macro ( cx . sess ( ) . source_map ( ) ) && ! is_from_proc_macro ( cx , expr ) {
133136 check_and_warn ( cx, expr) ;
134137 }
135138 }
@@ -188,19 +191,19 @@ impl EarlyLintPass for NeedlessContinue {
188191///
189192/// - The expression is a `continue` node.
190193/// - The expression node is a block with the first statement being a `continue`.
191- fn needless_continue_in_else ( else_expr : & ast :: Expr , label : Option < & Label > ) -> bool {
194+ fn needless_continue_in_else ( else_expr : & Expr < ' _ > , label : Option < & Label > ) -> bool {
192195 match else_expr. kind {
193- ast :: ExprKind :: Block ( ref else_block, _) => is_first_block_stmt_continue ( else_block, label) ,
194- ast :: ExprKind :: Continue ( l) => compare_labels ( label, l. as_ref ( ) ) ,
196+ ExprKind :: Block ( ref else_block, _) => is_first_block_stmt_continue ( else_block, label) ,
197+ ExprKind :: Continue ( l) => compare_labels ( label, l. label . as_ref ( ) ) ,
195198 _ => false ,
196199 }
197200}
198201
199- fn is_first_block_stmt_continue ( block : & Block , label : Option < & Label > ) -> bool {
202+ fn is_first_block_stmt_continue ( block : & Block < ' _ > , label : Option < & Label > ) -> bool {
200203 block. stmts . first ( ) . is_some_and ( |stmt| match stmt. kind {
201- ast :: StmtKind :: Semi ( ref e) | ast :: StmtKind :: Expr ( ref e) => {
202- if let ast :: ExprKind :: Continue ( ref l) = e. kind {
203- compare_labels ( label, l. as_ref ( ) )
204+ StmtKind :: Semi ( ref e) | StmtKind :: Expr ( ref e) => {
205+ if let ExprKind :: Continue ( ref l) = e. kind {
206+ compare_labels ( label, l. label . as_ref ( ) )
204207 } else {
205208 false
206209 }
@@ -223,19 +226,33 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) ->
223226
224227/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
225228/// the AST object representing the loop block of `expr`.
226- fn with_loop_block < F > ( expr : & ast :: Expr , mut func : F )
229+ fn with_loop_block < ' tcx , F > ( expr : & Expr < ' tcx > , mut func : F )
227230where
228- F : FnMut ( & Block , Option < & Label > ) ,
231+ F : FnMut ( & Block < ' tcx > , Option < & Label > ) ,
229232{
230- if let ast :: ExprKind :: While ( _ , loop_block , label)
231- | ast :: ExprKind :: ForLoop {
232- body : loop_block ,
233- label ,
234- ..
233+ if let Some ( higher :: ForLoop { body , label, .. } ) = higher :: ForLoop :: hir ( expr )
234+ && let ExprKind :: Block ( block , _ ) = & body . kind
235+ {
236+ func ( block , label . as_ref ( ) ) ;
237+ return ;
235238 }
236- | ast:: ExprKind :: Loop ( loop_block, label, ..) = & expr. kind
239+
240+ if let Some ( higher:: While { body, label, .. } ) = higher:: While :: hir ( expr)
241+ && let ExprKind :: Block ( block, _) = & body. kind
237242 {
238- func ( loop_block, label. as_ref ( ) ) ;
243+ func ( block, label. as_ref ( ) ) ;
244+ return ;
245+ }
246+
247+ if let Some ( higher:: WhileLet { if_then, label, .. } ) = higher:: WhileLet :: hir ( expr)
248+ && let ExprKind :: Block ( block, _) = & if_then. kind
249+ {
250+ func ( block, label. as_ref ( ) ) ;
251+ return ;
252+ }
253+
254+ if let ExprKind :: Loop ( block, label, LoopSource :: Loop , ..) = expr. kind {
255+ func ( block, label. as_ref ( ) ) ;
239256 }
240257}
241258
@@ -247,17 +264,18 @@ where
247264/// - The `if` condition expression,
248265/// - The `then` block, and
249266/// - The `else` expression.
250- fn with_if_expr < F > ( stmt : & ast :: Stmt , mut func : F )
267+ fn with_if_expr < ' tcx , F > ( expr : & Expr < ' tcx > , mut func : F )
251268where
252- F : FnMut ( & ast :: Expr , & ast :: Expr , & Block , & ast :: Expr ) ,
269+ F : FnMut ( & Expr < ' tcx > , & Expr < ' tcx > , & Block < ' tcx > , & Expr < ' tcx > ) ,
253270{
254- match stmt. kind {
255- ast:: StmtKind :: Semi ( ref e) | ast:: StmtKind :: Expr ( ref e) => {
256- if let ast:: ExprKind :: If ( ref cond, ref if_block, Some ( ref else_expr) ) = e. kind {
257- func ( e, cond, if_block, else_expr) ;
258- }
259- } ,
260- _ => { } ,
271+ if let Some ( higher:: If {
272+ cond,
273+ then,
274+ r#else : Some ( r#else) ,
275+ } ) = higher:: If :: hir ( expr)
276+ && let ExprKind :: Block ( then, _) = then. kind
277+ {
278+ func ( expr, cond, then, r#else) ;
261279 }
262280}
263281
@@ -269,20 +287,21 @@ enum LintType {
269287}
270288
271289/// Data we pass around for construction of help messages.
272- struct LintData < ' a > {
290+ #[ derive( Debug ) ]
291+ struct LintData < ' tcx > {
273292 /// The `if` expression encountered in the above loop.
274- if_expr : & ' a ast :: Expr ,
293+ if_expr : & ' tcx Expr < ' tcx > ,
275294 /// The condition expression for the above `if`.
276- if_cond : & ' a ast :: Expr ,
295+ if_cond : & ' tcx Expr < ' tcx > ,
277296 /// The `then` block of the `if` statement.
278- if_block : & ' a Block ,
297+ if_block : & ' tcx Block < ' tcx > ,
279298 /// The `else` block of the `if` statement.
280299 /// Note that we only work with `if` exprs that have an `else` branch.
281- else_expr : & ' a ast :: Expr ,
300+ else_expr : & ' tcx Expr < ' tcx > ,
282301 /// The 0-based index of the `if` statement in the containing loop block.
283- stmt_idx : usize ,
302+ stmt_idx : Option < usize > ,
284303 /// The statements of the loop block.
285- loop_block : & ' a Block ,
304+ loop_block : & ' tcx Block < ' tcx > ,
286305}
287306
288307const MSG_REDUNDANT_CONTINUE_EXPRESSION : & str = "this `continue` expression is redundant" ;
@@ -299,7 +318,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
299318
300319const DROP_CONTINUE_EXPRESSION_MSG : & str = "consider dropping the `continue` expression" ;
301320
302- fn emit_warning ( cx : & EarlyContext < ' _ > , data : & LintData < ' _ > , header : & str , typ : LintType ) {
321+ fn emit_warning ( cx : & LateContext < ' _ > , data : & LintData < ' _ > , header : & str , typ : LintType ) {
303322 // snip is the whole *help* message that appears after the warning.
304323 // message is the warning message.
305324 // expr is the expression which the lint warning message refers to.
@@ -325,8 +344,15 @@ fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: L
325344 ) ;
326345}
327346
328- fn suggestion_snippet_for_continue_inside_if ( cx : & EarlyContext < ' _ > , data : & LintData < ' _ > ) -> String {
329- let cond_code = snippet ( cx, data. if_cond . span , ".." ) ;
347+ fn suggestion_snippet_for_continue_inside_if ( cx : & LateContext < ' _ > , data : & LintData < ' _ > ) -> String {
348+ let mut applicability = Applicability :: MachineApplicable ;
349+ let ( cond_code, _) = snippet_with_context (
350+ cx,
351+ data. if_cond . span ,
352+ data. if_expr . span . ctxt ( ) ,
353+ ".." ,
354+ & mut applicability,
355+ ) ;
330356
331357 let continue_code = snippet_block ( cx, data. if_block . span , ".." , Some ( data. if_expr . span ) ) ;
332358
@@ -339,7 +365,7 @@ fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintD
339365 )
340366}
341367
342- fn suggestion_snippet_for_continue_inside_else ( cx : & EarlyContext < ' _ > , data : & LintData < ' _ > ) -> String {
368+ fn suggestion_snippet_for_continue_inside_else ( cx : & LateContext < ' _ > , data : & LintData < ' _ > ) -> String {
343369 let cond_code = snippet ( cx, data. if_cond . span , ".." ) ;
344370
345371 // Region B
@@ -352,18 +378,32 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
352378 let indent = span_of_first_expr_in_block ( data. if_block )
353379 . and_then ( |span| indent_of ( cx, span) )
354380 . unwrap_or ( 0 ) ;
355- let to_annex = data. loop_block . stmts [ data. stmt_idx + 1 ..]
356- . iter ( )
357- . map ( |stmt| {
358- let span = cx. sess ( ) . source_map ( ) . stmt_span ( stmt. span , data. loop_block . span ) ;
381+ let to_annex = if let Some ( stmt_idx) = data. stmt_idx {
382+ let mut lines = data. loop_block . stmts [ stmt_idx + 1 ..]
383+ . iter ( )
384+ . map ( |stmt| {
385+ let span = cx. sess ( ) . source_map ( ) . stmt_span ( stmt. span , data. loop_block . span ) ;
386+ let snip = snippet_block ( cx, span, ".." , None ) ;
387+ snip. lines ( )
388+ . map ( |line| format ! ( "{}{line}" , " " . repeat( indent) ) )
389+ . collect :: < Vec < _ > > ( )
390+ . join ( "\n " )
391+ } )
392+ . collect :: < Vec < _ > > ( ) ;
393+ if let Some ( expr) = data. loop_block . expr {
394+ let span = expr. span ;
359395 let snip = snippet_block ( cx, span, ".." , None ) ;
360- snip. lines ( )
396+ let expr_lines = snip
397+ . lines ( )
361398 . map ( |line| format ! ( "{}{line}" , " " . repeat( indent) ) )
362399 . collect :: < Vec < _ > > ( )
363- . join ( "\n " )
364- } )
365- . collect :: < Vec < _ > > ( )
366- . join ( "\n " ) ;
400+ . join ( "\n " ) ;
401+ lines. push ( expr_lines) ;
402+ }
403+ lines. join ( "\n " )
404+ } else {
405+ "" . to_string ( )
406+ } ;
367407
368408 let indent_if = indent_of ( cx, data. if_expr . span ) . unwrap_or ( 0 ) ;
369409 format ! (
@@ -373,46 +413,49 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
373413 )
374414}
375415
376- fn check_last_stmt_in_expr < F > ( inner_expr : & ast :: Expr , func : & F )
416+ fn check_last_stmt_in_expr < F > ( inner_expr : & Expr < ' _ > , func : & F )
377417where
378418 F : Fn ( Option < & Label > , Span ) ,
379419{
380420 match & inner_expr. kind {
381- ast :: ExprKind :: Continue ( continue_label) => {
382- func ( continue_label. as_ref ( ) , inner_expr. span ) ;
421+ ExprKind :: Continue ( continue_label) => {
422+ func ( continue_label. label . as_ref ( ) , inner_expr. span ) ;
383423 } ,
384- ast :: ExprKind :: If ( _, then_block, else_block) => {
424+ ExprKind :: If ( _, then_block, else_block) if let ExprKind :: Block ( then_block , _ ) = then_block . kind => {
385425 check_last_stmt_in_block ( then_block, func) ;
386426 if let Some ( else_block) = else_block {
387427 check_last_stmt_in_expr ( else_block, func) ;
388428 }
389429 } ,
390- ast:: ExprKind :: Match ( _, arms, _) => {
391- for arm in arms {
392- if let Some ( expr) = & arm. body {
393- check_last_stmt_in_expr ( expr, func) ;
394- }
430+ ExprKind :: Match ( _, arms, _) => {
431+ for arm in arms. iter ( ) {
432+ check_last_stmt_in_expr ( arm. body , func) ;
395433 }
396434 } ,
397- ast :: ExprKind :: Block ( b, _) => {
435+ ExprKind :: Block ( b, _) => {
398436 check_last_stmt_in_block ( b, func) ;
399437 } ,
400438 _ => { } ,
401439 }
402440}
403441
404- fn check_last_stmt_in_block < F > ( b : & Block , func : & F )
442+ fn check_last_stmt_in_block < F > ( b : & Block < ' _ > , func : & F )
405443where
406444 F : Fn ( Option < & Label > , Span ) ,
407445{
446+ if let Some ( expr) = b. expr {
447+ check_last_stmt_in_expr ( expr, func) ;
448+ return ;
449+ }
450+
408451 if let Some ( last_stmt) = b. stmts . last ( )
409- && let ast :: StmtKind :: Expr ( inner_expr) | ast :: StmtKind :: Semi ( inner_expr) = & last_stmt. kind
452+ && let StmtKind :: Expr ( inner_expr) | StmtKind :: Semi ( inner_expr) = last_stmt. kind
410453 {
411454 check_last_stmt_in_expr ( inner_expr, func) ;
412455 }
413456}
414457
415- fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast :: Expr ) {
458+ fn check_and_warn ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
416459 with_loop_block ( expr, |loop_block, label| {
417460 let p = |continue_label : Option < & Label > , span : Span | {
418461 if compare_labels ( label, continue_label) {
@@ -427,16 +470,51 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
427470 }
428471 } ;
429472
430- let stmts = & loop_block. stmts ;
473+ let stmts = loop_block. stmts ;
431474 for ( i, stmt) in stmts. iter ( ) . enumerate ( ) {
432475 let mut maybe_emitted_in_if = false ;
433- with_if_expr ( stmt, |if_expr, cond, then_block, else_expr| {
476+ if let StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) = stmt. kind {
477+ with_if_expr ( expr, |if_expr, cond, then_block, else_expr| {
478+ let data = & LintData {
479+ if_expr,
480+ if_cond : cond,
481+ if_block : then_block,
482+ else_expr,
483+ stmt_idx : Some ( i) ,
484+ loop_block,
485+ } ;
486+
487+ maybe_emitted_in_if = true ;
488+ if needless_continue_in_else ( else_expr, label) {
489+ emit_warning (
490+ cx,
491+ data,
492+ DROP_ELSE_BLOCK_AND_MERGE_MSG ,
493+ LintType :: ContinueInsideElseBlock ,
494+ ) ;
495+ } else if is_first_block_stmt_continue ( then_block, label) {
496+ emit_warning ( cx, data, DROP_ELSE_BLOCK_MSG , LintType :: ContinueInsideThenBlock ) ;
497+ } else {
498+ maybe_emitted_in_if = false ;
499+ }
500+ } ) ;
501+ }
502+
503+ if i == stmts. len ( ) - 1 && loop_block. expr . is_none ( ) && !maybe_emitted_in_if {
504+ check_last_stmt_in_block ( loop_block, & p) ;
505+ }
506+ }
507+
508+ if let Some ( expr) = loop_block. expr {
509+ let mut maybe_emitted_in_if = false ;
510+
511+ with_if_expr ( expr, |if_expr, cond, then_block, else_expr| {
434512 let data = & LintData {
435513 if_expr,
436514 if_cond : cond,
437515 if_block : then_block,
438516 else_expr,
439- stmt_idx : i ,
517+ stmt_idx : None ,
440518 loop_block,
441519 } ;
442520
@@ -455,7 +533,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
455533 }
456534 } ) ;
457535
458- if i == stmts . len ( ) - 1 && !maybe_emitted_in_if {
536+ if !maybe_emitted_in_if {
459537 check_last_stmt_in_block ( loop_block, & p) ;
460538 }
461539 }
@@ -491,8 +569,12 @@ fn erode_from_back(s: &str) -> String {
491569 if ret. is_empty ( ) { s. to_string ( ) } else { ret }
492570}
493571
494- fn span_of_first_expr_in_block ( block : & Block ) -> Option < Span > {
495- block. stmts . first ( ) . map ( |stmt| stmt. span )
572+ fn span_of_first_expr_in_block ( block : & Block < ' _ > ) -> Option < Span > {
573+ block
574+ . stmts
575+ . first ( )
576+ . map ( |stmt| stmt. span )
577+ . or ( block. expr . map ( |expr| expr. span ) )
496578}
497579
498580#[ cfg( test) ]
0 commit comments