1
1
use 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 } ;
5
8
use rustc_session:: declare_lint_pass;
6
9
use rustc_span:: Span ;
7
10
@@ -127,9 +130,9 @@ declare_clippy_lint! {
127
130
128
131
declare_lint_pass ! ( NeedlessContinue => [ NEEDLESS_CONTINUE ] ) ;
129
132
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 ) {
133
136
check_and_warn ( cx, expr) ;
134
137
}
135
138
}
@@ -188,19 +191,19 @@ impl EarlyLintPass for NeedlessContinue {
188
191
///
189
192
/// - The expression is a `continue` node.
190
193
/// - 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 {
192
195
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 ( ) ) ,
195
198
_ => false ,
196
199
}
197
200
}
198
201
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 {
200
203
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 ( ) )
204
207
} else {
205
208
false
206
209
}
@@ -223,19 +226,33 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) ->
223
226
224
227
/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
225
228
/// 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 )
227
230
where
228
- F : FnMut ( & Block , Option < & Label > ) ,
231
+ F : FnMut ( & Block < ' tcx > , Option < & Label > ) ,
229
232
{
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 ;
235
238
}
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
237
242
{
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 ( ) ) ;
239
256
}
240
257
}
241
258
@@ -247,17 +264,18 @@ where
247
264
/// - The `if` condition expression,
248
265
/// - The `then` block, and
249
266
/// - 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 )
251
268
where
252
- F : FnMut ( & ast :: Expr , & ast :: Expr , & Block , & ast :: Expr ) ,
269
+ F : FnMut ( & Expr < ' tcx > , & Expr < ' tcx > , & Block < ' tcx > , & Expr < ' tcx > ) ,
253
270
{
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) ;
261
279
}
262
280
}
263
281
@@ -269,20 +287,21 @@ enum LintType {
269
287
}
270
288
271
289
/// Data we pass around for construction of help messages.
272
- struct LintData < ' a > {
290
+ #[ derive( Debug ) ]
291
+ struct LintData < ' tcx > {
273
292
/// The `if` expression encountered in the above loop.
274
- if_expr : & ' a ast :: Expr ,
293
+ if_expr : & ' tcx Expr < ' tcx > ,
275
294
/// The condition expression for the above `if`.
276
- if_cond : & ' a ast :: Expr ,
295
+ if_cond : & ' tcx Expr < ' tcx > ,
277
296
/// The `then` block of the `if` statement.
278
- if_block : & ' a Block ,
297
+ if_block : & ' tcx Block < ' tcx > ,
279
298
/// The `else` block of the `if` statement.
280
299
/// 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 > ,
282
301
/// The 0-based index of the `if` statement in the containing loop block.
283
- stmt_idx : usize ,
302
+ stmt_idx : Option < usize > ,
284
303
/// The statements of the loop block.
285
- loop_block : & ' a Block ,
304
+ loop_block : & ' tcx Block < ' tcx > ,
286
305
}
287
306
288
307
const 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";
299
318
300
319
const DROP_CONTINUE_EXPRESSION_MSG : & str = "consider dropping the `continue` expression" ;
301
320
302
- fn emit_warning ( cx : & EarlyContext < ' _ > , data : & LintData < ' _ > , header : & str , typ : LintType ) {
321
+ fn emit_warning ( cx : & LateContext < ' _ > , data : & LintData < ' _ > , header : & str , typ : LintType ) {
303
322
// snip is the whole *help* message that appears after the warning.
304
323
// message is the warning message.
305
324
// 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
325
344
) ;
326
345
}
327
346
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
+ ) ;
330
356
331
357
let continue_code = snippet_block ( cx, data. if_block . span , ".." , Some ( data. if_expr . span ) ) ;
332
358
@@ -339,7 +365,7 @@ fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintD
339
365
)
340
366
}
341
367
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 {
343
369
let cond_code = snippet ( cx, data. if_cond . span , ".." ) ;
344
370
345
371
// Region B
@@ -352,18 +378,32 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
352
378
let indent = span_of_first_expr_in_block ( data. if_block )
353
379
. and_then ( |span| indent_of ( cx, span) )
354
380
. 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 ;
359
395
let snip = snippet_block ( cx, span, ".." , None ) ;
360
- snip. lines ( )
396
+ let expr_lines = snip
397
+ . lines ( )
361
398
. map ( |line| format ! ( "{}{line}" , " " . repeat( indent) ) )
362
399
. 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
+ } ;
367
407
368
408
let indent_if = indent_of ( cx, data. if_expr . span ) . unwrap_or ( 0 ) ;
369
409
format ! (
@@ -373,46 +413,49 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
373
413
)
374
414
}
375
415
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 )
377
417
where
378
418
F : Fn ( Option < & Label > , Span ) ,
379
419
{
380
420
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 ) ;
383
423
} ,
384
- ast :: ExprKind :: If ( _, then_block, else_block) => {
424
+ ExprKind :: If ( _, then_block, else_block) if let ExprKind :: Block ( then_block , _ ) = then_block . kind => {
385
425
check_last_stmt_in_block ( then_block, func) ;
386
426
if let Some ( else_block) = else_block {
387
427
check_last_stmt_in_expr ( else_block, func) ;
388
428
}
389
429
} ,
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) ;
395
433
}
396
434
} ,
397
- ast :: ExprKind :: Block ( b, _) => {
435
+ ExprKind :: Block ( b, _) => {
398
436
check_last_stmt_in_block ( b, func) ;
399
437
} ,
400
438
_ => { } ,
401
439
}
402
440
}
403
441
404
- fn check_last_stmt_in_block < F > ( b : & Block , func : & F )
442
+ fn check_last_stmt_in_block < F > ( b : & Block < ' _ > , func : & F )
405
443
where
406
444
F : Fn ( Option < & Label > , Span ) ,
407
445
{
446
+ if let Some ( expr) = b. expr {
447
+ check_last_stmt_in_expr ( expr, func) ;
448
+ return ;
449
+ }
450
+
408
451
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
410
453
{
411
454
check_last_stmt_in_expr ( inner_expr, func) ;
412
455
}
413
456
}
414
457
415
- fn check_and_warn ( cx : & EarlyContext < ' _ > , expr : & ast :: Expr ) {
458
+ fn check_and_warn ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
416
459
with_loop_block ( expr, |loop_block, label| {
417
460
let p = |continue_label : Option < & Label > , span : Span | {
418
461
if compare_labels ( label, continue_label) {
@@ -427,16 +470,51 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
427
470
}
428
471
} ;
429
472
430
- let stmts = & loop_block. stmts ;
473
+ let stmts = loop_block. stmts ;
431
474
for ( i, stmt) in stmts. iter ( ) . enumerate ( ) {
432
475
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| {
434
512
let data = & LintData {
435
513
if_expr,
436
514
if_cond : cond,
437
515
if_block : then_block,
438
516
else_expr,
439
- stmt_idx : i ,
517
+ stmt_idx : None ,
440
518
loop_block,
441
519
} ;
442
520
@@ -455,7 +533,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
455
533
}
456
534
} ) ;
457
535
458
- if i == stmts . len ( ) - 1 && !maybe_emitted_in_if {
536
+ if !maybe_emitted_in_if {
459
537
check_last_stmt_in_block ( loop_block, & p) ;
460
538
}
461
539
}
@@ -491,8 +569,12 @@ fn erode_from_back(s: &str) -> String {
491
569
if ret. is_empty ( ) { s. to_string ( ) } else { ret }
492
570
}
493
571
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 ) )
496
578
}
497
579
498
580
#[ cfg( test) ]
0 commit comments