1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
4 lints::{NonBindingLet, NonBindingLetSub},
5 LateContext, LateLintPass, LintContext,
7 use rustc_errors::MultiSpan;
10 use rustc_span::Symbol;
13 /// The `let_underscore_drop` lint checks for statements which don't bind
14 /// an expression which has a non-trivial Drop implementation to anything,
15 /// causing the expression to be dropped immediately instead of at end of
21 /// struct SomeStruct;
22 /// impl Drop for SomeStruct {
23 /// fn drop(&mut self) {
24 /// println!("Dropping SomeStruct");
29 /// #[warn(let_underscore_drop)]
30 /// // SomeStuct is dropped immediately instead of at end of scope,
31 /// // so "Dropping SomeStruct" is printed before "end of main".
32 /// // The order of prints would be reversed if SomeStruct was bound to
33 /// // a name (such as "_foo").
34 /// let _ = SomeStruct;
35 /// println!("end of main");
43 /// Statements which assign an expression to an underscore causes the
44 /// expression to immediately drop instead of extending the expression's
45 /// lifetime to the end of the scope. This is usually unintended,
46 /// especially for types like `MutexGuard`, which are typically used to
47 /// lock a mutex for the duration of an entire scope.
49 /// If you want to extend the expression's lifetime to the end of the scope,
50 /// assign an underscore-prefixed name (such as `_foo`) to the expression.
51 /// If you do actually want to drop the expression immediately, then
52 /// calling `std::mem::drop` on the expression is clearer and helps convey
54 pub LET_UNDERSCORE_DROP,
56 "non-binding let on a type that implements `Drop`"
60 /// The `let_underscore_lock` lint checks for statements which don't bind
61 /// a mutex to anything, causing the lock to be released immediately instead
62 /// of at end of scope, which is typically incorrect.
65 /// ```rust,compile_fail
66 /// use std::sync::{Arc, Mutex};
68 /// let data = Arc::new(Mutex::new(0));
70 /// thread::spawn(move || {
71 /// // The lock is immediately released instead of at the end of the
72 /// // scope, which is probably not intended.
73 /// let _ = data.lock().unwrap();
74 /// println!("doing some work");
75 /// let mut lock = data.lock().unwrap();
84 /// Statements which assign an expression to an underscore causes the
85 /// expression to immediately drop instead of extending the expression's
86 /// lifetime to the end of the scope. This is usually unintended,
87 /// especially for types like `MutexGuard`, which are typically used to
88 /// lock a mutex for the duration of an entire scope.
90 /// If you want to extend the expression's lifetime to the end of the scope,
91 /// assign an underscore-prefixed name (such as `_foo`) to the expression.
92 /// If you do actually want to drop the expression immediately, then
93 /// calling `std::mem::drop` on the expression is clearer and helps convey
95 pub LET_UNDERSCORE_LOCK,
97 "non-binding let on a synchronization lock"
100 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
102 const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
103 rustc_span::sym::MutexGuard,
104 rustc_span::sym::RwLockReadGuard,
105 rustc_span::sym::RwLockWriteGuard,
108 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
109 fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
110 if !matches!(local.pat.kind, hir::PatKind::Wild) {
113 if let Some(init) = local.init {
114 let init_ty = cx.typeck_results().expr_ty(init);
115 // If the type has a trivial Drop implementation, then it doesn't
116 // matter that we drop the value immediately.
117 if !init_ty.needs_drop(cx.tcx, cx.param_env) {
120 let is_sync_lock = match init_ty.kind() {
121 ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
123 .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
127 let sub = NonBindingLetSub {
128 suggestion: local.pat.span,
129 multi_suggestion_start: local.span.until(init.span),
130 multi_suggestion_end: init.span.shrink_to_hi(),
133 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
134 span.push_span_label(
136 "this lock is not assigned to a binding and is immediately dropped".to_string(),
138 span.push_span_label(
140 "this binding will immediately drop the value assigned to it".to_string(),
142 cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
144 cx.emit_spanned_lint(
147 NonBindingLet::DropType { sub },