]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use if_chain::if_chain; |
2 | use rustc_hir::{Local, PatKind}; | |
3 | use rustc_lint::{LateContext, LateLintPass}; | |
4 | use rustc_middle::lint::in_external_macro; | |
5 | use rustc_middle::ty::subst::GenericArgKind; | |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
7 | ||
8 | use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; | |
9 | ||
10 | declare_clippy_lint! { | |
11 | /// **What it does:** Checks for `let _ = <expr>` | |
12 | /// where expr is #[must_use] | |
13 | /// | |
14 | /// **Why is this bad?** It's better to explicitly | |
15 | /// handle the value of a #[must_use] expr | |
16 | /// | |
17 | /// **Known problems:** None. | |
18 | /// | |
19 | /// **Example:** | |
20 | /// ```rust | |
21 | /// fn f() -> Result<u32, u32> { | |
22 | /// Ok(0) | |
23 | /// } | |
24 | /// | |
25 | /// let _ = f(); | |
26 | /// // is_ok() is marked #[must_use] | |
27 | /// let _ = f().is_ok(); | |
28 | /// ``` | |
29 | pub LET_UNDERSCORE_MUST_USE, | |
30 | restriction, | |
31 | "non-binding let on a `#[must_use]` expression" | |
32 | } | |
33 | ||
34 | declare_clippy_lint! { | |
35 | /// **What it does:** Checks for `let _ = sync_lock` | |
36 | /// | |
37 | /// **Why is this bad?** This statement immediately drops the lock instead of | |
38 | /// extending its lifetime to the end of the scope, which is often not intended. | |
39 | /// To extend lock lifetime to the end of the scope, use an underscore-prefixed | |
40 | /// name instead (i.e. _lock). If you want to explicitly drop the lock, | |
41 | /// `std::mem::drop` conveys your intention better and is less error-prone. | |
42 | /// | |
43 | /// **Known problems:** None. | |
44 | /// | |
45 | /// **Example:** | |
46 | /// | |
47 | /// Bad: | |
48 | /// ```rust,ignore | |
49 | /// let _ = mutex.lock(); | |
50 | /// ``` | |
51 | /// | |
52 | /// Good: | |
53 | /// ```rust,ignore | |
54 | /// let _lock = mutex.lock(); | |
55 | /// ``` | |
56 | pub LET_UNDERSCORE_LOCK, | |
57 | correctness, | |
58 | "non-binding let on a synchronization lock" | |
59 | } | |
60 | ||
61 | declare_clippy_lint! { | |
62 | /// **What it does:** Checks for `let _ = <expr>` | |
63 | /// where expr has a type that implements `Drop` | |
64 | /// | |
65 | /// **Why is this bad?** This statement immediately drops the initializer | |
66 | /// expression instead of extending its lifetime to the end of the scope, which | |
67 | /// is often not intended. To extend the expression's lifetime to the end of the | |
68 | /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to | |
69 | /// explicitly drop the expression, `std::mem::drop` conveys your intention | |
70 | /// better and is less error-prone. | |
71 | /// | |
72 | /// **Known problems:** None. | |
73 | /// | |
74 | /// **Example:** | |
75 | /// | |
76 | /// Bad: | |
77 | /// ```rust,ignore | |
78 | /// struct Droppable; | |
79 | /// impl Drop for Droppable { | |
80 | /// fn drop(&mut self) {} | |
81 | /// } | |
82 | /// { | |
83 | /// let _ = Droppable; | |
84 | /// // ^ dropped here | |
85 | /// /* more code */ | |
86 | /// } | |
87 | /// ``` | |
88 | /// | |
89 | /// Good: | |
90 | /// ```rust,ignore | |
91 | /// { | |
92 | /// let _droppable = Droppable; | |
93 | /// /* more code */ | |
94 | /// // dropped at end of scope | |
95 | /// } | |
96 | /// ``` | |
97 | pub LET_UNDERSCORE_DROP, | |
98 | pedantic, | |
99 | "non-binding let on a type that implements `Drop`" | |
100 | } | |
101 | ||
102 | declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); | |
103 | ||
104 | const SYNC_GUARD_PATHS: [&[&str]; 3] = [ | |
105 | &paths::MUTEX_GUARD, | |
106 | &paths::RWLOCK_READ_GUARD, | |
107 | &paths::RWLOCK_WRITE_GUARD, | |
108 | ]; | |
109 | ||
110 | impl<'tcx> LateLintPass<'tcx> for LetUnderscore { | |
111 | fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { | |
112 | if in_external_macro(cx.tcx.sess, local.span) { | |
113 | return; | |
114 | } | |
115 | ||
116 | if_chain! { | |
117 | if let PatKind::Wild = local.pat.kind; | |
118 | if let Some(ref init) = local.init; | |
119 | then { | |
120 | let init_ty = cx.typeck_results().expr_ty(init); | |
121 | let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { | |
122 | GenericArgKind::Type(inner_ty) => { | |
123 | SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) | |
124 | }, | |
125 | ||
126 | GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, | |
127 | }); | |
128 | if contains_sync_guard { | |
129 | span_lint_and_help( | |
130 | cx, | |
131 | LET_UNDERSCORE_LOCK, | |
132 | local.span, | |
133 | "non-binding let on a synchronization lock", | |
134 | None, | |
135 | "consider using an underscore-prefixed named \ | |
136 | binding or dropping explicitly with `std::mem::drop`" | |
137 | ) | |
138 | } else if init_ty.needs_drop(cx.tcx, cx.param_env) { | |
139 | span_lint_and_help( | |
140 | cx, | |
141 | LET_UNDERSCORE_DROP, | |
142 | local.span, | |
143 | "non-binding `let` on a type that implements `Drop`", | |
144 | None, | |
145 | "consider using an underscore-prefixed named \ | |
146 | binding or dropping explicitly with `std::mem::drop`" | |
147 | ) | |
148 | } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { | |
149 | span_lint_and_help( | |
150 | cx, | |
151 | LET_UNDERSCORE_MUST_USE, | |
152 | local.span, | |
153 | "non-binding let on an expression with `#[must_use]` type", | |
154 | None, | |
155 | "consider explicitly using expression value" | |
156 | ) | |
157 | } else if is_must_use_func_call(cx, init) { | |
158 | span_lint_and_help( | |
159 | cx, | |
160 | LET_UNDERSCORE_MUST_USE, | |
161 | local.span, | |
162 | "non-binding let on a result of a `#[must_use]` function", | |
163 | None, | |
164 | "consider explicitly using function result" | |
165 | ) | |
166 | } | |
167 | } | |
168 | } | |
169 | } | |
170 | } |