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