rust/compiler/rustc_lint/src/let_underscore.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

135 lines
5.5 KiB
Rust
Raw Normal View History

use crate::{LateContext, LateLintPass, LintContext};
use rustc_hir as hir;
use rustc_middle::ty::{self, subst::GenericArgKind};
use rustc_span::Symbol;
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! {
/// The `let_underscore_lock` lint checks for statements which don't bind
/// a mutex to anything, causing the lock to be released immediately instead
/// of at end of scope, which is typically incorrect.
///
/// ### Example
/// ```rust
/// use std::sync::{Arc, Mutex};
/// use std::thread;
/// let data = Arc::new(Mutex::new(0));
///
/// thread::spawn(move || {
/// // The lock is immediately released instead of at the end of the
/// // scope, which is probably not intended.
/// let _ = data.lock().unwrap();
/// println!("doing some work");
/// let mut lock = data.lock().unwrap();
/// *lock += 1;
/// });
/// ```
/// ### 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_LOCK,
Warn,
"non-binding let on a synchronization lock"
}
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
const SYNC_GUARD_PATHS: [&[&str]; 5] = [
&["std", "sync", "mutex", "MutexGuard"],
&["std", "sync", "rwlock", "RwLockReadGuard"],
&["std", "sync", "rwlock", "RwLockWriteGuard"],
&["parking_lot", "raw_mutex", "RawMutex"],
&["parking_lot", "raw_rwlock", "RawRwLock"],
];
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);
let is_sync_lock = init_ty.walk().any(|inner| match inner.unpack() {
GenericArgKind::Type(inner_ty) => {
SYNC_GUARD_PATHS.iter().any(|guard_path| match inner_ty.kind() {
ty::Adt(adt, _) => {
let ty_path = cx.get_def_path(adt.did());
guard_path.iter().map(|x| Symbol::intern(x)).eq(ty_path.iter().copied())
}
_ => false,
})
}
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
});
if is_sync_lock {
cx.struct_span_lint(LET_UNDERSCORE_LOCK, local.span, |lint| {
lint.build("non-binding let on a synchronization lock")
.help("consider binding to an unused variable")
.help("consider explicitly droping with `std::mem::drop`")
.emit();
})
} else 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();
})
}
}
}
}