Skip to content

Commit cef578b

Browse files
authored
Rollup merge of rust-lang#149072 - chenyukang:yukang-fix-unused-148960, r=davidtwco
Fix the issue of unused assignment from MIR liveness checking Fixes rust-lang#148960 Fixes rust-lang#148418 r? ``@davidtwco`` cc ``@cjgillot`` My first try on MIR related code, so it may not be the best fix.
2 parents 0279cba + 00f3155 commit cef578b

File tree

7 files changed

+200
-22
lines changed

7 files changed

+200
-22
lines changed

compiler/rustc_mir_transform/src/liveness.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ struct Access {
4545
/// When we encounter multiple statements at the same location, we only increase the liveness,
4646
/// in order to avoid false positives.
4747
live: bool,
48+
/// Is this a direct access to the place itself, no projections, or to a field?
49+
/// This helps distinguish `x = ...` from `x.field = ...`
50+
is_direct: bool,
4851
}
4952

5053
#[tracing::instrument(level = "debug", skip(tcx), ret)]
@@ -650,15 +653,17 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
650653
|place: Place<'tcx>, kind, source_info: SourceInfo, live: &DenseBitSet<PlaceIndex>| {
651654
if let Some((index, extra_projections)) = checked_places.get(place.as_ref()) {
652655
if !is_indirect(extra_projections) {
656+
let is_direct = extra_projections.is_empty();
653657
match assignments[index].entry(source_info) {
654658
IndexEntry::Vacant(v) => {
655-
let access = Access { kind, live: live.contains(index) };
659+
let access = Access { kind, live: live.contains(index), is_direct };
656660
v.insert(access);
657661
}
658662
IndexEntry::Occupied(mut o) => {
659663
// There were already a sighting. Mark this statement as live if it
660664
// was, to avoid false positives.
661665
o.get_mut().live |= live.contains(index);
666+
o.get_mut().is_direct &= is_direct;
662667
}
663668
}
664669
}
@@ -742,7 +747,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
742747
continue;
743748
};
744749
let source_info = body.local_decls[place.local].source_info;
745-
let access = Access { kind, live: live.contains(index) };
750+
let access = Access { kind, live: live.contains(index), is_direct: true };
746751
assignments[index].insert(source_info, access);
747752
}
748753
}
@@ -976,8 +981,10 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
976981
self.checked_places,
977982
self.body,
978983
) {
979-
statements.clear();
980-
continue;
984+
statements.retain(|_, access| access.is_direct);
985+
if statements.is_empty() {
986+
continue;
987+
}
981988
}
982989

983990
let typo = maybe_suggest_typo();
@@ -1048,26 +1055,28 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
10481055

10491056
let Some((name, decl_span)) = self.checked_places.names[index] else { continue };
10501057

1051-
// We have outstanding assignments and with non-trivial drop.
1052-
// This is probably a drop-guard, so we do not issue a warning there.
1053-
if maybe_drop_guard(
1058+
let is_maybe_drop_guard = maybe_drop_guard(
10541059
tcx,
10551060
self.typing_env,
10561061
index,
10571062
&self.ever_dropped,
10581063
self.checked_places,
10591064
self.body,
1060-
) {
1061-
continue;
1062-
}
1065+
);
10631066

10641067
// We probed MIR in reverse order for dataflow.
10651068
// We revert the vector to give a consistent order to the user.
1066-
for (source_info, Access { live, kind }) in statements.into_iter().rev() {
1069+
for (source_info, Access { live, kind, is_direct }) in statements.into_iter().rev() {
10671070
if live {
10681071
continue;
10691072
}
10701073

1074+
// If this place was dropped and has non-trivial drop,
1075+
// skip reporting field assignments.
1076+
if !is_direct && is_maybe_drop_guard {
1077+
continue;
1078+
}
1079+
10711080
// Report the dead assignment.
10721081
let Some(hir_id) = source_info.scope.lint_root(&self.body.source_scopes) else {
10731082
continue;

tests/ui/drop/or-pattern-drop-order.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ fn main() {
3333
// Drops are right-to-left: `z`, `y`, `x`.
3434
let (x, Ok(y) | Err(y), z);
3535
// Assignment order doesn't matter.
36-
z = LogDrop(o, 1);
37-
y = LogDrop(o, 2);
38-
x = LogDrop(o, 3);
36+
z = LogDrop(o, 1); //~ WARN value assigned to `z` is never read
37+
y = LogDrop(o, 2); //~ WARN value assigned to `y` is never read
38+
x = LogDrop(o, 3); //~ WARN value assigned to `x` is never read
3939
});
4040
assert_drop_order(1..=2, |o| {
4141
// The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
4242
let ((true, x, y) | (false, y, x));
43-
x = LogDrop(o, 2);
44-
y = LogDrop(o, 1);
43+
x = LogDrop(o, 2); //~ WARN value assigned to `x` is never read
44+
y = LogDrop(o, 1); //~ WARN value assigned to `y` is never read
4545
});
4646

4747
// `let pat = expr;` should have the same drop order.
@@ -61,15 +61,21 @@ fn main() {
6161
// `match` should have the same drop order.
6262
assert_drop_order(1..=3, |o| {
6363
// Drops are right-to-left: `z`, `y` `x`.
64-
match (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)) { (x, Ok(y) | Err(y), z) => {} }
64+
match (LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1)) {
65+
(x, Ok(y) | Err(y), z) => {}
66+
}
6567
});
6668
assert_drop_order(1..=2, |o| {
6769
// The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
68-
match (true, LogDrop(o, 2), LogDrop(o, 1)) { (true, x, y) | (false, y, x) => {} }
70+
match (true, LogDrop(o, 2), LogDrop(o, 1)) {
71+
(true, x, y) | (false, y, x) => {}
72+
}
6973
});
7074
assert_drop_order(1..=2, |o| {
7175
// That drop order is used regardless of which or-pattern alternative matches: `y`, `x`.
72-
match (false, LogDrop(o, 1), LogDrop(o, 2)) { (true, x, y) | (false, y, x) => {} }
76+
match (false, LogDrop(o, 1), LogDrop(o, 2)) {
77+
(true, x, y) | (false, y, x) => {}
78+
}
7379
});
7480

7581
// Function params are visited one-by-one, and the order of bindings within a param's pattern is
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
warning: value assigned to `x` is never read
2+
--> $DIR/or-pattern-drop-order.rs:43:9
3+
|
4+
LL | x = LogDrop(o, 2);
5+
| ^
6+
|
7+
= help: maybe it is overwritten before being read?
8+
= note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default
9+
10+
warning: value assigned to `y` is never read
11+
--> $DIR/or-pattern-drop-order.rs:44:9
12+
|
13+
LL | y = LogDrop(o, 1);
14+
| ^
15+
|
16+
= help: maybe it is overwritten before being read?
17+
18+
warning: value assigned to `x` is never read
19+
--> $DIR/or-pattern-drop-order.rs:38:9
20+
|
21+
LL | x = LogDrop(o, 3);
22+
| ^
23+
|
24+
= help: maybe it is overwritten before being read?
25+
26+
warning: value assigned to `y` is never read
27+
--> $DIR/or-pattern-drop-order.rs:37:9
28+
|
29+
LL | y = LogDrop(o, 2);
30+
| ^
31+
|
32+
= help: maybe it is overwritten before being read?
33+
34+
warning: value assigned to `z` is never read
35+
--> $DIR/or-pattern-drop-order.rs:36:9
36+
|
37+
LL | z = LogDrop(o, 1);
38+
| ^
39+
|
40+
= help: maybe it is overwritten before being read?
41+
42+
warning: 5 warnings emitted
43+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//@ check-fail
2+
#![deny(unused)]
3+
#![allow(dead_code)]
4+
5+
fn test_one_extra_assign() {
6+
let mut value = b"0".to_vec(); //~ ERROR value assigned to `value` is never read
7+
value = b"1".to_vec();
8+
println!("{:?}", value);
9+
}
10+
11+
fn test_two_extra_assign() {
12+
let mut x = 1; //~ ERROR value assigned to `x` is never read
13+
x = 2; //~ ERROR value assigned to `x` is never read
14+
x = 3;
15+
println!("{}", x);
16+
}
17+
18+
struct Point {
19+
x: i32,
20+
y: i32,
21+
}
22+
23+
fn test_indirect_assign() {
24+
let mut p = Point { x: 1, y: 1 }; //~ ERROR value assigned to `p` is never read
25+
p = Point { x: 2, y: 2 };
26+
p.x = 3;
27+
println!("{}", p.y);
28+
}
29+
30+
struct Foo;
31+
32+
impl Drop for Foo {
33+
fn drop(&mut self) {}
34+
}
35+
36+
// testcase for issue #148418
37+
fn test_unused_variable() {
38+
let mut foo = Foo; //~ ERROR variable `foo` is assigned to, but never used
39+
foo = Foo; //~ ERROR value assigned to `foo` is never read
40+
}
41+
42+
fn main() {}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
error: value assigned to `value` is never read
2+
--> $DIR/unused-assign-148960.rs:6:21
3+
|
4+
LL | let mut value = b"0".to_vec();
5+
| ^^^^^^^^^^^^^
6+
|
7+
= help: maybe it is overwritten before being read?
8+
note: the lint level is defined here
9+
--> $DIR/unused-assign-148960.rs:2:9
10+
|
11+
LL | #![deny(unused)]
12+
| ^^^^^^
13+
= note: `#[deny(unused_assignments)]` implied by `#[deny(unused)]`
14+
15+
error: value assigned to `x` is never read
16+
--> $DIR/unused-assign-148960.rs:12:17
17+
|
18+
LL | let mut x = 1;
19+
| ^
20+
|
21+
= help: maybe it is overwritten before being read?
22+
23+
error: value assigned to `x` is never read
24+
--> $DIR/unused-assign-148960.rs:13:5
25+
|
26+
LL | x = 2;
27+
| ^^^^^
28+
|
29+
= help: maybe it is overwritten before being read?
30+
31+
error: value assigned to `p` is never read
32+
--> $DIR/unused-assign-148960.rs:24:17
33+
|
34+
LL | let mut p = Point { x: 1, y: 1 };
35+
| ^^^^^^^^^^^^^^^^^^^^
36+
|
37+
= help: maybe it is overwritten before being read?
38+
39+
error: variable `foo` is assigned to, but never used
40+
--> $DIR/unused-assign-148960.rs:38:9
41+
|
42+
LL | let mut foo = Foo;
43+
| ^^^^^^^
44+
|
45+
= note: consider using `_foo` instead
46+
= note: `#[deny(unused_variables)]` implied by `#[deny(unused)]`
47+
help: you might have meant to pattern match on the similarly named struct `Foo`
48+
|
49+
LL - let mut foo = Foo;
50+
LL + let Foo = Foo;
51+
|
52+
53+
error: value assigned to `foo` is never read
54+
--> $DIR/unused-assign-148960.rs:39:5
55+
|
56+
LL | foo = Foo;
57+
| ^^^
58+
|
59+
= help: maybe it is overwritten before being read?
60+
61+
error: aborting due to 6 previous errors
62+

tests/ui/liveness/liveness-unused.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ fn f10<T>(mut a: T, b: T) {
244244
//~^ ERROR value assigned to `a` is never read
245245
}
246246

247-
fn f10b<T>(mut a: Box<T>, b: Box<T>) {
248-
a = b;
247+
fn f10b<T>(mut a: Box<T>, b: Box<T>) { //~ ERROR variable `a` is assigned to, but never used
248+
a = b; //~ ERROR value assigned to `a` is never read
249249
}
250250

251251
// unused params warnings are not needed for intrinsic functions without bodies

tests/ui/liveness/liveness-unused.stderr

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,5 +283,21 @@ LL | a = b;
283283
|
284284
= help: maybe it is overwritten before being read?
285285

286-
error: aborting due to 34 previous errors; 1 warning emitted
286+
error: variable `a` is assigned to, but never used
287+
--> $DIR/liveness-unused.rs:247:12
288+
|
289+
LL | fn f10b<T>(mut a: Box<T>, b: Box<T>) {
290+
| ^^^^^
291+
|
292+
= note: consider using `_a` instead
293+
294+
error: value assigned to `a` is never read
295+
--> $DIR/liveness-unused.rs:248:5
296+
|
297+
LL | a = b;
298+
| ^
299+
|
300+
= help: maybe it is overwritten before being read?
301+
302+
error: aborting due to 36 previous errors; 1 warning emitted
287303

0 commit comments

Comments
 (0)