Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3991bfb

Browse files
committedNov 20, 2018
Auto merge of rust-lang#55663 - varkor:must_use-traits, r=estebank
Allow #[must_use] on traits Addresses rust-lang#55506, but we'll probably want to add it to some library traits like `Iterator` before the issue is considered fixed. Fixes rust-lang#51560. `#[must_use]` is already permitted on traits, with no effect, so this seems like a bug fix, but I might be overlooking something. This currently warns for `impl Trait` or `dyn Trait` when the `Trait` is `#[must_use]` (although I don't think the latter is currently possible, so it's simply future-proofed).
2 parents 15e6613 + 737dec0 commit 3991bfb

File tree

3 files changed

+79
-16
lines changed

3 files changed

+79
-16
lines changed
 

‎src/librustc_lint/unused.rs

+43-16
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,39 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
6060
}
6161

6262
let t = cx.tables.expr_ty(&expr);
63-
// FIXME(varkor): replace with `t.is_unit() || t.conservative_is_uninhabited()`.
64-
let type_permits_no_use = match t.sty {
65-
ty::Tuple(ref tys) if tys.is_empty() => true,
66-
ty::Never => true,
67-
ty::Adt(def, _) => {
68-
if def.variants.is_empty() {
69-
true
70-
} else {
71-
check_must_use(cx, def.did, s.span, "")
63+
let type_permits_lack_of_use = if t.is_unit()
64+
|| cx.tcx.is_ty_uninhabited_from(cx.tcx.hir.get_module_parent(expr.id), t) {
65+
true
66+
} else {
67+
match t.sty {
68+
ty::Adt(def, _) => check_must_use(cx, def.did, s.span, "", ""),
69+
ty::Opaque(def, _) => {
70+
let mut must_use = false;
71+
for (predicate, _) in &cx.tcx.predicates_of(def).predicates {
72+
if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
73+
let trait_ref = poly_trait_predicate.skip_binder().trait_ref;
74+
if check_must_use(cx, trait_ref.def_id, s.span, "implementer of ", "") {
75+
must_use = true;
76+
break;
77+
}
78+
}
79+
}
80+
must_use
81+
}
82+
ty::Dynamic(binder, _) => {
83+
let mut must_use = false;
84+
for predicate in binder.skip_binder().iter() {
85+
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
86+
if check_must_use(cx, trait_ref.def_id, s.span, "", " trait object") {
87+
must_use = true;
88+
break;
89+
}
90+
}
91+
}
92+
must_use
7293
}
94+
_ => false,
7395
}
74-
_ => false,
7596
};
7697

7798
let mut fn_warned = false;
@@ -98,8 +119,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
98119
};
99120
if let Some(def) = maybe_def {
100121
let def_id = def.def_id();
101-
fn_warned = check_must_use(cx, def_id, s.span, "return value of ");
102-
} else if type_permits_no_use {
122+
fn_warned = check_must_use(cx, def_id, s.span, "return value of ", "");
123+
} else if type_permits_lack_of_use {
103124
// We don't warn about unused unit or uninhabited types.
104125
// (See https://github.com/rust-lang/rust/issues/43806 for details.)
105126
return;
@@ -148,15 +169,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
148169
op_warned = true;
149170
}
150171

151-
if !(type_permits_no_use || fn_warned || op_warned) {
172+
if !(type_permits_lack_of_use || fn_warned || op_warned) {
152173
cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
153174
}
154175

155-
fn check_must_use(cx: &LateContext, def_id: DefId, sp: Span, describe_path: &str) -> bool {
176+
fn check_must_use(
177+
cx: &LateContext,
178+
def_id: DefId,
179+
sp: Span,
180+
descr_pre_path: &str,
181+
descr_post_path: &str,
182+
) -> bool {
156183
for attr in cx.tcx.get_attrs(def_id).iter() {
157184
if attr.check_name("must_use") {
158-
let msg = format!("unused {}`{}` that must be used",
159-
describe_path, cx.tcx.item_path_str(def_id));
185+
let msg = format!("unused {}`{}`{} that must be used",
186+
descr_pre_path, cx.tcx.item_path_str(def_id), descr_post_path);
160187
let mut err = cx.struct_span_lint(UNUSED_MUST_USE, sp, &msg);
161188
// check for #[must_use = "..."]
162189
if let Some(note) = attr.value_str() {

‎src/test/ui/lint/must_use-trait.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![deny(unused_must_use)]
2+
3+
#[must_use]
4+
trait Critical {}
5+
6+
trait NotSoCritical {}
7+
8+
trait DecidedlyUnimportant {}
9+
10+
struct Anon;
11+
12+
impl Critical for Anon {}
13+
impl NotSoCritical for Anon {}
14+
impl DecidedlyUnimportant for Anon {}
15+
16+
fn get_critical() -> impl NotSoCritical + Critical + DecidedlyUnimportant {
17+
Anon {}
18+
}
19+
20+
fn main() {
21+
get_critical(); //~ ERROR unused implementer of `Critical` that must be used
22+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: unused implementer of `Critical` that must be used
2+
--> $DIR/must_use-trait.rs:21:5
3+
|
4+
LL | get_critical(); //~ ERROR unused implementer of `Critical` that must be used
5+
| ^^^^^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/must_use-trait.rs:1:9
9+
|
10+
LL | #![deny(unused_must_use)]
11+
| ^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)
Please sign in to comment.