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:
parent
e57884b6e9
commit
821b32bd40
66
compiler/rustc_lint/src/let_underscore.rs
Normal file
66
compiler/rustc_lint/src/let_underscore.rs
Normal 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();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
13
src/test/ui/let_underscore_drop.rs
Normal file
13
src/test/ui/let_underscore_drop.rs
Normal 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`
|
||||
}
|
12
src/test/ui/let_underscore_drop.stderr
Normal file
12
src/test/ui/let_underscore_drop.stderr
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user