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