]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_lint/src/let_underscore.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / compiler / rustc_lint / src / let_underscore.rs
CommitLineData
9c376795
FG
1use crate::{
2 lints::{NonBindingLet, NonBindingLetSub},
3 LateContext, LateLintPass, LintContext,
4};
5use rustc_errors::MultiSpan;
f2b60f7d
FG
6use rustc_hir as hir;
7use rustc_middle::ty;
8use rustc_span::Symbol;
9
10declare_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
487cf647
FG
17 ///
18 /// ```rust
f2b60f7d
FG
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
57declare_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
487cf647 63 /// ```rust,compile_fail
f2b60f7d
FG
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
98declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
99
100const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
101 rustc_span::sym::MutexGuard,
102 rustc_span::sym::RwLockReadGuard,
103 rustc_span::sym::RwLockWriteGuard,
104];
105
106impl<'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
9c376795
FG
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 };
f2b60f7d
FG
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 );
9c376795 140 cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
f2b60f7d 141 } else {
9c376795 142 cx.emit_spanned_lint(
2b03887a
FG
143 LET_UNDERSCORE_DROP,
144 local.span,
9c376795
FG
145 NonBindingLet::DropType { sub },
146 );
f2b60f7d
FG
147 }
148 }
149 }
150}