@@ -286,24 +286,22 @@ declare_lint! {
286
286
"looping on a map using `iter` when `keys` or `values` would do"
287
287
}
288
288
289
- /// **What it does:** Checks for loops that contain an unconditional `break`
290
- /// or `return` .
289
+ /// **What it does:** Checks for loops that will always `break`, `return` or
290
+ /// `continue` an outer loop .
291
291
///
292
292
/// **Why is this bad?** This loop never loops, all it does is obfuscating the
293
293
/// code.
294
294
///
295
- /// **Known problems:** Ignores `continue` statements in the loop that create
296
- /// nontrivial control flow. Therefore set to `Allow` by default.
297
- /// See https://github.com/Manishearth/rust-clippy/issues/1586
295
+ /// **Known problems:** None
298
296
///
299
297
/// **Example:**
300
298
/// ```rust
301
299
/// loop { ..; break; }
302
300
/// ```
303
301
declare_lint ! {
304
302
pub NEVER_LOOP ,
305
- Allow ,
306
- "any loop with an unconditional `break` or `return` statement "
303
+ Warn ,
304
+ "any loop that will always `break` or `return`"
307
305
}
308
306
309
307
#[ derive( Copy , Clone ) ]
@@ -333,6 +331,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
333
331
if let Some ( ( pat, arg, body) ) = higher:: for_loop ( expr) {
334
332
check_for_loop ( cx, pat, arg, body, expr) ;
335
333
}
334
+
335
+ // check for never_loop
336
+ match expr. node {
337
+ ExprWhile ( _, ref block, _) |
338
+ ExprLoop ( ref block, _, _) => {
339
+ if never_loop ( block, & expr. id ) {
340
+ span_lint ( cx, NEVER_LOOP , expr. span , "this loop never actually loops" ) ;
341
+ }
342
+ } ,
343
+ _ => ( ) ,
344
+ }
345
+
336
346
// check for `loop { if let {} else break }` that could be `while let`
337
347
// (also matches an explicit "match" instead of "if let")
338
348
// (even if the "match" or "if let" is used for declaration)
@@ -345,9 +355,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
345
355
"empty `loop {}` detected. You may want to either use `panic!()` or add \
346
356
`std::thread::sleep(..);` to the loop body.") ;
347
357
}
348
- if never_loop_block ( block) {
349
- span_lint ( cx, NEVER_LOOP , expr. span , "this loop never actually loops" ) ;
350
- }
351
358
352
359
// extract the expression from the first statement (if any) in a block
353
360
let inner_stmt_expr = extract_expr_from_first_stmt ( block) ;
@@ -425,47 +432,103 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
425
432
}
426
433
}
427
434
428
- fn never_loop_block ( block : & Block ) -> bool {
429
- block. stmts . iter ( ) . any ( never_loop_stmt) || block. expr . as_ref ( ) . map_or ( false , |e| never_loop_expr ( e) )
435
+ fn never_loop ( block : & Block , id : & NodeId ) -> bool {
436
+ !contains_continue_block ( block, id) && loop_exit_block ( block)
437
+ }
438
+
439
+ fn contains_continue_block ( block : & Block , dest : & NodeId ) -> bool {
440
+ block. stmts . iter ( ) . any ( |e| contains_continue_stmt ( e, dest) )
441
+ || block. expr . as_ref ( ) . map_or ( false , |e| contains_continue_expr ( e, dest) )
430
442
}
431
443
432
- fn never_loop_stmt ( stmt : & Stmt ) -> bool {
444
+ fn contains_continue_stmt ( stmt : & Stmt , dest : & NodeId ) -> bool {
433
445
match stmt. node {
434
446
StmtSemi ( ref e, _) |
435
- StmtExpr ( ref e, _) => never_loop_expr ( e ) ,
436
- StmtDecl ( ref d, _) => never_loop_decl ( d ) ,
447
+ StmtExpr ( ref e, _) => contains_continue_expr ( e , dest ) ,
448
+ StmtDecl ( ref d, _) => contains_continue_decl ( d , dest ) ,
437
449
}
438
450
}
439
451
440
- fn never_loop_decl ( decl : & Decl ) -> bool {
441
- if let DeclLocal ( ref local) = decl. node {
442
- local. init . as_ref ( ) . map_or ( false , |e| never_loop_expr ( e) )
443
- } else {
444
- false
452
+ fn contains_continue_decl ( decl : & Decl , dest : & NodeId ) -> bool {
453
+ match decl. node {
454
+ DeclLocal ( ref local) => local. init . as_ref ( ) . map_or ( false , |e| contains_continue_expr ( e, dest) ) ,
455
+ _ => false
445
456
}
446
457
}
447
458
448
- fn never_loop_expr ( expr : & Expr ) -> bool {
459
+ fn contains_continue_expr ( expr : & Expr , dest : & NodeId ) -> bool {
449
460
match expr. node {
450
- ExprBreak ( ..) | ExprRet ( ..) => true ,
451
461
ExprBox ( ref e) |
452
462
ExprUnary ( _, ref e) |
453
- ExprBinary ( _, ref e, _) | // because short-circuiting
454
463
ExprCast ( ref e, _) |
455
464
ExprType ( ref e, _) |
456
465
ExprField ( ref e, _) |
457
466
ExprTupField ( ref e, _) |
458
- ExprRepeat ( ref e, _) |
459
- ExprAddrOf ( _, ref e) => never_loop_expr ( e) ,
467
+ ExprAddrOf ( _, ref e) |
468
+ ExprRepeat ( ref e, _) => contains_continue_expr ( e, dest) ,
469
+ ExprArray ( ref es) |
470
+ ExprMethodCall ( _, _, ref es) |
471
+ ExprTup ( ref es) => es. iter ( ) . any ( |e| contains_continue_expr ( e, dest) ) ,
472
+ ExprCall ( ref e, ref es) => contains_continue_expr ( e, dest) || es. iter ( ) . any ( |e| contains_continue_expr ( e, dest) ) ,
473
+ ExprBinary ( _, ref e1, ref e2) |
460
474
ExprAssign ( ref e1, ref e2) |
461
475
ExprAssignOp ( _, ref e1, ref e2) |
462
- ExprIndex ( ref e1, ref e2) => never_loop_expr ( e1) || never_loop_expr ( e2) ,
476
+ ExprIndex ( ref e1, ref e2) => [ e1, e2] . iter ( ) . any ( |e| contains_continue_expr ( e, dest) ) ,
477
+ ExprIf ( ref e, ref e2, ref e3) => [ e, e2] . iter ( ) . chain ( e3. as_ref ( ) . iter ( ) ) . any ( |e| contains_continue_expr ( e, dest) ) ,
478
+ ExprWhile ( ref e, ref b, _) => contains_continue_expr ( e, dest) || contains_continue_block ( b, dest) ,
479
+ ExprMatch ( ref e, ref arms, _) => contains_continue_expr ( e, dest) || arms. iter ( ) . any ( |a| contains_continue_expr ( & a. body , dest) ) ,
480
+ ExprBlock ( ref block) => contains_continue_block ( block, dest) ,
481
+ ExprStruct ( _, _, ref base) => base. as_ref ( ) . map_or ( false , |e| contains_continue_expr ( e, dest) ) ,
482
+ ExprAgain ( d) => d. target_id . opt_id ( ) . map_or ( false , |id| id == * dest) ,
483
+ _ => false ,
484
+ }
485
+ }
486
+
487
+ fn loop_exit_block ( block : & Block ) -> bool {
488
+ block. stmts . iter ( ) . any ( |e| loop_exit_stmt ( e) )
489
+ || block. expr . as_ref ( ) . map_or ( false , |e| loop_exit_expr ( e) )
490
+ }
491
+
492
+ fn loop_exit_stmt ( stmt : & Stmt ) -> bool {
493
+ match stmt. node {
494
+ StmtSemi ( ref e, _) |
495
+ StmtExpr ( ref e, _) => loop_exit_expr ( e) ,
496
+ StmtDecl ( ref d, _) => loop_exit_decl ( d) ,
497
+ }
498
+ }
499
+
500
+ fn loop_exit_decl ( decl : & Decl ) -> bool {
501
+ match decl. node {
502
+ DeclLocal ( ref local) => local. init . as_ref ( ) . map_or ( false , |e| loop_exit_expr ( e) ) ,
503
+ _ => false
504
+ }
505
+ }
506
+
507
+ fn loop_exit_expr ( expr : & Expr ) -> bool {
508
+ match expr. node {
509
+ ExprBox ( ref e) |
510
+ ExprUnary ( _, ref e) |
511
+ ExprCast ( ref e, _) |
512
+ ExprType ( ref e, _) |
513
+ ExprField ( ref e, _) |
514
+ ExprTupField ( ref e, _) |
515
+ ExprAddrOf ( _, ref e) |
516
+ ExprRepeat ( ref e, _) => loop_exit_expr ( e) ,
463
517
ExprArray ( ref es) |
464
- ExprTup ( ref es) |
465
- ExprMethodCall ( _, _, ref es) => es. iter ( ) . any ( |e| never_loop_expr ( e) ) ,
466
- ExprCall ( ref e, ref es) => never_loop_expr ( e) || es. iter ( ) . any ( |e| never_loop_expr ( e) ) ,
467
- ExprBlock ( ref block) => never_loop_block ( block) ,
468
- ExprStruct ( _, _, ref base) => base. as_ref ( ) . map_or ( false , |e| never_loop_expr ( e) ) ,
518
+ ExprMethodCall ( _, _, ref es) |
519
+ ExprTup ( ref es) => es. iter ( ) . any ( |e| loop_exit_expr ( e) ) ,
520
+ ExprCall ( ref e, ref es) => loop_exit_expr ( e) || es. iter ( ) . any ( |e| loop_exit_expr ( e) ) ,
521
+ ExprBinary ( _, ref e1, ref e2) |
522
+ ExprAssign ( ref e1, ref e2) |
523
+ ExprAssignOp ( _, ref e1, ref e2) |
524
+ ExprIndex ( ref e1, ref e2) => [ e1, e2] . iter ( ) . any ( |e| loop_exit_expr ( e) ) ,
525
+ ExprIf ( ref e, ref e2, ref e3) => loop_exit_expr ( e) || e3. as_ref ( ) . map_or ( false , |e| loop_exit_expr ( e) ) && loop_exit_expr ( e2) ,
526
+ ExprWhile ( ref e, ref b, _) => loop_exit_expr ( e) || loop_exit_block ( b) ,
527
+ ExprMatch ( ref e, ref arms, _) => loop_exit_expr ( e) || arms. iter ( ) . all ( |a| loop_exit_expr ( & a. body ) ) ,
528
+ ExprBlock ( ref b) => loop_exit_block ( b) ,
529
+ ExprBreak ( _, _) |
530
+ ExprAgain ( _) |
531
+ ExprRet ( _) => true ,
469
532
_ => false ,
470
533
}
471
534
}
0 commit comments