add tests and fixes

This commit is contained in:
Cameron Steffen 2017-05-31 23:22:15 -05:00
parent 20728fb0d0
commit a73edc0944
4 changed files with 218 additions and 50 deletions

View file

@ -330,6 +330,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
if let Some((pat, arg, body)) = higher::for_loop(expr) { if let Some((pat, arg, body)) = higher::for_loop(expr) {
check_for_loop(cx, pat, arg, body, expr); check_for_loop(cx, pat, arg, body, expr);
} }
// check for never_loop
match expr.node {
ExprWhile(_, ref block, _) |
ExprLoop(ref block, _, _) => {
if never_loop(block, &expr.id) {
span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops");
}
},
_ => (),
}
// check for `loop { if let {} else break }` that could be `while let` // check for `loop { if let {} else break }` that could be `while let`
// (also matches an explicit "match" instead of "if let") // (also matches an explicit "match" instead of "if let")
// (even if the "match" or "if let" is used for declaration) // (even if the "match" or "if let" is used for declaration)
@ -342,9 +354,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
"empty `loop {}` detected. You may want to either use `panic!()` or add \ "empty `loop {}` detected. You may want to either use `panic!()` or add \
`std::thread::sleep(..);` to the loop body."); `std::thread::sleep(..);` to the loop body.");
} }
if never_loop(block, &expr.id) {
span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops");
}
// extract the expression from the first statement (if any) in a block // extract the expression from the first statement (if any) in a block
let inner_stmt_expr = extract_expr_from_first_stmt(block); let inner_stmt_expr = extract_expr_from_first_stmt(block);
@ -456,14 +465,17 @@ fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
ExprTupField(ref e, _) | ExprTupField(ref e, _) |
ExprAddrOf(_, ref e) | ExprAddrOf(_, ref e) |
ExprRepeat(ref e, _) => contains_continue_expr(e, dest), ExprRepeat(ref e, _) => contains_continue_expr(e, dest),
ExprArray(ref es) |
ExprMethodCall(_, _, ref es) |
ExprTup(ref es) => es.iter().any(|e| contains_continue_expr(e, dest)),
ExprCall(ref e, ref es) => contains_continue_expr(e, dest) || es.iter().any(|e| contains_continue_expr(e, dest)),
ExprBinary(_, ref e1, ref e2) | ExprBinary(_, ref e1, ref e2) |
ExprAssign(ref e1, ref e2) | ExprAssign(ref e1, ref e2) |
ExprAssignOp(_, ref e1, ref e2) | ExprAssignOp(_, ref e1, ref e2) |
ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| contains_continue_expr(e, dest)), ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| contains_continue_expr(e, dest)),
ExprArray(ref es) | ExprIf(ref e, ref e2, ref e3) => [e, e2].iter().chain(e3.as_ref().iter()).any(|e| contains_continue_expr(e, dest)),
ExprTup(ref es) | ExprWhile(ref e, ref b, _) => contains_continue_expr(e, dest) || contains_continue_block(b, dest),
ExprMethodCall(_, _, ref es) => es.iter().any(|e| contains_continue_expr(e, dest)), ExprMatch(ref e, ref arms, _) => contains_continue_expr(e, dest) || arms.iter().any(|a| contains_continue_expr(&a.body, dest)),
ExprCall(ref e, ref es) => contains_continue_expr(e, dest) || es.iter().any(|e| contains_continue_expr(e, dest)),
ExprBlock(ref block) => contains_continue_block(block, dest), ExprBlock(ref block) => contains_continue_block(block, dest),
ExprStruct(_, _, ref base) => base.as_ref().map_or(false, |e| contains_continue_expr(e, dest)), ExprStruct(_, _, ref base) => base.as_ref().map_or(false, |e| contains_continue_expr(e, dest)),
ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| id == *dest), ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| id == *dest),
@ -501,8 +513,8 @@ fn loop_exit_expr(expr: &Expr) -> bool {
ExprTupField(ref e, _) | ExprTupField(ref e, _) |
ExprAddrOf(_, ref e) | ExprAddrOf(_, ref e) |
ExprRepeat(ref e, _) => loop_exit_expr(e), ExprRepeat(ref e, _) => loop_exit_expr(e),
ExprMethodCall(_, _, ref es) => es.iter().any(|e| loop_exit_expr(e)),
ExprArray(ref es) | ExprArray(ref es) |
ExprMethodCall(_, _, ref es) |
ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e)), ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e)),
ExprCall(ref e, ref es) => loop_exit_expr(e) || es.iter().any(|e| loop_exit_expr(e)), ExprCall(ref e, ref es) => loop_exit_expr(e) || es.iter().any(|e| loop_exit_expr(e)),
ExprBinary(_, ref e1, ref e2) | ExprBinary(_, ref e1, ref e2) |

View file

@ -53,6 +53,28 @@ error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`
= note: `-D for-loop-over-result` implied by `-D warnings` = note: `-D for-loop-over-result` implied by `-D warnings`
= help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")`
error: this loop never actually loops
--> for_loop.rs:52:5
|
52 | / while let Some(x) = option {
53 | | println!("{}", x);
54 | | break;
55 | | }
| |_____^
|
= note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops
--> for_loop.rs:58:5
|
58 | / while let Ok(x) = result {
59 | | println!("{}", x);
60 | | break;
61 | | }
| |_____^
|
= note: `-D never-loop` implied by `-D warnings`
error: the loop variable `i` is only used to index `vec`. error: the loop variable `i` is only used to index `vec`.
--> for_loop.rs:84:5 --> for_loop.rs:84:5
| |

View file

@ -1,57 +1,118 @@
#![feature(plugin)] #![feature(plugin)]
#![plugin(clippy)] #![plugin(clippy)]
#![allow(single_match, unused_assignments, unused_variables)]
#![warn(never_loop)] fn test1() {
#![allow(single_match, while_true)] let mut x = 0;
loop { // never_loop
fn break_stmt() { x += 1;
loop { if x == 1 {
return
}
break; break;
} }
} }
fn conditional_break() { fn test2() {
let mut x = 5; let mut x = 0;
loop { loop {
x -= 1; x += 1;
if x == 1 { if x == 1 {
break break
} }
} }
} }
fn nested_loop() { fn test3() {
loop { let mut x = 0;
while true { loop { // never loops
break x += 1;
}
break break
} }
} }
fn if_false() { fn test4() {
let x = 1; let mut x = 1;
loop {
if x == 1 {
return
}
}
}
fn match_false() {
let x = 1;
loop { loop {
x += 1;
match x { match x {
1 => return, 5 => return,
_ => (), _ => (),
} }
} }
} }
fn main() { fn test5() {
break_stmt(); let i = 0;
conditional_break(); loop { // never loops
nested_loop(); while i == 0 { // never loops
if_false(); break
match_false(); }
return
}
} }
fn test6() {
let mut x = 0;
'outer: loop { // never loops
x += 1;
loop { // never loops
if x == 5 { break }
continue 'outer
}
return
}
}
fn test7() {
let mut x = 0;
loop {
x += 1;
match x {
1 => continue,
_ => (),
}
return
}
}
fn test8() {
let mut x = 0;
loop {
x += 1;
match x {
5 => return,
_ => continue,
}
}
}
fn test9() {
let x = Some(1);
while let Some(y) = x { // never loops
return
}
}
fn test10() {
for x in 0..10 { // never loops
match x {
1 => break,
_ => return,
}
}
}
fn main() {
test1();
test2();
test3();
test4();
test5();
test6();
test7();
test8();
test9();
test10();
}

View file

@ -1,22 +1,95 @@
error: this loop never actually loops error: this loop never actually loops
--> never_loop.rs:8:5 --> never_loop.rs:7:5
| |
8 | / loop { 7 | / loop { // never_loop
9 | | break; 8 | | x += 1;
10 | | } 9 | | if x == 1 {
10 | | return
11 | | }
12 | | break;
13 | | }
| |_____^ | |_____^
| |
= note: `-D never-loop` implied by `-D warnings` = note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops error: this loop never actually loops
--> never_loop.rs:24:5 --> never_loop.rs:28:5
| |
24 | / loop { 28 | / loop { // never loops
25 | | while true { 29 | | x += 1;
26 | | break 30 | | break
27 | | } 31 | | }
28 | | break | |_____^
29 | | } |
= note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops
--> never_loop.rs:47:2
|
47 | / loop { // never loops
48 | | while i == 0 { // never loops
49 | | break
50 | | }
51 | | return
52 | | }
| |__^
|
= note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops
--> never_loop.rs:48:9
|
48 | / while i == 0 { // never loops
49 | | break
50 | | }
| |_________^
|
= note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops
--> never_loop.rs:57:5
|
57 | / 'outer: loop { // never loops
58 | | x += 1;
59 | | loop { // never loops
60 | | if x == 5 { break }
... |
63 | | return
64 | | }
| |__^
|
= note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops
--> never_loop.rs:59:3
|
59 | / loop { // never loops
60 | | if x == 5 { break }
61 | | continue 'outer
62 | | }
| |___^
|
= note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops
--> never_loop.rs:92:5
|
92 | / while let Some(y) = x { // never loops
93 | | return
94 | | }
| |_____^
|
= note: `-D never-loop` implied by `-D warnings`
error: this loop never actually loops
--> never_loop.rs:98:5
|
98 | / for x in 0..10 { // never loops
99 | | match x {
100 | | 1 => break,
101 | | _ => return,
102 | | }
103 | | }
| |_____^ | |_____^
| |
= note: `-D never-loop` implied by `-D warnings` = note: `-D never-loop` implied by `-D warnings`