]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/let_underscore.rs
merge rustc history
[rust.git] / compiler / rustc_lint / src / let_underscore.rs
1 use crate::{LateContext, LateLintPass, LintContext};
2 use rustc_errors::{Applicability, LintDiagnosticBuilder, MultiSpan};
3 use rustc_hir as hir;
4 use rustc_middle::ty;
5 use rustc_span::Symbol;
6
7 declare_lint! {
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
11     /// scope.
12     ///
13     /// ### Example
14     /// ```
15     /// struct SomeStruct;
16     /// impl Drop for SomeStruct {
17     ///     fn drop(&mut self) {
18     ///         println!("Dropping SomeStruct");
19     ///     }
20     /// }
21     ///
22     /// fn main() {
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");
30     /// }
31     /// ```
32     ///
33     /// {{produces}}
34     ///
35     /// ### Explanation
36     ///
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.
42     ///
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
47     /// intent.
48     pub LET_UNDERSCORE_DROP,
49     Allow,
50     "non-binding let on a type that implements `Drop`"
51 }
52
53 declare_lint! {
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.
57     ///
58     /// ### Example
59     /// ```compile_fail
60     /// use std::sync::{Arc, Mutex};
61     /// use std::thread;
62     /// let data = Arc::new(Mutex::new(0));
63     ///
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();
70     ///     *lock += 1;
71     /// });
72     /// ```
73     ///
74     /// {{produces}}
75     ///
76     /// ### Explanation
77     ///
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.
83     ///
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
88     /// intent.
89     pub LET_UNDERSCORE_LOCK,
90     Deny,
91     "non-binding let on a synchronization lock"
92 }
93
94 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
95
96 const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
97     rustc_span::sym::MutexGuard,
98     rustc_span::sym::RwLockReadGuard,
99     rustc_span::sym::RwLockWriteGuard,
100 ];
101
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) {
105             return;
106         }
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) {
112                 return;
113             }
114             let is_sync_lock = match init_ty.kind() {
115                 ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
116                     .iter()
117                     .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
118                 _ => false,
119             };
120
121             if is_sync_lock {
122                 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
123                 span.push_span_label(
124                     local.pat.span,
125                     "this lock is not assigned to a binding and is immediately dropped".to_string(),
126                 );
127                 span.push_span_label(
128                     init.span,
129                     "this binding will immediately drop the value assigned to it".to_string(),
130                 );
131                 cx.struct_span_lint(LET_UNDERSCORE_LOCK, span, |lint| {
132                     build_and_emit_lint(
133                         lint,
134                         local,
135                         init.span,
136                         "non-binding let on a synchronization lock",
137                     )
138                 })
139             } else {
140                 cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| {
141                     build_and_emit_lint(
142                         lint,
143                         local,
144                         init.span,
145                         "non-binding let on a type that implements `Drop`",
146                     );
147                 })
148             }
149         }
150     }
151 }
152
153 fn build_and_emit_lint(
154     lint: LintDiagnosticBuilder<'_, ()>,
155     local: &hir::Local<'_>,
156     init_span: rustc_span::Span,
157     msg: &str,
158 ) {
159     lint.build(msg)
160         .span_suggestion_verbose(
161             local.pat.span,
162             "consider binding to an unused variable to avoid immediately dropping the value",
163             "_unused",
164             Applicability::MachineApplicable,
165         )
166         .multipart_suggestion(
167             "consider immediately dropping the value",
168             vec![
169                 (local.span.until(init_span), "drop(".to_string()),
170                 (init_span.shrink_to_hi(), ")".to_string()),
171             ],
172             Applicability::MachineApplicable,
173         )
174         .emit();
175 }