-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
let_underscore.rs
176 lines (168 loc) · 5.97 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
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{is_must_use_ty, match_type};
use clippy_utils::{is_must_use_func_call, paths};
use if_chain::if_chain;
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for `let _ = <expr>` where expr is `#[must_use]`
///
/// ### Why is this bad?
/// It's better to explicitly handle the value of a `#[must_use]`
/// expr
///
/// ### Example
/// ```rust
/// 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 `std::sync` and `parking_lot`.
///
/// ### 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
///
/// Bad:
/// ```rust,ignore
/// let _ = mutex.lock();
/// ```
///
/// Good:
/// ```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 expr has a type that implements `Drop`
///
/// ### Why is this bad?
/// This statement immediately drops the initializer
/// expression instead of extending its lifetime to the end of the scope, which
/// is often not intended. To extend the expression's lifetime to the end of the
/// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
/// explicitly drop the expression, `std::mem::drop` conveys your intention
/// better and is less error-prone.
///
/// ### Example
///
/// Bad:
/// ```rust,ignore
/// struct Droppable;
/// impl Drop for Droppable {
/// fn drop(&mut self) {}
/// }
/// {
/// let _ = Droppable;
/// // ^ dropped here
/// /* more code */
/// }
/// ```
///
/// Good:
/// ```rust,ignore
/// {
/// let _droppable = Droppable;
/// /* more code */
/// // dropped at end of scope
/// }
/// ```
#[clippy::version = "1.50.0"]
pub LET_UNDERSCORE_DROP,
pedantic,
"non-binding let on a type that implements `Drop`"
}
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
const SYNC_GUARD_PATHS: [&[&str]; 5] = [
&paths::MUTEX_GUARD,
&paths::RWLOCK_READ_GUARD,
&paths::RWLOCK_WRITE_GUARD,
&paths::PARKING_LOT_RAWMUTEX,
&paths::PARKING_LOT_RAWRWLOCK,
];
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
if in_external_macro(cx.tcx.sess, local.span) {
return;
}
if_chain! {
if let PatKind::Wild = local.pat.kind;
if let Some(init) = local.init;
then {
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 {
span_lint_and_help(
cx,
LET_UNDERSCORE_LOCK,
local.span,
"non-binding let on a synchronization lock",
None,
"consider using an underscore-prefixed named \
binding or dropping explicitly with `std::mem::drop`"
);
} else if init_ty.needs_drop(cx.tcx, cx.param_env) {
span_lint_and_help(
cx,
LET_UNDERSCORE_DROP,
local.span,
"non-binding `let` on a type that implements `Drop`",
None,
"consider using an underscore-prefixed named \
binding or dropping explicitly with `std::mem::drop`"
);
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
span_lint_and_help(
cx,
LET_UNDERSCORE_MUST_USE,
local.span,
"non-binding let on an expression with `#[must_use]` type",
None,
"consider explicitly using expression value"
);
} else if is_must_use_func_call(cx, init) {
span_lint_and_help(
cx,
LET_UNDERSCORE_MUST_USE,
local.span,
"non-binding let on a result of a `#[must_use]` function",
None,
"consider explicitly using function result"
);
}
}
}
}
}