@@ -16,6 +16,7 @@ use rustc::ty::{self, Ty};
16
16
use rustc:: ty:: subst:: { Subst , Substs } ;
17
17
use rustc_const_eval:: ConstContext ;
18
18
use std:: collections:: { HashMap , HashSet } ;
19
+ use std:: iter:: { Iterator , once} ;
19
20
use syntax:: ast;
20
21
use syntax:: codemap:: Span ;
21
22
use utils:: sugg;
@@ -378,7 +379,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
378
379
match expr. node {
379
380
ExprWhile ( _, ref block, _) |
380
381
ExprLoop ( ref block, _, _) => {
381
- if never_loop ( block, expr. id ) {
382
+ let mut state = NeverLoopState {
383
+ breaks : HashSet :: new ( ) ,
384
+ continues : HashSet :: new ( ) ,
385
+ } ;
386
+ let may_complete = never_loop_block ( block, & mut state) ;
387
+ if !may_complete && !state. continues . contains ( & expr. id ) {
382
388
span_lint ( cx, NEVER_LOOP , expr. span , "this loop never actually loops" ) ;
383
389
}
384
390
} ,
@@ -485,31 +491,34 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
485
491
}
486
492
}
487
493
488
- fn never_loop ( block : & Block , id : NodeId ) -> bool {
489
- !contains_continue_block ( block, Some ( id) ) && loop_exit_block ( block, & mut vec ! [ id] )
494
+ struct NeverLoopState {
495
+ breaks : HashSet < NodeId > ,
496
+ continues : HashSet < NodeId > ,
490
497
}
491
498
492
- fn loop_exit_block ( block : & Block , loops : & mut Vec < NodeId > ) -> bool {
493
- block. stmts . iter ( ) . take_while ( |s| !contains_continue_stmt ( s, None ) ) . any ( |s| loop_exit_stmt ( s, loops) )
494
- || block. expr . as_ref ( ) . map_or ( false , |e| loop_exit_expr ( e, loops) )
499
+ fn never_loop_block ( block : & Block , state : & mut NeverLoopState ) -> bool {
500
+ let stmts = block. stmts . iter ( ) . map ( stmt_to_expr) ;
501
+ let expr = once ( block. expr . as_ref ( ) . map ( |p| & * * p) ) ;
502
+ let mut iter = stmts. chain ( expr) . filter_map ( |e| e) ;
503
+ never_loop_expr_seq ( & mut iter, state)
495
504
}
496
505
497
- fn loop_exit_stmt ( stmt : & Stmt , loops : & mut Vec < NodeId > ) -> bool {
506
+ fn stmt_to_expr ( stmt : & Stmt ) -> Option < & Expr > {
498
507
match stmt. node {
499
- StmtSemi ( ref e, _ ) |
500
- StmtExpr ( ref e, _ ) => loop_exit_expr ( e , loops ) ,
501
- StmtDecl ( ref d, _ ) => loop_exit_decl ( d , loops ) ,
508
+ StmtSemi ( ref e, .. ) |
509
+ StmtExpr ( ref e, .. ) => Some ( e ) ,
510
+ StmtDecl ( ref d, .. ) => decl_to_expr ( d ) ,
502
511
}
503
512
}
504
513
505
- fn loop_exit_decl ( decl : & Decl , loops : & mut Vec < NodeId > ) -> bool {
514
+ fn decl_to_expr ( decl : & Decl ) -> Option < & Expr > {
506
515
match decl. node {
507
- DeclLocal ( ref local) => local. init . as_ref ( ) . map_or ( false , |e| loop_exit_expr ( e , loops ) ) ,
508
- _ => false ,
516
+ DeclLocal ( ref local) => local. init . as_ref ( ) . map ( |p| & * * p ) ,
517
+ _ => None ,
509
518
}
510
519
}
511
520
512
- fn loop_exit_expr ( expr : & Expr , loops : & mut Vec < NodeId > ) -> bool {
521
+ fn never_loop_expr ( expr : & Expr , state : & mut NeverLoopState ) -> bool {
513
522
match expr. node {
514
523
ExprBox ( ref e) |
515
524
ExprUnary ( _, ref e) |
@@ -518,38 +527,73 @@ fn loop_exit_expr(expr: &Expr, loops: &mut Vec<NodeId>) -> bool {
518
527
ExprField ( ref e, _) |
519
528
ExprTupField ( ref e, _) |
520
529
ExprAddrOf ( _, ref e) |
521
- ExprRepeat ( ref e, _) => loop_exit_expr ( e, loops ) ,
530
+ ExprRepeat ( ref e, _) => never_loop_expr ( e, state ) ,
522
531
ExprArray ( ref es) |
523
532
ExprMethodCall ( _, _, ref es) |
524
- ExprTup ( ref es) => es. iter ( ) . any ( |e| loop_exit_expr ( e , loops ) ) ,
525
- ExprCall ( ref e, ref es) => loop_exit_expr ( e , loops ) || es. iter ( ) . any ( |e| loop_exit_expr ( e , loops ) ) ,
533
+ ExprTup ( ref es) => never_loop_expr_seq ( & mut es. iter ( ) , state ) ,
534
+ ExprCall ( ref e, ref es) => never_loop_expr_seq ( & mut once ( & * * e ) . chain ( es. iter ( ) ) , state ) ,
526
535
ExprBinary ( _, ref e1, ref e2) |
527
536
ExprAssign ( ref e1, ref e2) |
528
537
ExprAssignOp ( _, ref e1, ref e2) |
529
- ExprIndex ( ref e1, ref e2) => [ e1, e2] . iter ( ) . any ( |e| loop_exit_expr ( e, loops) ) ,
530
- ExprIf ( ref e, ref e2, ref e3) => loop_exit_expr ( e, loops)
531
- || e3. as_ref ( ) . map_or ( false , |e3| loop_exit_expr ( e3, loops) ) && loop_exit_expr ( e2, loops) ,
538
+ ExprIndex ( ref e1, ref e2) => never_loop_expr_seq ( & mut [ & * * e1, & * * e2] . iter ( ) . cloned ( ) , state) ,
539
+ ExprIf ( ref e, ref e2, ref e3) => {
540
+ let e1 = never_loop_expr ( e, state) ;
541
+ let e2 = never_loop_expr ( e2, state) ;
542
+ match * e3 {
543
+ Some ( ref e3) => {
544
+ let e3 = never_loop_expr ( e3, state) ;
545
+ e1 && ( e2 || e3)
546
+ } ,
547
+ None => e1,
548
+ }
549
+ } ,
532
550
ExprLoop ( ref b, _, _) => {
533
- loops . push ( expr . id ) ;
534
- let val = loop_exit_block ( b , loops ) ;
535
- loops . pop ( ) ;
536
- val
551
+ let block_may_complete = never_loop_block ( b , state ) ;
552
+ let has_break = state . breaks . remove ( & expr . id ) ;
553
+ state . continues . remove ( & expr . id ) ;
554
+ block_may_complete || has_break
537
555
} ,
538
556
ExprWhile ( ref e, ref b, _) => {
539
- loops. push ( expr. id ) ;
540
- let val = loop_exit_expr ( e, loops) || loop_exit_block ( b, loops) ;
541
- loops. pop ( ) ;
542
- val
557
+ let e = never_loop_expr ( e, state) ;
558
+ let block_may_complete = never_loop_block ( b, state) ;
559
+ let has_break = state. breaks . remove ( & expr. id ) ;
560
+ let has_continue = state. continues . remove ( & expr. id ) ;
561
+ e && ( block_may_complete || has_break || has_continue)
543
562
} ,
544
- ExprMatch ( ref e, ref arms, _) => loop_exit_expr ( e, loops) || arms. iter ( ) . all ( |a| loop_exit_expr ( & a. body , loops) ) ,
545
- ExprBlock ( ref b) => loop_exit_block ( b, loops) ,
546
- ExprAgain ( d) => d. target_id . opt_id ( ) . map_or ( false , |id| loops. iter ( ) . skip ( 1 ) . all ( |& id2| id != id2) ) ,
547
- ExprBreak ( d, _) => d. target_id . opt_id ( ) . map_or ( false , |id| loops[ 0 ] == id) ,
548
- ExprRet ( _) => true ,
549
- _ => false ,
563
+ ExprMatch ( ref e, ref arms, _) => {
564
+ let e = never_loop_expr ( e, state) ;
565
+ let arms = never_loop_expr_branch ( & mut arms. iter ( ) . map ( |a| & * a. body ) , state) ;
566
+ e && arms
567
+ } ,
568
+ ExprBlock ( ref b) => never_loop_block ( b, state) ,
569
+ ExprAgain ( d) => {
570
+ let id = d. target_id . opt_id ( ) . expect ( "continue is missing target id" ) ;
571
+ state. continues . insert ( id) ;
572
+ false
573
+ } ,
574
+ ExprBreak ( d, _) => {
575
+ let id = d. target_id . opt_id ( ) . expect ( "break is missing target id" ) ;
576
+ state. breaks . insert ( id) ;
577
+ false
578
+ } ,
579
+ ExprRet ( ref e) => {
580
+ if let Some ( ref e) = * e {
581
+ never_loop_expr ( e, state) ;
582
+ }
583
+ false
584
+ } ,
585
+ _ => true ,
550
586
}
551
587
}
552
588
589
+ fn never_loop_expr_seq < ' a , T : Iterator < Item =& ' a Expr > > ( es : & mut T , state : & mut NeverLoopState ) -> bool {
590
+ es. map ( |e| never_loop_expr ( e, state) ) . fold ( true , |a, b| a && b)
591
+ }
592
+
593
+ fn never_loop_expr_branch < ' a , T : Iterator < Item =& ' a Expr > > ( e : & mut T , state : & mut NeverLoopState ) -> bool {
594
+ e. map ( |e| never_loop_expr ( e, state) ) . fold ( false , |a, b| a || b)
595
+ }
596
+
553
597
fn check_for_loop < ' a , ' tcx > (
554
598
cx : & LateContext < ' a , ' tcx > ,
555
599
pat : & ' tcx Pat ,
0 commit comments