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