-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
let_underscore.rs
237 lines (226 loc) · 8.7 KB
/
let_underscore.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
use rustc_hir::{LetStmt, LocalSource, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{GenericArgKind, IsSuggestable};
use rustc_session::declare_lint_pass;
use rustc_span::{BytePos, Span};
declare_clippy_lint! {
/// ### What it does
/// Checks for `let _ = <expr>` where expr is `#[must_use]`
///
/// ### Why restrict this?
/// To ensure that all `#[must_use]` types are used rather than ignored.
///
/// ### Example
/// ```no_run
/// fn f() -> Result<u32, u32> {
/// Ok(0)
/// }
///
/// let _ = f();
/// // is_ok() is marked #[must_use]
/// let _ = f().is_ok();
/// ```
#[clippy::version = "1.42.0"]
pub LET_UNDERSCORE_MUST_USE,
restriction,
"non-binding `let` on a `#[must_use]` expression"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in
/// `parking_lot`. For `std` locks see the `rustc` lint
/// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock)
///
/// ### Why is this bad?
/// This statement immediately drops the lock instead of
/// extending its lifetime to the end of the scope, which is often not intended.
/// To extend lock lifetime to the end of the scope, use an underscore-prefixed
/// name instead (i.e. _lock). If you want to explicitly drop the lock,
/// `std::mem::drop` conveys your intention better and is less error-prone.
///
/// ### Example
/// ```rust,ignore
/// let _ = mutex.lock();
/// ```
///
/// Use instead:
/// ```rust,ignore
/// let _lock = mutex.lock();
/// ```
#[clippy::version = "1.43.0"]
pub LET_UNDERSCORE_LOCK,
correctness,
"non-binding `let` on a synchronization lock"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
///
/// ### Why is this bad?
/// Futures must be polled for work to be done. The original intention was most likely to await the future
/// and ignore the resulting value.
///
/// ### Example
/// ```no_run
/// async fn foo() -> Result<(), ()> {
/// Ok(())
/// }
/// let _ = foo();
/// ```
///
/// Use instead:
/// ```no_run
/// # async fn context() {
/// async fn foo() -> Result<(), ()> {
/// Ok(())
/// }
/// let _ = foo().await;
/// # }
/// ```
#[clippy::version = "1.67.0"]
pub LET_UNDERSCORE_FUTURE,
suspicious,
"non-binding `let` on a future"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one,
/// or remove the `let` keyword altogether.
///
/// ### Why restrict this?
/// The `let _ = <expr>` expression ignores the value of `<expr>`, but will continue to do so even
/// if the type were to change, thus potentially introducing subtle bugs. By supplying a type
/// annotation, one will be forced to re-visit the decision to ignore the value in such cases.
///
/// ### Known problems
/// The `_ = <expr>` is not properly supported by some tools (e.g. IntelliJ) and may seem odd
/// to many developers. This lint also partially overlaps with the other `let_underscore_*`
/// lints.
///
/// ### Example
/// ```no_run
/// fn foo() -> Result<u32, ()> {
/// Ok(123)
/// }
/// let _ = foo();
/// ```
/// Use instead:
/// ```no_run
/// fn foo() -> Result<u32, ()> {
/// Ok(123)
/// }
/// // Either provide a type annotation:
/// let _: Result<u32, ()> = foo();
/// // …or drop the let keyword:
/// _ = foo();
/// ```
#[clippy::version = "1.69.0"]
pub LET_UNDERSCORE_UNTYPED,
restriction,
"non-binding `let` without a type annotation"
}
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);
const SYNC_GUARD_PATHS: [&[&str]; 3] = [
&paths::PARKING_LOT_MUTEX_GUARD,
&paths::PARKING_LOT_RWLOCK_READ_GUARD,
&paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
];
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
if matches!(local.source, LocalSource::Normal)
&& let PatKind::Wild = local.pat.kind
&& let Some(init) = local.init
&& !in_external_macro(cx.tcx.sess, local.span)
{
let init_ty = cx.typeck_results().expr_ty(init);
let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
});
if contains_sync_guard {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_LOCK,
local.span,
"non-binding `let` on a synchronization lock",
|diag| {
diag.help(
"consider using an underscore-prefixed named \
binding or dropping explicitly with `std::mem::drop`",
);
},
);
} else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[])
{
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_FUTURE,
local.span,
"non-binding `let` on a future",
|diag| {
diag.help("consider awaiting the future or dropping explicitly with `std::mem::drop`");
},
);
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_MUST_USE,
local.span,
"non-binding `let` on an expression with `#[must_use]` type",
|diag| {
diag.help("consider explicitly using expression value");
},
);
} else if is_must_use_func_call(cx, init) {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
LET_UNDERSCORE_MUST_USE,
local.span,
"non-binding `let` on a result of a `#[must_use]` function",
|diag| {
diag.help("consider explicitly using function result");
},
);
}
if local.pat.default_binding_modes && local.ty.is_none() {
// When `default_binding_modes` is true, the `let` keyword is present.
// Ignore unnameable types
if let Some(init) = local.init
&& !cx.typeck_results().expr_ty(init).is_suggestable(cx.tcx, true)
{
return;
}
// Ignore if it is from a procedural macro...
if is_from_proc_macro(cx, init) {
return;
}
span_lint_and_then(
cx,
LET_UNDERSCORE_UNTYPED,
local.span,
"non-binding `let` without a type annotation",
|diag| {
diag.span_help(
Span::new(
local.pat.span.hi(),
local.pat.span.hi() + BytePos(1),
local.pat.span.ctxt(),
local.pat.span.parent(),
),
"consider adding a type annotation",
);
},
);
}
}
}
}