]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/shadow.rs
New upstream version 1.53.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / shadow.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::snippet;
3 use clippy_utils::{contains_name, higher, iter_input_pats};
4 use rustc_hir::intravisit::FnKind;
5 use rustc_hir::{
6 Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
7 UnOp,
8 };
9 use rustc_lint::{LateContext, LateLintPass, LintContext};
10 use rustc_middle::lint::in_external_macro;
11 use rustc_middle::ty;
12 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 use rustc_span::source_map::Span;
14 use rustc_span::symbol::Symbol;
15
16 declare_clippy_lint! {
17 /// **What it does:** Checks for bindings that shadow other bindings already in
18 /// scope, while just changing reference level or mutability.
19 ///
20 /// **Why is this bad?** Not much, in fact it's a very common pattern in Rust
21 /// code. Still, some may opt to avoid it in their code base, they can set this
22 /// lint to `Warn`.
23 ///
24 /// **Known problems:** This lint, as the other shadowing related lints,
25 /// currently only catches very simple patterns.
26 ///
27 /// **Example:**
28 /// ```rust
29 /// # let x = 1;
30 /// // Bad
31 /// let x = &x;
32 ///
33 /// // Good
34 /// let y = &x; // use different variable name
35 /// ```
36 pub SHADOW_SAME,
37 restriction,
38 "rebinding a name to itself, e.g., `let mut x = &mut x`"
39 }
40
41 declare_clippy_lint! {
42 /// **What it does:** Checks for bindings that shadow other bindings already in
43 /// scope, while reusing the original value.
44 ///
45 /// **Why is this bad?** Not too much, in fact it's a common pattern in Rust
46 /// code. Still, some argue that name shadowing like this hurts readability,
47 /// because a value may be bound to different things depending on position in
48 /// the code.
49 ///
50 /// **Known problems:** This lint, as the other shadowing related lints,
51 /// currently only catches very simple patterns.
52 ///
53 /// **Example:**
54 /// ```rust
55 /// let x = 2;
56 /// let x = x + 1;
57 /// ```
58 /// use different variable name:
59 /// ```rust
60 /// let x = 2;
61 /// let y = x + 1;
62 /// ```
63 pub SHADOW_REUSE,
64 restriction,
65 "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
66 }
67
68 declare_clippy_lint! {
69 /// **What it does:** Checks for bindings that shadow other bindings already in
70 /// scope, either without a initialization or with one that does not even use
71 /// the original value.
72 ///
73 /// **Why is this bad?** Name shadowing can hurt readability, especially in
74 /// large code bases, because it is easy to lose track of the active binding at
75 /// any place in the code. This can be alleviated by either giving more specific
76 /// names to bindings or introducing more scopes to contain the bindings.
77 ///
78 /// **Known problems:** This lint, as the other shadowing related lints,
79 /// currently only catches very simple patterns. Note that
80 /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level
81 /// for this lint.
82 ///
83 /// **Example:**
84 /// ```rust
85 /// # let y = 1;
86 /// # let z = 2;
87 /// let x = y;
88 ///
89 /// // Bad
90 /// let x = z; // shadows the earlier binding
91 ///
92 /// // Good
93 /// let w = z; // use different variable name
94 /// ```
95 pub SHADOW_UNRELATED,
96 pedantic,
97 "rebinding a name without even using the original value"
98 }
99
100 declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
101
102 impl<'tcx> LateLintPass<'tcx> for Shadow {
103 fn check_fn(
104 &mut self,
105 cx: &LateContext<'tcx>,
106 _: FnKind<'tcx>,
107 decl: &'tcx FnDecl<'_>,
108 body: &'tcx Body<'_>,
109 _: Span,
110 _: HirId,
111 ) {
112 if in_external_macro(cx.sess(), body.value.span) {
113 return;
114 }
115 check_fn(cx, decl, body);
116 }
117 }
118
119 fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) {
120 let mut bindings = Vec::with_capacity(decl.inputs.len());
121 for arg in iter_input_pats(decl, body) {
122 if let PatKind::Binding(.., ident, _) = arg.pat.kind {
123 bindings.push((ident.name, ident.span))
124 }
125 }
126 check_expr(cx, &body.value, &mut bindings);
127 }
128
129 fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) {
130 let len = bindings.len();
131 for stmt in block.stmts {
132 match stmt.kind {
133 StmtKind::Local(local) => check_local(cx, local, bindings),
134 StmtKind::Expr(e) | StmtKind::Semi(e) => check_expr(cx, e, bindings),
135 StmtKind::Item(..) => {},
136 }
137 }
138 if let Some(o) = block.expr {
139 check_expr(cx, o, bindings);
140 }
141 bindings.truncate(len);
142 }
143
144 fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) {
145 if in_external_macro(cx.sess(), local.span) {
146 return;
147 }
148 if higher::is_from_for_desugar(local) {
149 return;
150 }
151 let Local {
152 pat,
153 ref ty,
154 ref init,
155 span,
156 ..
157 } = *local;
158 if let Some(t) = *ty {
159 check_ty(cx, t, bindings)
160 }
161 if let Some(o) = *init {
162 check_expr(cx, o, bindings);
163 check_pat(cx, pat, Some(o), span, bindings);
164 } else {
165 check_pat(cx, pat, None, span, bindings);
166 }
167 }
168
169 fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool {
170 let var_ty = cx.typeck_results().node_type_opt(pat_id);
171 var_ty.map_or(false, |var_ty| !matches!(var_ty.kind(), ty::Adt(..)))
172 }
173
174 fn check_pat<'tcx>(
175 cx: &LateContext<'tcx>,
176 pat: &'tcx Pat<'_>,
177 init: Option<&'tcx Expr<'_>>,
178 span: Span,
179 bindings: &mut Vec<(Symbol, Span)>,
180 ) {
181 // TODO: match more stuff / destructuring
182 match pat.kind {
183 PatKind::Binding(.., ident, ref inner) => {
184 let name = ident.name;
185 if is_binding(cx, pat.hir_id) {
186 let mut new_binding = true;
187 for tup in bindings.iter_mut() {
188 if tup.0 == name {
189 lint_shadow(cx, name, span, pat.span, init, tup.1);
190 tup.1 = ident.span;
191 new_binding = false;
192 break;
193 }
194 }
195 if new_binding {
196 bindings.push((name, ident.span));
197 }
198 }
199 if let Some(p) = *inner {
200 check_pat(cx, p, init, span, bindings);
201 }
202 },
203 PatKind::Struct(_, pfields, _) => {
204 if let Some(init_struct) = init {
205 if let ExprKind::Struct(_, efields, _) = init_struct.kind {
206 for field in pfields {
207 let name = field.ident.name;
208 let efield = efields
209 .iter()
210 .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None });
211 check_pat(cx, field.pat, efield, span, bindings);
212 }
213 } else {
214 for field in pfields {
215 check_pat(cx, field.pat, init, span, bindings);
216 }
217 }
218 } else {
219 for field in pfields {
220 check_pat(cx, field.pat, None, span, bindings);
221 }
222 }
223 },
224 PatKind::Tuple(inner, _) => {
225 if let Some(init_tup) = init {
226 if let ExprKind::Tup(tup) = init_tup.kind {
227 for (i, p) in inner.iter().enumerate() {
228 check_pat(cx, p, Some(&tup[i]), p.span, bindings);
229 }
230 } else {
231 for p in inner {
232 check_pat(cx, p, init, span, bindings);
233 }
234 }
235 } else {
236 for p in inner {
237 check_pat(cx, p, None, span, bindings);
238 }
239 }
240 },
241 PatKind::Box(inner) => {
242 if let Some(initp) = init {
243 if let ExprKind::Box(inner_init) = initp.kind {
244 check_pat(cx, inner, Some(inner_init), span, bindings);
245 } else {
246 check_pat(cx, inner, init, span, bindings);
247 }
248 } else {
249 check_pat(cx, inner, init, span, bindings);
250 }
251 },
252 PatKind::Ref(inner, _) => check_pat(cx, inner, init, span, bindings),
253 // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
254 _ => (),
255 }
256 }
257
258 fn lint_shadow<'tcx>(
259 cx: &LateContext<'tcx>,
260 name: Symbol,
261 span: Span,
262 pattern_span: Span,
263 init: Option<&'tcx Expr<'_>>,
264 prev_span: Span,
265 ) {
266 if let Some(expr) = init {
267 if is_self_shadow(name, expr) {
268 span_lint_and_then(
269 cx,
270 SHADOW_SAME,
271 span,
272 &format!(
273 "`{}` is shadowed by itself in `{}`",
274 snippet(cx, pattern_span, "_"),
275 snippet(cx, expr.span, "..")
276 ),
277 |diag| {
278 diag.span_note(prev_span, "previous binding is here");
279 },
280 );
281 } else if contains_name(name, expr) {
282 span_lint_and_then(
283 cx,
284 SHADOW_REUSE,
285 pattern_span,
286 &format!(
287 "`{}` is shadowed by `{}` which reuses the original value",
288 snippet(cx, pattern_span, "_"),
289 snippet(cx, expr.span, "..")
290 ),
291 |diag| {
292 diag.span_note(expr.span, "initialization happens here");
293 diag.span_note(prev_span, "previous binding is here");
294 },
295 );
296 } else {
297 span_lint_and_then(
298 cx,
299 SHADOW_UNRELATED,
300 pattern_span,
301 &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")),
302 |diag| {
303 diag.span_note(expr.span, "initialization happens here");
304 diag.span_note(prev_span, "previous binding is here");
305 },
306 );
307 }
308 } else {
309 span_lint_and_then(
310 cx,
311 SHADOW_UNRELATED,
312 span,
313 &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
314 |diag| {
315 diag.span_note(prev_span, "previous binding is here");
316 },
317 );
318 }
319 }
320
321 fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) {
322 if in_external_macro(cx.sess(), expr.span) {
323 return;
324 }
325 match expr.kind {
326 ExprKind::Unary(_, e) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Box(e) => {
327 check_expr(cx, e, bindings)
328 },
329 ExprKind::Block(block, _) | ExprKind::Loop(block, ..) => check_block(cx, block, bindings),
330 // ExprKind::Call
331 // ExprKind::MethodCall
332 ExprKind::Array(v) | ExprKind::Tup(v) => {
333 for e in v {
334 check_expr(cx, e, bindings)
335 }
336 },
337 ExprKind::If(cond, then, ref otherwise) => {
338 check_expr(cx, cond, bindings);
339 check_expr(cx, then, bindings);
340 if let Some(o) = *otherwise {
341 check_expr(cx, o, bindings);
342 }
343 },
344 ExprKind::Match(init, arms, _) => {
345 check_expr(cx, init, bindings);
346 let len = bindings.len();
347 for arm in arms {
348 check_pat(cx, arm.pat, Some(init), arm.pat.span, bindings);
349 // This is ugly, but needed to get the right type
350 if let Some(ref guard) = arm.guard {
351 match guard {
352 Guard::If(if_expr) => check_expr(cx, if_expr, bindings),
353 Guard::IfLet(guard_pat, guard_expr) => {
354 check_pat(cx, guard_pat, Some(*guard_expr), guard_pat.span, bindings);
355 check_expr(cx, guard_expr, bindings);
356 },
357 }
358 }
359 check_expr(cx, arm.body, bindings);
360 bindings.truncate(len);
361 }
362 },
363 _ => (),
364 }
365 }
366
367 fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) {
368 match ty.kind {
369 TyKind::Slice(sty) => check_ty(cx, sty, bindings),
370 TyKind::Array(fty, ref anon_const) => {
371 check_ty(cx, fty, bindings);
372 check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings);
373 },
374 TyKind::Ptr(MutTy { ty: mty, .. }) | TyKind::Rptr(_, MutTy { ty: mty, .. }) => check_ty(cx, mty, bindings),
375 TyKind::Tup(tup) => {
376 for t in tup {
377 check_ty(cx, t, bindings)
378 }
379 },
380 TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings),
381 _ => (),
382 }
383 }
384
385 fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
386 match expr.kind {
387 ExprKind::Box(inner) | ExprKind::AddrOf(_, _, inner) => is_self_shadow(name, inner),
388 ExprKind::Block(block, _) => {
389 block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e))
390 },
391 ExprKind::Unary(op, inner) => (UnOp::Deref == op) && is_self_shadow(name, inner),
392 ExprKind::Path(QPath::Resolved(_, path)) => path_eq_name(name, path),
393 _ => false,
394 }
395 }
396
397 fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
398 !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name
399 }