diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs new file mode 100644 index 00000000000..44242173c00 --- /dev/null +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -0,0 +1,66 @@ +use crate::{LateContext, LateLintPass, LintContext}; +use rustc_hir as hir; + +declare_lint! { + /// The `let_underscore_drop` lint checks for statements which don't bind + /// an expression which has a non-trivial Drop implementation to anything, + /// causing the expression to be dropped immediately instead of at end of + /// scope. + /// + /// ### Example + /// ```rust + /// struct SomeStruct; + /// impl Drop for SomeStruct { + /// fn drop(&mut self) { + /// println!("Dropping SomeStruct"); + /// } + /// } + /// + /// fn main() { + /// // SomeStuct is dropped immediately instead of at end of scope, + /// // so "Dropping SomeStruct" is printed before "end of main". + /// // The order of prints would be reversed if SomeStruct was bound to + /// // a name (such as "_foo"). + /// let _ = SomeStruct; + /// println!("end of main"); + /// } + /// ``` + /// ### Explanation + /// + /// Statements which assign an expression to an underscore causes the + /// expression to immediately drop instead of extending the expression's + /// lifetime to the end of the scope. This is usually unintended, + /// especially for types like `MutexGuard`, which are typically used to + /// lock a mutex for the duration of an entire scope. + /// + /// If you want to extend the expression's lifetime to the end of the scope, + /// assign an underscore-prefixed name (such as `_foo`) to the expression. + /// If you do actually want to drop the expression immediately, then + /// calling `std::mem::drop` on the expression is clearer and helps convey + /// intent. + pub LET_UNDERSCORE_DROP, + Warn, + "non-binding let on a type that implements `Drop`" +} + +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP]); + +impl<'tcx> LateLintPass<'tcx> for LetUnderscore { + fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { + if !matches!(local.pat.kind, hir::PatKind::Wild) { + return; + } + if let Some(init) = local.init { + let init_ty = cx.typeck_results().expr_ty(init); + let needs_drop = init_ty.needs_drop(cx.tcx, cx.param_env); + if needs_drop { + cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| { + lint.build("non-binding let on a type that implements `Drop`") + .help("consider binding to an unused variable") + .help("consider explicitly droping with `std::mem::drop`") + .emit(); + }) + } + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 0a0f292fe7a..55396b36dbc 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -54,6 +54,7 @@ pub mod hidden_unicode_codepoints; mod internal; mod late; +mod let_underscore; mod levels; mod methods; mod non_ascii_idents; @@ -85,6 +86,7 @@ use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use hidden_unicode_codepoints::*; use internal::*; +use let_underscore::*; use methods::*; use non_ascii_idents::*; use non_fmt_panic::NonPanicFmt; @@ -199,6 +201,7 @@ macro_rules! late_lint_mod_passes { VariantSizeDifferences: VariantSizeDifferences, BoxPointers: BoxPointers, PathStatements: PathStatements, + LetUnderscore: LetUnderscore, // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, NonUpperCaseGlobals: NonUpperCaseGlobals, @@ -314,6 +317,8 @@ macro_rules! register_passes { REDUNDANT_SEMICOLONS ); + add_lint_group!("let_underscore", LET_UNDERSCORE_DROP); + add_lint_group!( "rust_2018_idioms", BARE_TRAIT_OBJECTS, diff --git a/src/test/ui/let_underscore_drop.rs b/src/test/ui/let_underscore_drop.rs new file mode 100644 index 00000000000..c1c5207d0fe --- /dev/null +++ b/src/test/ui/let_underscore_drop.rs @@ -0,0 +1,13 @@ +// run-pass + +struct NontrivialDrop; + +impl Drop for NontrivialDrop { + fn drop(&mut self) { + println!("Dropping!"); + } +} + +fn main() { + let _ = NontrivialDrop; //~WARNING non-binding let on a type that implements `Drop` +} diff --git a/src/test/ui/let_underscore_drop.stderr b/src/test/ui/let_underscore_drop.stderr new file mode 100644 index 00000000000..40ed1abd8dc --- /dev/null +++ b/src/test/ui/let_underscore_drop.stderr @@ -0,0 +1,12 @@ +warning: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:12:5 + | +LL | let _ = NontrivialDrop; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(let_underscore_drop)]` on by default + = help: consider binding to an unused variable + = help: consider explicitly droping with `std::mem::drop` + +warning: 1 warning emitted +