Add let_underscore_drop lint.

This lint checks for statements similar to `let _ = foo`, where `foo` is
a type that implements `Drop`. These types of let statements cause the
expression in them to be dropped immediately, instead of at the end of
the scope. Such behavior can be surprizing, especially if you are
relying on the value to be dropped at the end of the scope. Instead, the
binding should be an underscore prefixed name (like `_unused`) or the
value should explicitly be passed to `std::mem::drop()` if the value
really should be dropped immediately.
This commit is contained in:
Aaron Kofsky 2022-05-29 14:35:00 -04:00
parent e57884b6e9
commit 821b32bd40
4 changed files with 96 additions and 0 deletions

View File

@ -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();
})
}
}
}
}

View File

@ -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,

View File

@ -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`
}

View File

@ -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