]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/let_underscore.rs
Rollup merge of #101648 - Timmmm:home_dir_docs, r=joshtriplett
[rust.git] / compiler / rustc_lint / src / let_underscore.rs
1 use crate::{LateContext, LateLintPass, LintContext};
2 use rustc_errors::{Applicability, DiagnosticBuilder, 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     /// ```rust
16     /// struct SomeStruct;
17     /// impl Drop for SomeStruct {
18     ///     fn drop(&mut self) {
19     ///         println!("Dropping SomeStruct");
20     ///     }
21     /// }
22     ///
23     /// fn main() {
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");
31     /// }
32     /// ```
33     ///
34     /// {{produces}}
35     ///
36     /// ### Explanation
37     ///
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.
43     ///
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
48     /// intent.
49     pub LET_UNDERSCORE_DROP,
50     Allow,
51     "non-binding let on a type that implements `Drop`"
52 }
53
54 declare_lint! {
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.
58     ///
59     /// ### Example
60     /// ```rust,compile_fail
61     /// use std::sync::{Arc, Mutex};
62     /// use std::thread;
63     /// let data = Arc::new(Mutex::new(0));
64     ///
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();
71     ///     *lock += 1;
72     /// });
73     /// ```
74     ///
75     /// {{produces}}
76     ///
77     /// ### Explanation
78     ///
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.
84     ///
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
89     /// intent.
90     pub LET_UNDERSCORE_LOCK,
91     Deny,
92     "non-binding let on a synchronization lock"
93 }
94
95 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
96
97 const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
98     rustc_span::sym::MutexGuard,
99     rustc_span::sym::RwLockReadGuard,
100     rustc_span::sym::RwLockWriteGuard,
101 ];
102
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) {
106             return;
107         }
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) {
113                 return;
114             }
115             let is_sync_lock = match init_ty.kind() {
116                 ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
117                     .iter()
118                     .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
119                 _ => false,
120             };
121
122             if is_sync_lock {
123                 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
124                 span.push_span_label(
125                     local.pat.span,
126                     "this lock is not assigned to a binding and is immediately dropped".to_string(),
127                 );
128                 span.push_span_label(
129                     init.span,
130                     "this binding will immediately drop the value assigned to it".to_string(),
131                 );
132                 cx.struct_span_lint(
133                     LET_UNDERSCORE_LOCK,
134                     span,
135                     "non-binding let on a synchronization lock",
136                     |lint| build_lint(lint, local, init.span),
137                 )
138             } else {
139                 cx.struct_span_lint(
140                     LET_UNDERSCORE_DROP,
141                     local.span,
142                     "non-binding let on a type that implements `Drop`",
143                     |lint| build_lint(lint, local, init.span),
144                 )
145             }
146         }
147     }
148 }
149
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(
156         local.pat.span,
157         "consider binding to an unused variable to avoid immediately dropping the value",
158         "_unused",
159         Applicability::MachineApplicable,
160     )
161     .multipart_suggestion(
162         "consider immediately dropping the value",
163         vec![
164             (local.span.until(init_span), "drop(".to_string()),
165             (init_span.shrink_to_hi(), ")".to_string()),
166         ],
167         Applicability::MachineApplicable,
168     )
169 }