1 use crate::{LateContext, LateLintPass, LintContext};
2 use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan};
5 use rustc_span::Symbol;
8 /// The `let_underscore_drop` lint checks for statements which don't bind
9 /// an expression which has a non-trivial Drop implementation to anything,
10 /// causing the expression to be dropped immediately instead of at end of
15 /// struct SomeStruct;
16 /// impl Drop for SomeStruct {
17 /// fn drop(&mut self) {
18 /// println!("Dropping SomeStruct");
23 /// #[warn(let_underscore_drop)]
24 /// // SomeStuct is dropped immediately instead of at end of scope,
25 /// // so "Dropping SomeStruct" is printed before "end of main".
26 /// // The order of prints would be reversed if SomeStruct was bound to
27 /// // a name (such as "_foo").
28 /// let _ = SomeStruct;
29 /// println!("end of main");
37 /// Statements which assign an expression to an underscore causes the
38 /// expression to immediately drop instead of extending the expression's
39 /// lifetime to the end of the scope. This is usually unintended,
40 /// especially for types like `MutexGuard`, which are typically used to
41 /// lock a mutex for the duration of an entire scope.
43 /// If you want to extend the expression's lifetime to the end of the scope,
44 /// assign an underscore-prefixed name (such as `_foo`) to the expression.
45 /// If you do actually want to drop the expression immediately, then
46 /// calling `std::mem::drop` on the expression is clearer and helps convey
48 pub LET_UNDERSCORE_DROP,
50 "non-binding let on a type that implements `Drop`"
54 /// The `let_underscore_lock` lint checks for statements which don't bind
55 /// a mutex to anything, causing the lock to be released immediately instead
56 /// of at end of scope, which is typically incorrect.
59 /// ```rust,compile_fail
60 /// use std::sync::{Arc, Mutex};
62 /// let data = Arc::new(Mutex::new(0));
64 /// thread::spawn(move || {
65 /// // The lock is immediately released instead of at the end of the
66 /// // scope, which is probably not intended.
67 /// let _ = data.lock().unwrap();
68 /// println!("doing some work");
69 /// let mut lock = data.lock().unwrap();
78 /// Statements which assign an expression to an underscore causes the
79 /// expression to immediately drop instead of extending the expression's
80 /// lifetime to the end of the scope. This is usually unintended,
81 /// especially for types like `MutexGuard`, which are typically used to
82 /// lock a mutex for the duration of an entire scope.
84 /// If you want to extend the expression's lifetime to the end of the scope,
85 /// assign an underscore-prefixed name (such as `_foo`) to the expression.
86 /// If you do actually want to drop the expression immediately, then
87 /// calling `std::mem::drop` on the expression is clearer and helps convey
89 pub LET_UNDERSCORE_LOCK,
91 "non-binding let on a synchronization lock"
94 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
96 const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
97 rustc_span::sym::MutexGuard,
98 rustc_span::sym::RwLockReadGuard,
99 rustc_span::sym::RwLockWriteGuard,
102 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
103 fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
104 if !matches!(local.pat.kind, hir::PatKind::Wild) {
107 if let Some(init) = local.init {
108 let init_ty = cx.typeck_results().expr_ty(init);
109 // If the type has a trivial Drop implementation, then it doesn't
110 // matter that we drop the value immediately.
111 if !init_ty.needs_drop(cx.tcx, cx.param_env) {
114 let is_sync_lock = match init_ty.kind() {
115 ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
117 .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
122 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
123 span.push_span_label(
125 "this lock is not assigned to a binding and is immediately dropped".to_string(),
127 span.push_span_label(
129 "this binding will immediately drop the value assigned to it".to_string(),
134 "non-binding let on a synchronization lock",
135 |lint| build_lint(lint, local, init.span),
141 "non-binding let on a type that implements `Drop`",
142 |lint| build_lint(lint, local, init.span),
149 fn build_lint<'a, 'b>(
150 lint: &'a mut DiagnosticBuilder<'b, ()>,
151 local: &hir::Local<'_>,
152 init_span: rustc_span::Span,
153 ) -> &'a mut DiagnosticBuilder<'b, ()> {
154 lint.span_suggestion_verbose(
156 "consider binding to an unused variable to avoid immediately dropping the value",
158 Applicability::MachineApplicable,
160 .multipart_suggestion(
161 "consider immediately dropping the value",
163 (local.span.until(init_span), "drop(".to_string()),
164 (init_span.shrink_to_hi(), ")".to_string()),
166 Applicability::MachineApplicable,