提交 3991bfbb 编写于 作者: B bors

Auto merge of #55663 - varkor:must_use-traits, r=estebank

Allow #[must_use] on traits

Addresses https://github.com/rust-lang/rust/issues/55506, but we'll probably want to add it to some library traits like `Iterator` before the issue is considered fixed. Fixes https://github.com/rust-lang/rust/issues/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).
...@@ -60,18 +60,39 @@ fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { ...@@ -60,18 +60,39 @@ fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
} }
let t = cx.tables.expr_ty(&expr); let t = cx.tables.expr_ty(&expr);
// FIXME(varkor): replace with `t.is_unit() || t.conservative_is_uninhabited()`. let type_permits_lack_of_use = if t.is_unit()
let type_permits_no_use = match t.sty { || cx.tcx.is_ty_uninhabited_from(cx.tcx.hir.get_module_parent(expr.id), t) {
ty::Tuple(ref tys) if tys.is_empty() => true, true
ty::Never => true, } else {
ty::Adt(def, _) => { match t.sty {
if def.variants.is_empty() { ty::Adt(def, _) => check_must_use(cx, def.did, s.span, "", ""),
true ty::Opaque(def, _) => {
} else { let mut must_use = false;
check_must_use(cx, def.did, s.span, "") for (predicate, _) in &cx.tcx.predicates_of(def).predicates {
if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
let trait_ref = poly_trait_predicate.skip_binder().trait_ref;
if check_must_use(cx, trait_ref.def_id, s.span, "implementer of ", "") {
must_use = true;
break;
}
}
}
must_use
}
ty::Dynamic(binder, _) => {
let mut must_use = false;
for predicate in binder.skip_binder().iter() {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
if check_must_use(cx, trait_ref.def_id, s.span, "", " trait object") {
must_use = true;
break;
}
}
}
must_use
} }
_ => false,
} }
_ => false,
}; };
let mut fn_warned = false; let mut fn_warned = false;
...@@ -98,8 +119,8 @@ fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { ...@@ -98,8 +119,8 @@ fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
}; };
if let Some(def) = maybe_def { if let Some(def) = maybe_def {
let def_id = def.def_id(); let def_id = def.def_id();
fn_warned = check_must_use(cx, def_id, s.span, "return value of "); fn_warned = check_must_use(cx, def_id, s.span, "return value of ", "");
} else if type_permits_no_use { } else if type_permits_lack_of_use {
// We don't warn about unused unit or uninhabited types. // We don't warn about unused unit or uninhabited types.
// (See https://github.com/rust-lang/rust/issues/43806 for details.) // (See https://github.com/rust-lang/rust/issues/43806 for details.)
return; return;
...@@ -148,15 +169,21 @@ fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) { ...@@ -148,15 +169,21 @@ fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
op_warned = true; op_warned = true;
} }
if !(type_permits_no_use || fn_warned || op_warned) { if !(type_permits_lack_of_use || fn_warned || op_warned) {
cx.span_lint(UNUSED_RESULTS, s.span, "unused result"); cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
} }
fn check_must_use(cx: &LateContext, def_id: DefId, sp: Span, describe_path: &str) -> bool { fn check_must_use(
cx: &LateContext,
def_id: DefId,
sp: Span,
descr_pre_path: &str,
descr_post_path: &str,
) -> bool {
for attr in cx.tcx.get_attrs(def_id).iter() { for attr in cx.tcx.get_attrs(def_id).iter() {
if attr.check_name("must_use") { if attr.check_name("must_use") {
let msg = format!("unused {}`{}` that must be used", let msg = format!("unused {}`{}`{} that must be used",
describe_path, cx.tcx.item_path_str(def_id)); descr_pre_path, cx.tcx.item_path_str(def_id), descr_post_path);
let mut err = cx.struct_span_lint(UNUSED_MUST_USE, sp, &msg); let mut err = cx.struct_span_lint(UNUSED_MUST_USE, sp, &msg);
// check for #[must_use = "..."] // check for #[must_use = "..."]
if let Some(note) = attr.value_str() { if let Some(note) = attr.value_str() {
......
#![deny(unused_must_use)]
#[must_use]
trait Critical {}
trait NotSoCritical {}
trait DecidedlyUnimportant {}
struct Anon;
impl Critical for Anon {}
impl NotSoCritical for Anon {}
impl DecidedlyUnimportant for Anon {}
fn get_critical() -> impl NotSoCritical + Critical + DecidedlyUnimportant {
Anon {}
}
fn main() {
get_critical(); //~ ERROR unused implementer of `Critical` that must be used
}
error: unused implementer of `Critical` that must be used
--> $DIR/must_use-trait.rs:21:5
|
LL | get_critical(); //~ ERROR unused implementer of `Critical` that must be used
| ^^^^^^^^^^^^^^^
|
note: lint level defined here
--> $DIR/must_use-trait.rs:1:9
|
LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册