2022-10-07 06:14:56 -04:00
|
|
|
use rustc_errors::MultiSpan;
|
2022-05-29 14:35:00 -04:00
|
|
|
use rustc_hir as hir;
|
2022-08-04 17:31:08 -04:00
|
|
|
use rustc_middle::ty;
|
2024-04-29 13:42:13 +10:00
|
|
|
use rustc_session::{declare_lint, declare_lint_pass};
|
2024-01-07 20:37:51 +01:00
|
|
|
use rustc_span::{sym, Symbol};
|
2022-05-29 14:35:00 -04:00
|
|
|
|
2022-10-07 06:14:56 -04:00
|
|
|
use crate::lints::{NonBindingLet, NonBindingLetSub};
|
|
|
|
use crate::{LateContext, LateLintPass, LintContext};
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2022-05-29 14:35:00 -04:00
|
|
|
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
|
2022-11-14 22:55:50 +09:00
|
|
|
///
|
2022-10-25 00:59:32 +02:00
|
|
|
/// ```rust
|
2022-05-29 14:35:00 -04:00
|
|
|
/// struct SomeStruct;
|
|
|
|
/// impl Drop for SomeStruct {
|
|
|
|
/// fn drop(&mut self) {
|
|
|
|
/// println!("Dropping SomeStruct");
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// fn main() {
|
2022-06-05 12:47:19 -04:00
|
|
|
/// #[warn(let_underscore_drop)]
|
2023-04-10 22:02:52 +02:00
|
|
|
/// // SomeStruct is dropped immediately instead of at end of scope,
|
2022-05-29 14:35:00 -04:00
|
|
|
/// // 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");
|
|
|
|
/// }
|
|
|
|
/// ```
|
2022-06-05 12:47:19 -04:00
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
2022-05-29 14:35:00 -04:00
|
|
|
/// ### 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,
|
2022-06-16 21:07:00 -04:00
|
|
|
Allow,
|
2022-05-29 14:35:00 -04:00
|
|
|
"non-binding let on a type that implements `Drop`"
|
|
|
|
}
|
|
|
|
|
2022-05-31 14:05:04 -04:00
|
|
|
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
|
2022-10-24 18:00:41 +02:00
|
|
|
/// ```rust,compile_fail
|
2022-05-31 14:05:04 -04:00
|
|
|
/// 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;
|
|
|
|
/// });
|
|
|
|
/// ```
|
2022-06-05 12:47:19 -04:00
|
|
|
///
|
|
|
|
/// {{produces}}
|
|
|
|
///
|
2022-05-31 14:05:04 -04:00
|
|
|
/// ### 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,
|
2022-06-04 13:50:12 -04:00
|
|
|
Deny,
|
2022-05-31 14:05:04 -04:00
|
|
|
"non-binding let on a synchronization lock"
|
|
|
|
}
|
|
|
|
|
2022-06-05 00:05:50 -04:00
|
|
|
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
|
2022-05-31 14:05:04 -04:00
|
|
|
|
2022-06-04 22:27:32 -04:00
|
|
|
const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
|
|
|
|
rustc_span::sym::MutexGuard,
|
|
|
|
rustc_span::sym::RwLockReadGuard,
|
|
|
|
rustc_span::sym::RwLockWriteGuard,
|
2022-05-31 14:05:04 -04:00
|
|
|
];
|
2022-05-29 14:35:00 -04:00
|
|
|
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
2024-03-20 17:50:31 +01:00
|
|
|
fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) {
|
2024-01-08 01:15:02 +08:00
|
|
|
if matches!(local.source, rustc_hir::LocalSource::AsyncFn) {
|
|
|
|
return;
|
|
|
|
}
|
2024-01-07 20:37:51 +01:00
|
|
|
|
|
|
|
let mut top_level = true;
|
|
|
|
|
2024-05-07 14:12:37 +10:00
|
|
|
// We recursively walk through all patterns, so that we can catch cases where the lock is
|
|
|
|
// nested in a pattern. For the basic `let_underscore_drop` lint, we only look at the top
|
|
|
|
// level, since there are many legitimate reasons to bind a sub-pattern to an `_`, if we're
|
|
|
|
// only interested in the rest. But with locks, we prefer having the chance of "false
|
|
|
|
// positives" over missing cases, since the effects can be quite catastrophic.
|
2024-01-07 20:37:51 +01:00
|
|
|
local.pat.walk_always(|pat| {
|
|
|
|
let is_top_level = top_level;
|
|
|
|
top_level = false;
|
|
|
|
|
|
|
|
if !matches!(pat.kind, hir::PatKind::Wild) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ty = cx.typeck_results().pat_ty(pat);
|
|
|
|
|
2022-06-04 20:19:19 -04:00
|
|
|
// If the type has a trivial Drop implementation, then it doesn't
|
|
|
|
// matter that we drop the value immediately.
|
2024-01-07 20:37:51 +01:00
|
|
|
if !ty.needs_drop(cx.tcx, cx.param_env) {
|
2022-06-04 20:19:19 -04:00
|
|
|
return;
|
|
|
|
}
|
2024-01-07 20:37:51 +01:00
|
|
|
// Lint for patterns like `mutex.lock()`, which returns `Result<MutexGuard, _>` as well.
|
|
|
|
let potential_lock_type = match ty.kind() {
|
|
|
|
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
|
|
|
|
args.type_at(0)
|
|
|
|
}
|
|
|
|
_ => ty,
|
|
|
|
};
|
|
|
|
let is_sync_lock = match potential_lock_type.kind() {
|
2022-06-04 22:27:32 -04:00
|
|
|
ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
|
|
|
|
.iter()
|
|
|
|
.any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
|
|
|
|
_ => false,
|
|
|
|
};
|
2022-06-03 21:33:13 -04:00
|
|
|
|
2024-01-07 20:37:51 +01:00
|
|
|
let can_use_init = is_top_level.then_some(local.init).flatten();
|
|
|
|
|
2022-10-07 06:14:56 -04:00
|
|
|
let sub = NonBindingLetSub {
|
2024-01-07 20:37:51 +01:00
|
|
|
suggestion: pat.span,
|
|
|
|
// We can't suggest `drop()` when we're on the top level.
|
|
|
|
drop_fn_start_end: can_use_init
|
|
|
|
.map(|init| (local.span.until(init.span), init.span.shrink_to_hi())),
|
2024-01-08 01:15:02 +08:00
|
|
|
is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)),
|
2022-10-07 06:14:56 -04:00
|
|
|
};
|
2022-05-31 14:05:04 -04:00
|
|
|
if is_sync_lock {
|
2024-07-30 00:33:18 +03:00
|
|
|
let span = MultiSpan::from_span(pat.span);
|
|
|
|
cx.emit_span_lint(
|
|
|
|
LET_UNDERSCORE_LOCK,
|
|
|
|
span,
|
|
|
|
NonBindingLet::SyncLock { sub, pat: pat.span },
|
2022-08-04 17:00:48 -04:00
|
|
|
);
|
2024-01-07 20:37:51 +01:00
|
|
|
// Only emit let_underscore_drop for top-level `_` patterns.
|
|
|
|
} else if can_use_init.is_some() {
|
2024-01-16 14:40:39 +11:00
|
|
|
cx.emit_span_lint(LET_UNDERSCORE_DROP, local.span, NonBindingLet::DropType { sub });
|
2022-05-29 14:35:00 -04:00
|
|
|
}
|
2024-01-07 20:37:51 +01:00
|
|
|
});
|
2022-06-04 19:52:12 -04:00
|
|
|
}
|
|
|
|
}
|