Skip to content

Commit 3b80aef

Browse files
authored
Rollup merge of rust-lang#82297 - tmiasko:write-only, r=oli-obk
Consider auto derefs before warning about write only fields Changes from rust-lang#81473 extended the dead code lint with an ability to detect fields that are written to but never read from. The implementation skips over fields on the left hand side of an assignment, without marking them as live. A field access might involve an automatic dereference and de-facto read the field. Conservatively mark expressions with deref adjustments as live to avoid generating false positive warnings. Closes rust-lang#81626.
2 parents 50272eb + 343b673 commit 3b80aef

File tree

3 files changed

+85
-12
lines changed

3 files changed

+85
-12
lines changed

compiler/rustc_passes/src/dead.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,6 @@ fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
3737
)
3838
}
3939

40-
fn base_expr<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
41-
loop {
42-
match expr.kind {
43-
hir::ExprKind::Field(base, ..) => expr = base,
44-
_ => return expr,
45-
}
46-
}
47-
}
48-
4940
struct MarkSymbolVisitor<'tcx> {
5041
worklist: Vec<hir::HirId>,
5142
tcx: TyCtxt<'tcx>,
@@ -143,6 +134,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
143134
}
144135
}
145136

137+
fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) {
138+
if self
139+
.typeck_results()
140+
.expr_adjustments(expr)
141+
.iter()
142+
.any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
143+
{
144+
self.visit_expr(expr);
145+
} else if let hir::ExprKind::Field(base, ..) = expr.kind {
146+
// Ignore write to field
147+
self.handle_assign(base);
148+
} else {
149+
self.visit_expr(expr);
150+
}
151+
}
152+
146153
fn handle_field_pattern_match(
147154
&mut self,
148155
lhs: &hir::Pat<'_>,
@@ -272,8 +279,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
272279
self.lookup_and_handle_method(expr.hir_id);
273280
}
274281
hir::ExprKind::Assign(ref left, ref right, ..) => {
275-
// Ignore write to field
276-
self.visit_expr(base_expr(left));
282+
self.handle_assign(left);
277283
self.visit_expr(right);
278284
return;
279285
}

src/test/ui/lint/dead-code/write-only-field.rs

+49
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,53 @@ fn field_write(s: &mut S) {
1717
fn main() {
1818
let mut s = S { f: 0, sub: Sub { f: 0 } };
1919
field_write(&mut s);
20+
21+
auto_deref();
22+
nested_boxes();
23+
}
24+
25+
fn auto_deref() {
26+
struct E {
27+
x: bool,
28+
y: bool, //~ ERROR: field is never read
29+
}
30+
31+
struct P<'a> {
32+
e: &'a mut E
33+
}
34+
35+
impl P<'_> {
36+
fn f(&mut self) {
37+
self.e.x = true;
38+
self.e.y = true;
39+
}
40+
}
41+
42+
let mut e = E { x: false, y: false };
43+
let mut p = P { e: &mut e };
44+
p.f();
45+
assert!(e.x);
46+
}
47+
48+
fn nested_boxes() {
49+
struct A {
50+
b: Box<B>,
51+
}
52+
53+
struct B {
54+
c: Box<C>,
55+
}
56+
57+
struct C {
58+
u: u32, //~ ERROR: field is never read
59+
v: u32, //~ ERROR: field is never read
60+
}
61+
62+
let mut a = A {
63+
b: Box::new(B {
64+
c: Box::new(C { u: 0, v: 0 }),
65+
}),
66+
};
67+
a.b.c.v = 10;
68+
a.b.c = Box::new(C { u: 1, v: 2 });
2069
}

src/test/ui/lint/dead-code/write-only-field.stderr

+19-1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,23 @@ error: field is never read: `f`
2222
LL | f: i32,
2323
| ^^^^^^
2424

25-
error: aborting due to 3 previous errors
25+
error: field is never read: `y`
26+
--> $DIR/write-only-field.rs:28:9
27+
|
28+
LL | y: bool,
29+
| ^^^^^^^
30+
31+
error: field is never read: `u`
32+
--> $DIR/write-only-field.rs:58:9
33+
|
34+
LL | u: u32,
35+
| ^^^^^^
36+
37+
error: field is never read: `v`
38+
--> $DIR/write-only-field.rs:59:9
39+
|
40+
LL | v: u32,
41+
| ^^^^^^
42+
43+
error: aborting due to 6 previous errors
2644

0 commit comments

Comments
 (0)