@@ -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,140 +491,107 @@ 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 contains_continue_block ( block : & Block , dest : Option < NodeId > ) -> bool {
493
- block. stmts . iter ( ) . any ( |e| contains_continue_stmt ( e, dest) ) ||
494
- block. expr . as_ref ( ) . map_or (
495
- false ,
496
- |e| contains_continue_expr ( e, dest) ,
497
- )
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)
498
504
}
499
505
500
- fn contains_continue_stmt ( stmt : & Stmt , dest : Option < NodeId > ) -> bool {
506
+ fn stmt_to_expr ( stmt : & Stmt ) -> Option < & Expr > {
501
507
match stmt. node {
502
- StmtSemi ( ref e, _ ) |
503
- StmtExpr ( ref e, _ ) => contains_continue_expr ( e , dest ) ,
504
- StmtDecl ( ref d, _ ) => contains_continue_decl ( d , dest ) ,
508
+ StmtSemi ( ref e, .. ) |
509
+ StmtExpr ( ref e, .. ) => Some ( e ) ,
510
+ StmtDecl ( ref d, .. ) => decl_to_expr ( d ) ,
505
511
}
506
512
}
507
513
508
- fn contains_continue_decl ( decl : & Decl , dest : Option < NodeId > ) -> bool {
514
+ fn decl_to_expr ( decl : & Decl ) -> Option < & Expr > {
509
515
match decl. node {
510
- DeclLocal ( ref local) => {
511
- local. init . as_ref ( ) . map_or (
512
- false ,
513
- |e| contains_continue_expr ( e, dest) ,
514
- )
515
- } ,
516
- _ => false ,
516
+ DeclLocal ( ref local) => local. init . as_ref ( ) . map ( |p| & * * p) ,
517
+ _ => None ,
517
518
}
518
519
}
519
520
520
- fn contains_continue_expr ( expr : & Expr , dest : Option < NodeId > ) -> bool {
521
+ fn never_loop_expr ( expr : & Expr , state : & mut NeverLoopState ) -> bool {
521
522
match expr. node {
522
- ExprRet ( Some ( ref e) ) |
523
523
ExprBox ( ref e) |
524
524
ExprUnary ( _, ref e) |
525
525
ExprCast ( ref e, _) |
526
526
ExprType ( ref e, _) |
527
527
ExprField ( ref e, _) |
528
528
ExprTupField ( ref e, _) |
529
529
ExprAddrOf ( _, ref e) |
530
- ExprRepeat ( ref e, _) => contains_continue_expr ( e, dest ) ,
530
+ ExprRepeat ( ref e, _) => never_loop_expr ( e, state ) ,
531
531
ExprArray ( ref es) |
532
532
ExprMethodCall ( _, _, ref es) |
533
- ExprTup ( ref es) => es. iter ( ) . any ( |e| contains_continue_expr ( e, dest) ) ,
534
- ExprCall ( ref e, ref es) => {
535
- contains_continue_expr ( e, dest) || es. iter ( ) . any ( |e| contains_continue_expr ( e, dest) )
536
- } ,
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) ,
537
535
ExprBinary ( _, ref e1, ref e2) |
538
536
ExprAssign ( ref e1, ref e2) |
539
537
ExprAssignOp ( _, ref e1, ref e2) |
540
- ExprIndex ( ref e1, ref e2) => [ e1, e2] . iter ( ) . any ( |e| contains_continue_expr ( e , dest ) ) ,
538
+ ExprIndex ( ref e1, ref e2) => never_loop_expr_seq ( & mut [ & * * e1, & * * e2] . iter ( ) . cloned ( ) , state ) ,
541
539
ExprIf ( ref e, ref e2, ref e3) => {
542
- [ e, e2] . iter ( ) . chain ( e3. as_ref ( ) . iter ( ) ) . any ( |e| {
543
- contains_continue_expr ( e, dest)
544
- } )
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
+ } ,
550
+ ExprLoop ( ref b, _, _) => {
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
555
+ } ,
556
+ ExprWhile ( ref e, ref b, _) => {
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)
545
562
} ,
546
- ExprWhile ( ref e, ref b, _) => contains_continue_expr ( e, dest) || contains_continue_block ( b, dest) ,
547
563
ExprMatch ( ref e, ref arms, _) => {
548
- contains_continue_expr ( e, dest) || arms. iter ( ) . any ( |a| contains_continue_expr ( & a. body , dest) )
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
549
567
} ,
550
- ExprBlock ( ref block) |
551
- ExprLoop ( ref block, ..) => contains_continue_block ( block, dest) ,
552
- ExprStruct ( _, _, ref base) => {
553
- base. as_ref ( ) . map_or (
554
- false ,
555
- |e| contains_continue_expr ( e, dest) ,
556
- )
568
+ ExprBlock ( ref b) => never_loop_block ( b, state) ,
569
+ ExprAgain ( d) => {
570
+ let id = d. target_id . opt_id ( ) . expect ( "target id can only be missing in the presence of compilation errors" ) ;
571
+ state. continues . insert ( id) ;
572
+ false
557
573
} ,
558
- ExprAgain ( d) => dest. map_or ( true , |dest| d. target_id . opt_id ( ) . map_or ( false , |id| id == dest) ) ,
559
- _ => false ,
560
- }
561
- }
562
-
563
- fn loop_exit_block ( block : & Block , loops : & mut Vec < NodeId > ) -> bool {
564
- block. stmts . iter ( ) . take_while ( |s| !contains_continue_stmt ( s, None ) ) . any ( |s| loop_exit_stmt ( s, loops) )
565
- || block. expr . as_ref ( ) . map_or ( false , |e| loop_exit_expr ( e, loops) )
566
- }
567
-
568
- fn loop_exit_stmt ( stmt : & Stmt , loops : & mut Vec < NodeId > ) -> bool {
569
- match stmt. node {
570
- StmtSemi ( ref e, _) |
571
- StmtExpr ( ref e, _) => loop_exit_expr ( e, loops) ,
572
- StmtDecl ( ref d, _) => loop_exit_decl ( d, loops) ,
574
+ ExprBreak ( d, _) => {
575
+ let id = d. target_id . opt_id ( ) . expect ( "target id can only be missing in the presence of compilation errors" ) ;
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 ,
573
586
}
574
587
}
575
588
576
- fn loop_exit_decl ( decl : & Decl , loops : & mut Vec < NodeId > ) -> bool {
577
- match decl. node {
578
- DeclLocal ( ref local) => local. init . as_ref ( ) . map_or ( false , |e| loop_exit_expr ( e, loops) ) ,
579
- _ => false ,
580
- }
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)
581
591
}
582
592
583
- fn loop_exit_expr ( expr : & Expr , loops : & mut Vec < NodeId > ) -> bool {
584
- match expr. node {
585
- ExprBox ( ref e) |
586
- ExprUnary ( _, ref e) |
587
- ExprCast ( ref e, _) |
588
- ExprType ( ref e, _) |
589
- ExprField ( ref e, _) |
590
- ExprTupField ( ref e, _) |
591
- ExprAddrOf ( _, ref e) |
592
- ExprRepeat ( ref e, _) => loop_exit_expr ( e, loops) ,
593
- ExprArray ( ref es) |
594
- ExprMethodCall ( _, _, ref es) |
595
- ExprTup ( ref es) => es. iter ( ) . any ( |e| loop_exit_expr ( e, loops) ) ,
596
- ExprCall ( ref e, ref es) => loop_exit_expr ( e, loops) || es. iter ( ) . any ( |e| loop_exit_expr ( e, loops) ) ,
597
- ExprBinary ( _, ref e1, ref e2) |
598
- ExprAssign ( ref e1, ref e2) |
599
- ExprAssignOp ( _, ref e1, ref e2) |
600
- ExprIndex ( ref e1, ref e2) => [ e1, e2] . iter ( ) . any ( |e| loop_exit_expr ( e, loops) ) ,
601
- ExprIf ( ref e, ref e2, ref e3) => loop_exit_expr ( e, loops)
602
- || e3. as_ref ( ) . map_or ( false , |e3| loop_exit_expr ( e3, loops) ) && loop_exit_expr ( e2, loops) ,
603
- ExprLoop ( ref b, _, _) => {
604
- loops. push ( expr. id ) ;
605
- let val = loop_exit_block ( b, loops) ;
606
- loops. pop ( ) ;
607
- val
608
- } ,
609
- ExprWhile ( ref e, ref b, _) => {
610
- loops. push ( expr. id ) ;
611
- let val = loop_exit_expr ( e, loops) || loop_exit_block ( b, loops) ;
612
- loops. pop ( ) ;
613
- val
614
- } ,
615
- ExprMatch ( ref e, ref arms, _) => loop_exit_expr ( e, loops) || arms. iter ( ) . all ( |a| loop_exit_expr ( & a. body , loops) ) ,
616
- ExprBlock ( ref b) => loop_exit_block ( b, loops) ,
617
- ExprAgain ( d) => d. target_id . opt_id ( ) . map_or ( false , |id| loops. iter ( ) . skip ( 1 ) . all ( |& id2| id != id2) ) ,
618
- ExprBreak ( d, _) => d. target_id . opt_id ( ) . map_or ( false , |id| loops[ 0 ] == id) ,
619
- ExprRet ( _) => true ,
620
- _ => false ,
621
- }
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)
622
595
}
623
596
624
597
fn check_for_loop < ' a , ' tcx > (
0 commit comments