Skip to content

Commit 12a7d14

Browse files
authored
Merge pull request #2111 from camsteffen/never_loop
Fix never_loop
2 parents 346936a + d92d5a8 commit 12a7d14

File tree

2 files changed

+53
-26
lines changed

2 files changed

+53
-26
lines changed

clippy_lints/src/loops.rs

+39-26
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
378378
match expr.node {
379379
ExprWhile(_, ref block, _) |
380380
ExprLoop(ref block, _, _) => {
381-
if never_loop(block, &expr.id) {
381+
if never_loop(block, expr.id) {
382382
span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops");
383383
}
384384
},
@@ -485,27 +485,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
485485
}
486486
}
487487

488-
fn never_loop(block: &Block, id: &NodeId) -> bool {
489-
!contains_continue_block(block, id) && loop_exit_block(block)
488+
fn never_loop(block: &Block, id: NodeId) -> bool {
489+
!contains_continue_block(block, Some(id)) && loop_exit_block(block, &mut vec![id])
490490
}
491491

492-
fn contains_continue_block(block: &Block, dest: &NodeId) -> bool {
492+
fn contains_continue_block(block: &Block, dest: Option<NodeId>) -> bool {
493493
block.stmts.iter().any(|e| contains_continue_stmt(e, dest)) ||
494494
block.expr.as_ref().map_or(
495495
false,
496496
|e| contains_continue_expr(e, dest),
497497
)
498498
}
499499

500-
fn contains_continue_stmt(stmt: &Stmt, dest: &NodeId) -> bool {
500+
fn contains_continue_stmt(stmt: &Stmt, dest: Option<NodeId>) -> bool {
501501
match stmt.node {
502502
StmtSemi(ref e, _) |
503503
StmtExpr(ref e, _) => contains_continue_expr(e, dest),
504504
StmtDecl(ref d, _) => contains_continue_decl(d, dest),
505505
}
506506
}
507507

508-
fn contains_continue_decl(decl: &Decl, dest: &NodeId) -> bool {
508+
fn contains_continue_decl(decl: &Decl, dest: Option<NodeId>) -> bool {
509509
match decl.node {
510510
DeclLocal(ref local) => {
511511
local.init.as_ref().map_or(
@@ -517,7 +517,7 @@ fn contains_continue_decl(decl: &Decl, dest: &NodeId) -> bool {
517517
}
518518
}
519519

520-
fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
520+
fn contains_continue_expr(expr: &Expr, dest: Option<NodeId>) -> bool {
521521
match expr.node {
522522
ExprRet(Some(ref e)) |
523523
ExprBox(ref e) |
@@ -555,31 +555,32 @@ fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
555555
|e| contains_continue_expr(e, dest),
556556
)
557557
},
558-
ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| id == *dest),
558+
ExprAgain(d) => dest.map_or(true, |dest| d.target_id.opt_id().map_or(false, |id| id == dest)),
559559
_ => false,
560560
}
561561
}
562562

563-
fn loop_exit_block(block: &Block) -> bool {
564-
block.stmts.iter().any(|e| loop_exit_stmt(e)) || block.expr.as_ref().map_or(false, |e| loop_exit_expr(e))
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))
565566
}
566567

567-
fn loop_exit_stmt(stmt: &Stmt) -> bool {
568+
fn loop_exit_stmt(stmt: &Stmt, loops: &mut Vec<NodeId>) -> bool {
568569
match stmt.node {
569570
StmtSemi(ref e, _) |
570-
StmtExpr(ref e, _) => loop_exit_expr(e),
571-
StmtDecl(ref d, _) => loop_exit_decl(d),
571+
StmtExpr(ref e, _) => loop_exit_expr(e, loops),
572+
StmtDecl(ref d, _) => loop_exit_decl(d, loops),
572573
}
573574
}
574575

575-
fn loop_exit_decl(decl: &Decl) -> bool {
576+
fn loop_exit_decl(decl: &Decl, loops: &mut Vec<NodeId>) -> bool {
576577
match decl.node {
577-
DeclLocal(ref local) => local.init.as_ref().map_or(false, |e| loop_exit_expr(e)),
578+
DeclLocal(ref local) => local.init.as_ref().map_or(false, |e| loop_exit_expr(e, loops)),
578579
_ => false,
579580
}
580581
}
581582

582-
fn loop_exit_expr(expr: &Expr) -> bool {
583+
fn loop_exit_expr(expr: &Expr, loops: &mut Vec<NodeId>) -> bool {
583584
match expr.node {
584585
ExprBox(ref e) |
585586
ExprUnary(_, ref e) |
@@ -588,22 +589,34 @@ fn loop_exit_expr(expr: &Expr) -> bool {
588589
ExprField(ref e, _) |
589590
ExprTupField(ref e, _) |
590591
ExprAddrOf(_, ref e) |
591-
ExprRepeat(ref e, _) => loop_exit_expr(e),
592+
ExprRepeat(ref e, _) => loop_exit_expr(e, loops),
592593
ExprArray(ref es) |
593594
ExprMethodCall(_, _, ref es) |
594-
ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e)),
595-
ExprCall(ref e, ref es) => loop_exit_expr(e) || es.iter().any(|e| loop_exit_expr(e)),
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)),
596597
ExprBinary(_, ref e1, ref e2) |
597598
ExprAssign(ref e1, ref e2) |
598599
ExprAssignOp(_, ref e1, ref e2) |
599-
ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| loop_exit_expr(e)),
600-
ExprIf(ref e, ref e2, ref e3) => {
601-
loop_exit_expr(e) || e3.as_ref().map_or(false, |e| loop_exit_expr(e)) && loop_exit_expr(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
602614
},
603-
ExprWhile(ref e, ref b, _) => loop_exit_expr(e) || loop_exit_block(b),
604-
ExprMatch(ref e, ref arms, _) => loop_exit_expr(e) || arms.iter().all(|a| loop_exit_expr(&a.body)),
605-
ExprBlock(ref b) => loop_exit_block(b),
606-
ExprBreak(_, _) | ExprAgain(_) | ExprRet(_) => true,
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,
607620
_ => false,
608621
}
609622
}

tests/ui/never_loop.rs

+14
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@ pub fn test12(a: bool, b: bool) {
126126
}
127127
}
128128

129+
pub fn test13() {
130+
let mut a = true;
131+
loop { // infinite loop
132+
while a {
133+
if true {
134+
a = false;
135+
continue;
136+
}
137+
return;
138+
}
139+
}
140+
}
141+
129142
fn main() {
130143
test1();
131144
test2();
@@ -139,5 +152,6 @@ fn main() {
139152
test10();
140153
test11(|| 0);
141154
test12(true, false);
155+
test13();
142156
}
143157

0 commit comments

Comments
 (0)