Skip to content

Commit 7cbe93a

Browse files
committed
Extend #[must_use] lint to user-defined ADTs
1 parent 4894123 commit 7cbe93a

File tree

3 files changed

+207
-5
lines changed

3 files changed

+207
-5
lines changed

src/librustc_lint/unused.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use rustc::hir::def::{Res, DefKind};
22
use rustc::hir::def_id::DefId;
3+
use rustc::hir::HirVec;
34
use rustc::lint;
45
use rustc::ty::{self, Ty};
6+
use rustc::ty::subst::Subst;
57
use rustc::ty::adjustment;
68
use rustc_data_structures::fx::FxHashMap;
79
use lint::{LateContext, EarlyContext, LintContext, LintArray};
@@ -151,8 +153,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
151153
let descr_pre = &format!("{}boxed ", descr_pre);
152154
check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural)
153155
}
154-
ty::Adt(def, _) => {
155-
check_must_use_def(cx, def.did, span, descr_pre, descr_post)
156+
ty::Adt(def, subst) => {
157+
// Check the type itself for `#[must_use]` annotations.
158+
let mut has_emitted = check_must_use_def(
159+
cx, def.did, span, descr_pre, descr_post);
160+
// Check any fields of the type for `#[must_use]` annotations.
161+
// We ignore ADTs with more than one variant for simplicity and to avoid
162+
// false positives.
163+
// Unions are also ignored (though in theory, we could lint if every field of
164+
// a union was `#[must_use]`).
165+
if def.variants.len() == 1 && !def.is_union() {
166+
let fields = match &expr.node {
167+
hir::ExprKind::Struct(_, fields, _) => {
168+
fields.iter().map(|f| &*f.expr).collect()
169+
}
170+
hir::ExprKind::Call(_, args) => args.iter().collect(),
171+
_ => HirVec::new(),
172+
};
173+
174+
for variant in &def.variants {
175+
for (i, field) in variant.fields.iter().enumerate() {
176+
let descr_post
177+
= &format!(" in field `{}`", field.ident.as_str());
178+
let ty = cx.tcx.type_of(field.did).subst(cx.tcx, subst);
179+
let (expr, span) = if let Some(&field) = fields.get(i) {
180+
(field, field.span)
181+
} else {
182+
(expr, span)
183+
};
184+
has_emitted |= check_must_use_ty(
185+
cx, ty, expr, span, descr_pre, descr_post, plural);
186+
}
187+
}
188+
}
189+
has_emitted
156190
}
157191
ty::Opaque(def, _) => {
158192
let mut has_emitted = false;
@@ -202,9 +236,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
202236
for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
203237
let descr_post = &format!(" in tuple element {}", i);
204238
let span = *spans.get(i).unwrap_or(&span);
205-
if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural) {
206-
has_emitted = true;
207-
}
239+
has_emitted |= check_must_use_ty(
240+
cx, ty, expr, span, descr_pre, descr_post, plural);
208241
}
209242
has_emitted
210243
}

src/test/ui/lint/must_use-adt.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#![deny(unused_must_use)]
2+
3+
#[must_use]
4+
struct S;
5+
6+
#[must_use]
7+
trait A {}
8+
9+
struct B;
10+
11+
impl A for B {}
12+
13+
struct T(S);
14+
15+
struct U {
16+
x: (),
17+
y: T,
18+
}
19+
20+
struct V {
21+
a: S,
22+
}
23+
24+
struct W {
25+
w: [(u8, Box<dyn A>); 2],
26+
x: u32,
27+
y: (B, B),
28+
z: (S, S),
29+
e: [(u8, Box<dyn A>); 2],
30+
f: S,
31+
}
32+
33+
fn get_v() -> V {
34+
V { a: S }
35+
}
36+
37+
struct Z([(u8, Box<dyn A>); 2]);
38+
39+
fn get_wrapped_arr() -> Z {
40+
Z([(0, Box::new(B)), (0, Box::new(B))])
41+
}
42+
43+
fn get_tuple_arr() -> ([(u8, Box<dyn A>); 2],) {
44+
([(0, Box::new(B)), (0, Box::new(B))],)
45+
}
46+
47+
struct R<T> {
48+
r: T
49+
}
50+
51+
struct List<T>(T, Option<Box<Self>>);
52+
53+
fn main() {
54+
S; //~ ERROR unused `S` that must be used
55+
T(S); //~ ERROR unused `S` in field `0` that must be used
56+
U { x: (), y: T(S) }; //~ ERROR unused `S` in field `0` that must be used
57+
get_v(); //~ ERROR unused `S` in field `a` that must be used
58+
V { a: S }; //~ ERROR unused `S` in field `a` that must be used
59+
W {
60+
w: [(0, Box::new(B)), (0, Box::new(B))],
61+
//~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be used
62+
x: 0,
63+
y: (B, B),
64+
z: (S, S),
65+
//~^ unused `S` in tuple element 0 that must be used
66+
//~^^ unused `S` in tuple element 1 that must be used
67+
e: [(0, Box::new(B)), (0, Box::new(B))],
68+
//~^ unused array of boxed `A` trait objects in tuple element 1 that must be used
69+
f: S, //~ ERROR unused `S` in field `f` that must be used
70+
};
71+
get_wrapped_arr();
72+
//~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be use
73+
get_tuple_arr();
74+
//~^ ERROR unused array of boxed `A` trait objects in tuple element 1 that must be used
75+
R { r: S }; //~ ERROR unused `S` in field `r` that must be used
76+
List(S, Some(Box::new(List(S, None)))); //~ ERROR unused `S` in field `0` that must be used
77+
}

src/test/ui/lint/must_use-adt.stderr

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
error: unused `S` that must be used
2+
--> $DIR/must_use-adt.rs:54:5
3+
|
4+
LL | S;
5+
| ^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/must_use-adt.rs:1:9
9+
|
10+
LL | #![deny(unused_must_use)]
11+
| ^^^^^^^^^^^^^^^
12+
13+
error: unused `S` in field `0` that must be used
14+
--> $DIR/must_use-adt.rs:55:7
15+
|
16+
LL | T(S);
17+
| ^
18+
19+
error: unused `S` in field `0` that must be used
20+
--> $DIR/must_use-adt.rs:56:21
21+
|
22+
LL | U { x: (), y: T(S) };
23+
| ^
24+
25+
error: unused `S` in field `a` that must be used
26+
--> $DIR/must_use-adt.rs:57:5
27+
|
28+
LL | get_v();
29+
| ^^^^^^^^
30+
31+
error: unused `S` in field `a` that must be used
32+
--> $DIR/must_use-adt.rs:58:12
33+
|
34+
LL | V { a: S };
35+
| ^
36+
37+
error: unused array of boxed `A` trait objects in tuple element 1 that must be used
38+
--> $DIR/must_use-adt.rs:60:12
39+
|
40+
LL | w: [(0, Box::new(B)), (0, Box::new(B))],
41+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42+
43+
error: unused `S` in tuple element 0 that must be used
44+
--> $DIR/must_use-adt.rs:64:13
45+
|
46+
LL | z: (S, S),
47+
| ^
48+
49+
error: unused `S` in tuple element 1 that must be used
50+
--> $DIR/must_use-adt.rs:64:16
51+
|
52+
LL | z: (S, S),
53+
| ^
54+
55+
error: unused array of boxed `A` trait objects in tuple element 1 that must be used
56+
--> $DIR/must_use-adt.rs:67:12
57+
|
58+
LL | e: [(0, Box::new(B)), (0, Box::new(B))],
59+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
60+
61+
error: unused `S` in field `f` that must be used
62+
--> $DIR/must_use-adt.rs:69:12
63+
|
64+
LL | f: S,
65+
| ^
66+
67+
error: unused array of boxed `A` trait objects in tuple element 1 that must be used
68+
--> $DIR/must_use-adt.rs:71:5
69+
|
70+
LL | get_wrapped_arr();
71+
| ^^^^^^^^^^^^^^^^^^
72+
73+
error: unused array of boxed `A` trait objects in tuple element 1 that must be used
74+
--> $DIR/must_use-adt.rs:73:5
75+
|
76+
LL | get_tuple_arr();
77+
| ^^^^^^^^^^^^^^^^
78+
79+
error: unused `S` in field `r` that must be used
80+
--> $DIR/must_use-adt.rs:75:12
81+
|
82+
LL | R { r: S };
83+
| ^
84+
85+
error: unused `S` in field `0` that must be used
86+
--> $DIR/must_use-adt.rs:76:10
87+
|
88+
LL | List(S, Some(Box::new(List(S, None))));
89+
| ^
90+
91+
error: aborting due to 14 previous errors
92+

0 commit comments

Comments
 (0)