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
16 /// struct SomeStruct;
17 /// impl Drop for SomeStruct {
18 /// fn drop(&mut self) {
19 /// println!("Dropping SomeStruct");
24 /// #[warn(let_underscore_drop)]
25 /// // SomeStuct is dropped immediately instead of at end of scope,
26 /// // so "Dropping SomeStruct" is printed before "end of main".
27 /// // The order of prints would be reversed if SomeStruct was bound to
28 /// // a name (such as "_foo").
29 /// let _ = SomeStruct;
30 /// println!("end of main");
38 /// Statements which assign an expression to an underscore causes the
39 /// expression to immediately drop instead of extending the expression's
40 /// lifetime to the end of the scope. This is usually unintended,
41 /// especially for types like `MutexGuard`, which are typically used to
42 /// lock a mutex for the duration of an entire scope.
44 /// If you want to extend the expression's lifetime to the end of the scope,
45 /// assign an underscore-prefixed name (such as `_foo`) to the expression.
46 /// If you do actually want to drop the expression immediately, then
47 /// calling `std::mem::drop` on the expression is clearer and helps convey
49 pub LET_UNDERSCORE_DROP,
51 "non-binding let on a type that implements `Drop`"
55 /// The `let_underscore_lock` lint checks for statements which don't bind
56 /// a mutex to anything, causing the lock to be released immediately instead
57 /// of at end of scope, which is typically incorrect.
60 /// ```rust,compile_fail
61 /// use std::sync::{Arc, Mutex};
63 /// let data = Arc::new(Mutex::new(0));
65 /// thread::spawn(move || {
66 /// // The lock is immediately released instead of at the end of the
67 /// // scope, which is probably not intended.
68 /// let _ = data.lock().unwrap();
69 /// println!("doing some work");
70 /// let mut lock = data.lock().unwrap();
79 /// Statements which assign an expression to an underscore causes the
80 /// expression to immediately drop instead of extending the expression's
81 /// lifetime to the end of the scope. This is usually unintended,
82 /// especially for types like `MutexGuard`, which are typically used to
83 /// lock a mutex for the duration of an entire scope.
85 /// If you want to extend the expression's lifetime to the end of the scope,
86 /// assign an underscore-prefixed name (such as `_foo`) to the expression.
87 /// If you do actually want to drop the expression immediately, then
88 /// calling `std::mem::drop` on the expression is clearer and helps convey
90 pub LET_UNDERSCORE_LOCK,
92 "non-binding let on a synchronization lock"
95 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
97 const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
98 rustc_span::sym::MutexGuard,
99 rustc_span::sym::RwLockReadGuard,
100 rustc_span::sym::RwLockWriteGuard,
103 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
104 fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
105 if !matches!(local.pat.kind, hir::PatKind::Wild) {
108 if let Some(init) = local.init {
109 let init_ty = cx.typeck_results().expr_ty(init);
110 // If the type has a trivial Drop implementation, then it doesn't
111 // matter that we drop the value immediately.
112 if !init_ty.needs_drop(cx.tcx, cx.param_env) {
115 let is_sync_lock = match init_ty.kind() {
116 ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
118 .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
123 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
124 span.push_span_label(
126 "this lock is not assigned to a binding and is immediately dropped".to_string(),
128 span.push_span_label(
130 "this binding will immediately drop the value assigned to it".to_string(),
135 "non-binding let on a synchronization lock",
136 |lint| build_lint(lint, local, init.span),
142 "non-binding let on a type that implements `Drop`",
143 |lint| build_lint(lint, local, init.span),
150 fn build_lint<'a, 'b>(
151 lint: &'a mut DiagnosticBuilder<'b, ()>,
152 local: &hir::Local<'_>,
153 init_span: rustc_span::Span,
154 ) -> &'a mut DiagnosticBuilder<'b, ()> {
155 lint.span_suggestion_verbose(
157 "consider binding to an unused variable to avoid immediately dropping the value",
159 Applicability::MachineApplicable,
161 .multipart_suggestion(
162 "consider immediately dropping the value",
164 (local.span.until(init_span), "drop(".to_string()),
165 (init_span.shrink_to_hi(), ")".to_string()),
167 Applicability::MachineApplicable,