Skip to content

Commit b6e2c47

Browse files
authored
Merge pull request #2572 from flip1995/immut_while
Fix check of immutable condition in closure
2 parents 29c449e + 7d29075 commit b6e2c47

File tree

3 files changed

+50
-21
lines changed

3 files changed

+50
-21
lines changed

clippy_lints/src/loops.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,9 @@ declare_lint! {
347347
/// **Why is this bad?** If the condition is unchanged, entering the body of the loop
348348
/// will lead to an infinite loop.
349349
///
350-
/// **Known problems:** None
350+
/// **Known problems:** If the `while`-loop is in a closure, the check for mutation of the
351+
/// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
352+
/// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
351353
///
352354
/// **Example:**
353355
/// ```rust
@@ -2152,11 +2154,15 @@ fn check_infinite_loop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cond: &'tcx Expr, b
21522154

21532155
let mut delegate = MutVarsDelegate {
21542156
used_mutably: mut_var_visitor.ids,
2157+
skip: false,
21552158
};
21562159
let def_id = def_id::DefId::local(block.hir_id.owner);
21572160
let region_scope_tree = &cx.tcx.region_scope_tree(def_id);
21582161
ExprUseVisitor::new(&mut delegate, cx.tcx, cx.param_env, region_scope_tree, cx.tables, None).walk_expr(expr);
21592162

2163+
if delegate.skip {
2164+
return;
2165+
}
21602166
if !delegate.used_mutably.iter().any(|(_, v)| *v) {
21612167
span_lint(
21622168
cx,
@@ -2183,9 +2189,13 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
21832189
if let ExprPath(ref qpath) = ex.node;
21842190
if let QPath::Resolved(None, _) = *qpath;
21852191
let def = self.cx.tables.qpath_def(qpath, ex.hir_id);
2186-
if let Def::Local(node_id) = def;
21872192
then {
2188-
self.ids.insert(node_id, false);
2193+
match def {
2194+
Def::Local(node_id) | Def::Upvar(node_id, ..) => {
2195+
self.ids.insert(node_id, false);
2196+
},
2197+
_ => {},
2198+
}
21892199
}
21902200
}
21912201
}
@@ -2211,6 +2221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
22112221

22122222
struct MutVarsDelegate {
22132223
used_mutably: HashMap<NodeId, bool>,
2224+
skip: bool,
22142225
}
22152226

22162227
impl<'tcx> MutVarsDelegate {
@@ -2220,6 +2231,12 @@ impl<'tcx> MutVarsDelegate {
22202231
if let Some(used) = self.used_mutably.get_mut(&id) {
22212232
*used = true;
22222233
},
2234+
Categorization::Upvar(_) => {
2235+
//FIXME: This causes false negatives. We can't get the `NodeId` from
2236+
//`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
2237+
//`while`-body, not just the ones in the condition.
2238+
self.skip = true
2239+
},
22232240
Categorization::Deref(ref cmt, _) => self.update(&cmt.cat, sp),
22242241
_ => {}
22252242
}

tests/ui/infinite_loop.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
2+
3+
14
fn fn_val(i: i32) -> i32 { unimplemented!() }
25
fn fn_constref(i: &i32) -> i32 { unimplemented!() }
36
fn fn_mutref(i: &mut i32) { unimplemented!() }
47
fn fooi() -> i32 { unimplemented!() }
58
fn foob() -> bool { unimplemented!() }
69

10+
#[allow(many_single_char_names)]
711
fn immutable_condition() {
812
// Should warn when all vars mentionned are immutable
913
let y = 0;
@@ -43,6 +47,14 @@ fn immutable_condition() {
4347
println!("OK - Fn call results may vary");
4448
}
4549

50+
let mut a = 0;
51+
let mut c = move || {
52+
while a < 5 {
53+
a += 1;
54+
println!("OK - a is mutable");
55+
}
56+
};
57+
c();
4658
}
4759

4860
fn unused_var() {

tests/ui/infinite_loop.stderr

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,57 @@
11
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
2-
--> $DIR/infinite_loop.rs:10:11
2+
--> $DIR/infinite_loop.rs:14:11
33
|
4-
10 | while y < 10 {
4+
14 | while y < 10 {
55
| ^^^^^^
66
|
77
= note: `-D while-immutable-condition` implied by `-D warnings`
88

99
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
10-
--> $DIR/infinite_loop.rs:15:11
10+
--> $DIR/infinite_loop.rs:19:11
1111
|
12-
15 | while y < 10 && x < 3 {
12+
19 | while y < 10 && x < 3 {
1313
| ^^^^^^^^^^^^^^^
1414

1515
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
16-
--> $DIR/infinite_loop.rs:22:11
16+
--> $DIR/infinite_loop.rs:26:11
1717
|
18-
22 | while !cond {
18+
26 | while !cond {
1919
| ^^^^^
2020

2121
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
22-
--> $DIR/infinite_loop.rs:52:11
22+
--> $DIR/infinite_loop.rs:64:11
2323
|
24-
52 | while i < 3 {
24+
64 | while i < 3 {
2525
| ^^^^^
2626

2727
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
28-
--> $DIR/infinite_loop.rs:57:11
28+
--> $DIR/infinite_loop.rs:69:11
2929
|
30-
57 | while i < 3 && j > 0 {
30+
69 | while i < 3 && j > 0 {
3131
| ^^^^^^^^^^^^^^
3232

3333
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
34-
--> $DIR/infinite_loop.rs:61:11
34+
--> $DIR/infinite_loop.rs:73:11
3535
|
36-
61 | while i < 3 {
36+
73 | while i < 3 {
3737
| ^^^^^
3838

3939
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
40-
--> $DIR/infinite_loop.rs:76:11
40+
--> $DIR/infinite_loop.rs:88:11
4141
|
42-
76 | while i < 3 {
42+
88 | while i < 3 {
4343
| ^^^^^
4444

4545
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
46-
--> $DIR/infinite_loop.rs:81:11
46+
--> $DIR/infinite_loop.rs:93:11
4747
|
48-
81 | while i < 3 {
48+
93 | while i < 3 {
4949
| ^^^^^
5050

5151
error: Variable in the condition are not mutated in the loop body. This either leads to an infinite or to a never running loop.
52-
--> $DIR/infinite_loop.rs:144:15
52+
--> $DIR/infinite_loop.rs:156:15
5353
|
54-
144 | while self.count < n {
54+
156 | while self.count < n {
5555
| ^^^^^^^^^^^^^^
5656

5757
error: aborting due to 9 previous errors

0 commit comments

Comments
 (0)