From 648aad3dc60f692469261a743059a67160d3b961 Mon Sep 17 00:00:00 2001 From: Vitaly _Vi Shukela <vi0oss@gmail.com> Date: Wed, 4 Nov 2020 02:08:55 +0300 Subject: [PATCH] Add #[must_bind] attribute and lint --- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_lint/src/unused.rs | 227 +++++++++++++++----- compiler/rustc_span/src/symbol.rs | 1 + 3 files changed, 179 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 57ae534590ddf..dfa0bade006fb 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -205,6 +205,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)), ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)), ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)), + ungated!(must_bind, AssumedUsed, template!(Word, NameValueStr: "reason")), ungated!(must_use, AssumedUsed, template!(Word, NameValueStr: "reason")), // FIXME(#14407) ungated!( diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 4bbc180b226a5..66177098881f8 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -88,12 +88,64 @@ declare_lint! { "unused result of an expression in a statement" } -declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); +declare_lint! { + /// The `unused_must_bind` lint detects attempts to "bind" to `_` value of a type marked with + /// `#[must_bind]`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unused_must_bind)] + /// + /// #[must_use] + /// #[must_bind] + /// struct SemaphoreGuard; + /// + /// fn acquire() -> SemaphoreGuard { + /// SemaphoreGuard + /// } + /// + /// fn main() { + /// let _ = acquire(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The `#[must_bind]` attribute is an indicator that it is a mistake to + /// let the value be dropped imediately after acquiring it. See [the reference] for more details. + /// + /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_bind-attribute + pub UNUSED_MUST_BIND, + Warn, + "Bogus `_` binding of a type flagged as `#[must_bind]`. Use something like `_guard`.", + report_in_external_macro +} + +declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS, UNUSED_MUST_BIND]); impl<'tcx> LateLintPass<'tcx> for UnusedResults { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let expr = match s.kind { - hir::StmtKind::Semi(ref expr) => &**expr, + #[derive(Clone, Copy)] + enum WhatAttrToCheck { + MustUse, + MustBind, + } + let (expr, attr_to_check) = match s.kind { + hir::StmtKind::Semi(ref expr) => (&**expr, WhatAttrToCheck::MustUse), + hir::StmtKind::Local(ref local) => { + match local.pat.kind { + hir::PatKind::Wild => (), + _ => return, + } + if let Some(ref init) = local.init { + (&**init, WhatAttrToCheck::MustBind) + } else { + return; + } + } _ => return, }; @@ -102,7 +154,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } let ty = cx.typeck_results().expr_ty(&expr); - let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, s.span, "", "", 1); + let type_permits_lack_of_use = + check_must_use_ty(cx, ty, &expr, s.span, "", "", 1, attr_to_check); let mut fn_warned = false; let mut op_warned = false; @@ -124,7 +177,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { _ => None, }; if let Some(def_id) = maybe_def_id { - fn_warned = check_must_use_def(cx, def_id, s.span, "return value of ", ""); + fn_warned = + check_must_use_def(cx, def_id, s.span, "return value of ", "", attr_to_check); } else if type_permits_lack_of_use { // We don't warn about unused unit or uninhabited types. // (See https://github.com/rust-lang/rust/issues/43806 for details.) @@ -158,15 +212,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { _ => None, }; - if let Some(must_use_op) = must_use_op { - cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| { - lint.build(&format!("unused {} that must be used", must_use_op)).emit() - }); - op_warned = true; - } + match attr_to_check { + WhatAttrToCheck::MustUse => { + if let Some(must_use_op) = must_use_op { + cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| { + lint.build(&format!("unused {} that must be used", must_use_op)).emit() + }); + op_warned = true; + } - if !(type_permits_lack_of_use || fn_warned || op_warned) { - cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| lint.build("unused result").emit()); + if !(type_permits_lack_of_use || fn_warned || op_warned) { + cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| { + lint.build("unused result").emit() + }); + } + } + _ => (), } // Returns whether an error has been emitted (and thus another does not need to be later). @@ -178,6 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { descr_pre: &str, descr_post: &str, plural_len: usize, + attr_to_check: WhatAttrToCheck, ) -> bool { if ty.is_unit() || cx.tcx.is_ty_uninhabited_from( @@ -195,9 +257,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Adt(..) if ty.is_box() => { let boxed_ty = ty.boxed_ty(); let descr_pre = &format!("{}boxed ", descr_pre); - check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural_len) + check_must_use_ty( + cx, + boxed_ty, + expr, + span, + descr_pre, + descr_post, + plural_len, + attr_to_check, + ) + } + ty::Adt(def, _) => { + check_must_use_def(cx, def.did, span, descr_pre, descr_post, attr_to_check) } - ty::Adt(def, _) => check_must_use_def(cx, def.did, span, descr_pre, descr_post), ty::Opaque(def, _) => { let mut has_emitted = false; for &(predicate, _) in cx.tcx.explicit_item_bounds(def) { @@ -208,7 +281,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let def_id = poly_trait_predicate.trait_ref.def_id; let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix,); - if check_must_use_def(cx, def_id, span, descr_pre, descr_post) { + if check_must_use_def( + cx, + def_id, + span, + descr_pre, + descr_post, + attr_to_check, + ) { has_emitted = true; break; } @@ -223,7 +303,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let def_id = trait_ref.def_id; let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post,); - if check_must_use_def(cx, def_id, span, descr_pre, descr_post) { + if check_must_use_def( + cx, + def_id, + span, + descr_pre, + descr_post, + attr_to_check, + ) { has_emitted = true; break; } @@ -242,8 +329,16 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() { let descr_post = &format!(" in tuple element {}", i); let span = *spans.get(i).unwrap_or(&span); - if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural_len) - { + if check_must_use_ty( + cx, + ty, + expr, + span, + descr_pre, + descr_post, + plural_len, + attr_to_check, + ) { has_emitted = true; } } @@ -255,31 +350,46 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { // If the array is definitely non-empty, we can do `#[must_use]` checking. Some(n) => { let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,); - check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, n as usize + 1) + check_must_use_ty( + cx, + ty, + expr, + span, + descr_pre, + descr_post, + n as usize + 1, + attr_to_check, + ) } }, - ty::Closure(..) => { - cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - let mut err = lint.build(&format!( - "unused {}closure{}{} that must be used", - descr_pre, plural_suffix, descr_post, - )); - err.note("closures are lazy and do nothing unless called"); - err.emit(); - }); - true - } - ty::Generator(..) => { - cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - let mut err = lint.build(&format!( - "unused {}generator{}{} that must be used", - descr_pre, plural_suffix, descr_post, - )); - err.note("generators are lazy and do nothing unless resumed"); - err.emit(); - }); - true - } + ty::Closure(..) => match attr_to_check { + WhatAttrToCheck::MustUse => { + cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { + let mut err = lint.build(&format!( + "unused {}closure{}{} that must be used", + descr_pre, plural_suffix, descr_post, + )); + err.note("closures are lazy and do nothing unless called"); + err.emit(); + }); + true + } + _ => false, + }, + ty::Generator(..) => match attr_to_check { + WhatAttrToCheck::MustUse => { + cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { + let mut err = lint.build(&format!( + "unused {}generator{}{} that must be used", + descr_pre, plural_suffix, descr_post, + )); + err.note("generators are lazy and do nothing unless resumed"); + err.emit(); + }); + true + } + _ => false, + }, _ => false, } } @@ -295,16 +405,33 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { span: Span, descr_pre_path: &str, descr_post_path: &str, + attr_to_check: WhatAttrToCheck, ) -> bool { + let attr_name = match attr_to_check { + WhatAttrToCheck::MustUse => sym::must_use, + WhatAttrToCheck::MustBind => sym::must_bind, + }; for attr in cx.tcx.get_attrs(def_id).iter() { - if cx.sess().check_name(attr, sym::must_use) { - cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - let msg = format!( - "unused {}`{}`{} that must be used", - descr_pre_path, - cx.tcx.def_path_str(def_id), - descr_post_path - ); + if cx.sess().check_name(attr, attr_name) { + let lint_type = match attr_to_check { + WhatAttrToCheck::MustUse => UNUSED_MUST_USE, + WhatAttrToCheck::MustBind => UNUSED_MUST_BIND, + }; + cx.struct_span_lint(lint_type, span, |lint| { + let msg = match attr_to_check { + WhatAttrToCheck::MustUse => format!( + "unused {}`{}`{} that must be used", + descr_pre_path, + cx.tcx.def_path_str(def_id), + descr_post_path + ), + WhatAttrToCheck::MustBind => format!( + "Bogus `let _ =` binding of {}`{}`{}. Use something like `let _guard =`.", + descr_pre_path, + cx.tcx.def_path_str(def_id), + descr_post_path + ), + }; let mut err = lint.build(&msg); // check for #[must_use = "..."] if let Some(note) = attr.value_str() { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1a6c45b6c80d2..1ad71e29da231 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -702,6 +702,7 @@ symbols! { mul, mul_assign, mul_with_overflow, + must_bind, must_use, mut_ptr, mut_slice_ptr,