]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/let_underscore.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / compiler / rustc_lint / src / let_underscore.rs
1 use crate::{
2     lints::{NonBindingLet, NonBindingLetSub},
3     LateContext, LateLintPass, LintContext,
4 };
5 use rustc_errors::MultiSpan;
6 use rustc_hir as hir;
7 use rustc_middle::ty;
8 use rustc_span::Symbol;
9
10 declare_lint! {
11     /// The `let_underscore_drop` lint checks for statements which don't bind
12     /// an expression which has a non-trivial Drop implementation to anything,
13     /// causing the expression to be dropped immediately instead of at end of
14     /// scope.
15     ///
16     /// ### Example
17     ///
18     /// ```rust
19     /// struct SomeStruct;
20     /// impl Drop for SomeStruct {
21     ///     fn drop(&mut self) {
22     ///         println!("Dropping SomeStruct");
23     ///     }
24     /// }
25     ///
26     /// fn main() {
27     ///    #[warn(let_underscore_drop)]
28     ///     // SomeStuct is dropped immediately instead of at end of scope,
29     ///     // so "Dropping SomeStruct" is printed before "end of main".
30     ///     // The order of prints would be reversed if SomeStruct was bound to
31     ///     // a name (such as "_foo").
32     ///     let _ = SomeStruct;
33     ///     println!("end of main");
34     /// }
35     /// ```
36     ///
37     /// {{produces}}
38     ///
39     /// ### Explanation
40     ///
41     /// Statements which assign an expression to an underscore causes the
42     /// expression to immediately drop instead of extending the expression's
43     /// lifetime to the end of the scope. This is usually unintended,
44     /// especially for types like `MutexGuard`, which are typically used to
45     /// lock a mutex for the duration of an entire scope.
46     ///
47     /// If you want to extend the expression's lifetime to the end of the scope,
48     /// assign an underscore-prefixed name (such as `_foo`) to the expression.
49     /// If you do actually want to drop the expression immediately, then
50     /// calling `std::mem::drop` on the expression is clearer and helps convey
51     /// intent.
52     pub LET_UNDERSCORE_DROP,
53     Allow,
54     "non-binding let on a type that implements `Drop`"
55 }
56
57 declare_lint! {
58     /// The `let_underscore_lock` lint checks for statements which don't bind
59     /// a mutex to anything, causing the lock to be released immediately instead
60     /// of at end of scope, which is typically incorrect.
61     ///
62     /// ### Example
63     /// ```rust,compile_fail
64     /// use std::sync::{Arc, Mutex};
65     /// use std::thread;
66     /// let data = Arc::new(Mutex::new(0));
67     ///
68     /// thread::spawn(move || {
69     ///     // The lock is immediately released instead of at the end of the
70     ///     // scope, which is probably not intended.
71     ///     let _ = data.lock().unwrap();
72     ///     println!("doing some work");
73     ///     let mut lock = data.lock().unwrap();
74     ///     *lock += 1;
75     /// });
76     /// ```
77     ///
78     /// {{produces}}
79     ///
80     /// ### Explanation
81     ///
82     /// Statements which assign an expression to an underscore causes the
83     /// expression to immediately drop instead of extending the expression's
84     /// lifetime to the end of the scope. This is usually unintended,
85     /// especially for types like `MutexGuard`, which are typically used to
86     /// lock a mutex for the duration of an entire scope.
87     ///
88     /// If you want to extend the expression's lifetime to the end of the scope,
89     /// assign an underscore-prefixed name (such as `_foo`) to the expression.
90     /// If you do actually want to drop the expression immediately, then
91     /// calling `std::mem::drop` on the expression is clearer and helps convey
92     /// intent.
93     pub LET_UNDERSCORE_LOCK,
94     Deny,
95     "non-binding let on a synchronization lock"
96 }
97
98 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
99
100 const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
101     rustc_span::sym::MutexGuard,
102     rustc_span::sym::RwLockReadGuard,
103     rustc_span::sym::RwLockWriteGuard,
104 ];
105
106 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
107     fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
108         if !matches!(local.pat.kind, hir::PatKind::Wild) {
109             return;
110         }
111         if let Some(init) = local.init {
112             let init_ty = cx.typeck_results().expr_ty(init);
113             // If the type has a trivial Drop implementation, then it doesn't
114             // matter that we drop the value immediately.
115             if !init_ty.needs_drop(cx.tcx, cx.param_env) {
116                 return;
117             }
118             let is_sync_lock = match init_ty.kind() {
119                 ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
120                     .iter()
121                     .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
122                 _ => false,
123             };
124
125             let sub = NonBindingLetSub {
126                 suggestion: local.pat.span,
127                 multi_suggestion_start: local.span.until(init.span),
128                 multi_suggestion_end: init.span.shrink_to_hi(),
129             };
130             if is_sync_lock {
131                 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
132                 span.push_span_label(
133                     local.pat.span,
134                     "this lock is not assigned to a binding and is immediately dropped".to_string(),
135                 );
136                 span.push_span_label(
137                     init.span,
138                     "this binding will immediately drop the value assigned to it".to_string(),
139                 );
140                 cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
141             } else {
142                 cx.emit_spanned_lint(
143                     LET_UNDERSCORE_DROP,
144                     local.span,
145                     NonBindingLet::DropType { sub },
146                 );
147             }
148         }
149     }
150 }