]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/let_underscore.rs
991b3e920adbc6ea8def3f6b19217292873d506f
[rust.git] / compiler / rustc_lint / src / let_underscore.rs
1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 use crate::{
4     lints::{NonBindingLet, NonBindingLetSub},
5     LateContext, LateLintPass, LintContext,
6 };
7 use rustc_errors::MultiSpan;
8 use rustc_hir as hir;
9 use rustc_middle::ty;
10 use rustc_span::Symbol;
11
12 declare_lint! {
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
16     /// scope.
17     ///
18     /// ### Example
19     ///
20     /// ```rust
21     /// struct SomeStruct;
22     /// impl Drop for SomeStruct {
23     ///     fn drop(&mut self) {
24     ///         println!("Dropping SomeStruct");
25     ///     }
26     /// }
27     ///
28     /// fn main() {
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");
36     /// }
37     /// ```
38     ///
39     /// {{produces}}
40     ///
41     /// ### Explanation
42     ///
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.
48     ///
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
53     /// intent.
54     pub LET_UNDERSCORE_DROP,
55     Allow,
56     "non-binding let on a type that implements `Drop`"
57 }
58
59 declare_lint! {
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.
63     ///
64     /// ### Example
65     /// ```rust,compile_fail
66     /// use std::sync::{Arc, Mutex};
67     /// use std::thread;
68     /// let data = Arc::new(Mutex::new(0));
69     ///
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();
76     ///     *lock += 1;
77     /// });
78     /// ```
79     ///
80     /// {{produces}}
81     ///
82     /// ### Explanation
83     ///
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.
89     ///
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
94     /// intent.
95     pub LET_UNDERSCORE_LOCK,
96     Deny,
97     "non-binding let on a synchronization lock"
98 }
99
100 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
101
102 const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
103     rustc_span::sym::MutexGuard,
104     rustc_span::sym::RwLockReadGuard,
105     rustc_span::sym::RwLockWriteGuard,
106 ];
107
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) {
111             return;
112         }
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) {
118                 return;
119             }
120             let is_sync_lock = match init_ty.kind() {
121                 ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
122                     .iter()
123                     .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
124                 _ => false,
125             };
126
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(),
131             };
132             if is_sync_lock {
133                 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
134                 span.push_span_label(
135                     local.pat.span,
136                     "this lock is not assigned to a binding and is immediately dropped".to_string(),
137                 );
138                 span.push_span_label(
139                     init.span,
140                     "this binding will immediately drop the value assigned to it".to_string(),
141                 );
142                 cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
143             } else {
144                 cx.emit_spanned_lint(
145                     LET_UNDERSCORE_DROP,
146                     local.span,
147                     NonBindingLet::DropType { sub },
148                 );
149             }
150         }
151     }
152 }