Skip to content

Commit 9ef6227

Browse files
authored
Rollup merge of #72465 - tmiasko:liveness-upvars, r=nikomatsakis
Warn about unused captured variables Include captured variables in liveness analysis. Warn when captured variables are unused (but possibly read or written to). Warn about dead assignments to captured variables. Fixes #37707. Fixes #47128. Fixes #63220.
2 parents 8bce240 + 4dc5661 commit 9ef6227

12 files changed

+543
-123
lines changed

src/librustc_passes/liveness.rs

+208-113
Large diffs are not rendered by default.

src/test/ui/closures/closure-immutable-outer-variable.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
88

99
fn main() {
1010
let mut y = true;
11-
foo(Box::new(move || y = false) as Box<_>);
11+
foo(Box::new(move || y = !y) as Box<_>);
1212
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
1313
}

src/test/ui/closures/closure-immutable-outer-variable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
88

99
fn main() {
1010
let y = true;
11-
foo(Box::new(move || y = false) as Box<_>);
11+
foo(Box::new(move || y = !y) as Box<_>);
1212
//~^ ERROR cannot assign to `y`, as it is not declared as mutable
1313
}

src/test/ui/closures/closure-immutable-outer-variable.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ error[E0594]: cannot assign to `y`, as it is not declared as mutable
33
|
44
LL | let y = true;
55
| - help: consider changing this to be mutable: `mut y`
6-
LL | foo(Box::new(move || y = false) as Box<_>);
7-
| ^^^^^^^^^ cannot assign
6+
LL | foo(Box::new(move || y = !y) as Box<_>);
7+
| ^^^^^^ cannot assign
88

99
error: aborting due to previous error
1010

src/test/ui/issues/issue-11958.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// run-pass
2-
#![forbid(warnings)]
32

43
// We shouldn't need to rebind a moved upvar as mut if it's already
54
// marked as mut
65

76
pub fn main() {
87
let mut x = 1;
98
let _thunk = Box::new(move|| { x = 2; });
9+
//~^ WARN value assigned to `x` is never read
10+
//~| WARN unused variable: `x`
1011
}

src/test/ui/issues/issue-11958.stderr

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
warning: value assigned to `x` is never read
2+
--> $DIR/issue-11958.rs:8:36
3+
|
4+
LL | let _thunk = Box::new(move|| { x = 2; });
5+
| ^
6+
|
7+
= note: `#[warn(unused_assignments)]` on by default
8+
= help: maybe it is overwritten before being read?
9+
10+
warning: unused variable: `x`
11+
--> $DIR/issue-11958.rs:8:36
12+
|
13+
LL | let _thunk = Box::new(move|| { x = 2; });
14+
| ^
15+
|
16+
= note: `#[warn(unused_variables)]` on by default
17+
= help: did you mean to capture by reference instead?
18+
19+
warning: 2 warnings emitted
20+
+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// edition:2018
2+
// check-pass
3+
#![warn(unused)]
4+
#![allow(unreachable_code)]
5+
6+
pub fn unintentional_copy_one() {
7+
let mut last = None;
8+
let mut f = move |s| {
9+
last = Some(s); //~ WARN value assigned to `last` is never read
10+
//~| WARN unused variable: `last`
11+
};
12+
f("a");
13+
f("b");
14+
f("c");
15+
dbg!(last.unwrap());
16+
}
17+
18+
pub fn unintentional_copy_two() {
19+
let mut sum = 0;
20+
(1..10).for_each(move |x| {
21+
sum += x; //~ WARN unused variable: `sum`
22+
});
23+
dbg!(sum);
24+
}
25+
26+
pub fn f() {
27+
let mut c = 0;
28+
29+
// Captured by value, but variable is dead on entry.
30+
move || {
31+
c = 1; //~ WARN value captured by `c` is never read
32+
println!("{}", c);
33+
};
34+
let _ = async move {
35+
c = 1; //~ WARN value captured by `c` is never read
36+
println!("{}", c);
37+
};
38+
39+
// Read and written to, but never actually used.
40+
move || {
41+
c += 1; //~ WARN unused variable: `c`
42+
};
43+
let _ = async move {
44+
c += 1; //~ WARN value assigned to `c` is never read
45+
//~| WARN unused variable: `c`
46+
};
47+
48+
move || {
49+
println!("{}", c);
50+
// Value is read by closure itself on later invocations.
51+
c += 1;
52+
};
53+
let b = Box::new(42);
54+
move || {
55+
println!("{}", c);
56+
// Never read because this is FnOnce closure.
57+
c += 1; //~ WARN value assigned to `c` is never read
58+
drop(b);
59+
};
60+
let _ = async move {
61+
println!("{}", c);
62+
// Never read because this is a generator.
63+
c += 1; //~ WARN value assigned to `c` is never read
64+
};
65+
}
66+
67+
pub fn nested() {
68+
let mut d = None;
69+
let mut e = None;
70+
|| {
71+
|| {
72+
d = Some("d1"); //~ WARN value assigned to `d` is never read
73+
d = Some("d2");
74+
};
75+
move || {
76+
e = Some("e1"); //~ WARN value assigned to `e` is never read
77+
//~| WARN unused variable: `e`
78+
e = Some("e2"); //~ WARN value assigned to `e` is never read
79+
};
80+
};
81+
}
82+
83+
pub fn g<T: Default>(mut v: T) {
84+
|r| {
85+
if r {
86+
v = T::default(); //~ WARN value assigned to `v` is never read
87+
} else {
88+
drop(v);
89+
}
90+
};
91+
}
92+
93+
pub fn h<T: Copy + Default + std::fmt::Debug>() {
94+
let mut z = T::default();
95+
move |b| {
96+
loop {
97+
if b {
98+
z = T::default(); //~ WARN value assigned to `z` is never read
99+
//~| WARN unused variable: `z`
100+
} else {
101+
return;
102+
}
103+
}
104+
dbg!(z);
105+
};
106+
}
107+
108+
fn main() {}
+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
warning: value assigned to `last` is never read
2+
--> $DIR/liveness-upvars.rs:9:9
3+
|
4+
LL | last = Some(s);
5+
| ^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/liveness-upvars.rs:3:9
9+
|
10+
LL | #![warn(unused)]
11+
| ^^^^^^
12+
= note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
13+
= help: maybe it is overwritten before being read?
14+
15+
warning: unused variable: `last`
16+
--> $DIR/liveness-upvars.rs:9:9
17+
|
18+
LL | last = Some(s);
19+
| ^^^^
20+
|
21+
note: the lint level is defined here
22+
--> $DIR/liveness-upvars.rs:3:9
23+
|
24+
LL | #![warn(unused)]
25+
| ^^^^^^
26+
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
27+
= help: did you mean to capture by reference instead?
28+
29+
warning: unused variable: `sum`
30+
--> $DIR/liveness-upvars.rs:21:9
31+
|
32+
LL | sum += x;
33+
| ^^^
34+
|
35+
= help: did you mean to capture by reference instead?
36+
37+
warning: value captured by `c` is never read
38+
--> $DIR/liveness-upvars.rs:31:9
39+
|
40+
LL | c = 1;
41+
| ^
42+
|
43+
= help: did you mean to capture by reference instead?
44+
45+
warning: value captured by `c` is never read
46+
--> $DIR/liveness-upvars.rs:35:9
47+
|
48+
LL | c = 1;
49+
| ^
50+
|
51+
= help: did you mean to capture by reference instead?
52+
53+
warning: unused variable: `c`
54+
--> $DIR/liveness-upvars.rs:41:9
55+
|
56+
LL | c += 1;
57+
| ^
58+
|
59+
= help: did you mean to capture by reference instead?
60+
61+
warning: value assigned to `c` is never read
62+
--> $DIR/liveness-upvars.rs:44:9
63+
|
64+
LL | c += 1;
65+
| ^
66+
|
67+
= help: maybe it is overwritten before being read?
68+
69+
warning: unused variable: `c`
70+
--> $DIR/liveness-upvars.rs:44:9
71+
|
72+
LL | c += 1;
73+
| ^
74+
|
75+
= help: did you mean to capture by reference instead?
76+
77+
warning: value assigned to `c` is never read
78+
--> $DIR/liveness-upvars.rs:57:9
79+
|
80+
LL | c += 1;
81+
| ^
82+
|
83+
= help: maybe it is overwritten before being read?
84+
85+
warning: value assigned to `c` is never read
86+
--> $DIR/liveness-upvars.rs:63:9
87+
|
88+
LL | c += 1;
89+
| ^
90+
|
91+
= help: maybe it is overwritten before being read?
92+
93+
warning: value assigned to `d` is never read
94+
--> $DIR/liveness-upvars.rs:72:13
95+
|
96+
LL | d = Some("d1");
97+
| ^
98+
|
99+
= help: maybe it is overwritten before being read?
100+
101+
warning: value assigned to `e` is never read
102+
--> $DIR/liveness-upvars.rs:76:13
103+
|
104+
LL | e = Some("e1");
105+
| ^
106+
|
107+
= help: maybe it is overwritten before being read?
108+
109+
warning: value assigned to `e` is never read
110+
--> $DIR/liveness-upvars.rs:78:13
111+
|
112+
LL | e = Some("e2");
113+
| ^
114+
|
115+
= help: maybe it is overwritten before being read?
116+
117+
warning: unused variable: `e`
118+
--> $DIR/liveness-upvars.rs:76:13
119+
|
120+
LL | e = Some("e1");
121+
| ^
122+
|
123+
= help: did you mean to capture by reference instead?
124+
125+
warning: value assigned to `v` is never read
126+
--> $DIR/liveness-upvars.rs:86:13
127+
|
128+
LL | v = T::default();
129+
| ^
130+
|
131+
= help: maybe it is overwritten before being read?
132+
133+
warning: value assigned to `z` is never read
134+
--> $DIR/liveness-upvars.rs:98:17
135+
|
136+
LL | z = T::default();
137+
| ^
138+
|
139+
= help: maybe it is overwritten before being read?
140+
141+
warning: unused variable: `z`
142+
--> $DIR/liveness-upvars.rs:98:17
143+
|
144+
LL | z = T::default();
145+
| ^
146+
|
147+
= help: did you mean to capture by reference instead?
148+
149+
warning: 17 warnings emitted
150+

src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// run-pass
2-
#![allow(unused_variables)]
32
// Test that we mutate a counter on the stack only when we expect to.
43

54
fn call<F>(f: F) where F : FnOnce() {
@@ -13,7 +12,7 @@ fn main() {
1312
call(|| {
1413
// Move `y`, but do not move `counter`, even though it is read
1514
// by value (note that it is also mutated).
16-
for item in y {
15+
for item in y { //~ WARN unused variable: `item`
1716
let v = counter;
1817
counter += v;
1918
}
@@ -22,7 +21,8 @@ fn main() {
2221

2322
call(move || {
2423
// this mutates a moved copy, and hence doesn't affect original
25-
counter += 1;
24+
counter += 1; //~ WARN value assigned to `counter` is never read
25+
//~| WARN unused variable: `counter`
2626
});
2727
assert_eq!(counter, 88);
2828
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
warning: unused variable: `item`
2+
--> $DIR/unboxed-closures-counter-not-moved.rs:15:13
3+
|
4+
LL | for item in y {
5+
| ^^^^ help: if this is intentional, prefix it with an underscore: `_item`
6+
|
7+
= note: `#[warn(unused_variables)]` on by default
8+
9+
warning: value assigned to `counter` is never read
10+
--> $DIR/unboxed-closures-counter-not-moved.rs:24:9
11+
|
12+
LL | counter += 1;
13+
| ^^^^^^^
14+
|
15+
= note: `#[warn(unused_assignments)]` on by default
16+
= help: maybe it is overwritten before being read?
17+
18+
warning: unused variable: `counter`
19+
--> $DIR/unboxed-closures-counter-not-moved.rs:24:9
20+
|
21+
LL | counter += 1;
22+
| ^^^^^^^
23+
|
24+
= help: did you mean to capture by reference instead?
25+
26+
warning: 3 warnings emitted
27+

src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ fn set(x: &mut usize) { *x = 42; }
1313
fn main() {
1414
{
1515
let mut x = 0_usize;
16-
move || x += 1;
16+
move || x += 1; //~ WARN unused variable: `x`
1717
}
1818
{
1919
let mut x = 0_usize;
20-
move || x += 1;
20+
move || x += 1; //~ WARN unused variable: `x`
2121
}
2222
{
2323
let mut x = 0_usize;

0 commit comments

Comments
 (0)