]> git.lizzy.rs Git - rust.git/blob - src/shadow.rs
Rustup to *1.10.0-nightly (476fe6eef 2016-05-21)*
[rust.git] / src / shadow.rs
1 use reexport::*;
2 use rustc::lint::*;
3 use rustc::hir::def::Def;
4 use rustc::hir::*;
5 use rustc::hir::intravisit::{Visitor, FnKind};
6 use std::ops::Deref;
7 use syntax::codemap::Span;
8 use utils::{is_from_for_desugar, in_external_macro, snippet, span_lint, span_note_and_lint, DiagnosticWrapper};
9
10 /// **What it does:** This lint checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.
11 ///
12 /// **Why is this bad?** Not much, in fact it's a very common pattern in Rust code. Still, some may opt to avoid it in their code base, they can set this lint to `Warn`.
13 ///
14 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
15 ///
16 /// **Example:** `let x = &x;`
17 declare_lint! {
18     pub SHADOW_SAME, Allow,
19     "rebinding a name to itself, e.g. `let mut x = &mut x`"
20 }
21
22 /// **What it does:** This lint checks for bindings that shadow other bindings already in scope, while reusing the original value.
23 ///
24 /// **Why is this bad?** Not too much, in fact it's a common pattern in Rust code. Still, some argue that name shadowing like this hurts readability, because a value may be bound to different things depending on position in the code.
25 ///
26 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
27 ///
28 /// **Example:** `let x = x + 1;`
29 declare_lint! {
30     pub SHADOW_REUSE, Allow,
31     "rebinding a name to an expression that re-uses the original value, e.g. \
32     `let x = x + 1`"
33 }
34
35 /// **What it does:** This lint checks for bindings that shadow other bindings already in scope, either without a initialization or with one that does not even use the original value.
36 ///
37 /// **Why is this bad?** Name shadowing can hurt readability, especially in large code bases, because it is easy to lose track of the active binding at any place in the code. This can be alleviated by either giving more specific names to bindings ore introducing more scopes to contain the bindings.
38 ///
39 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
40 ///
41 /// **Example:** `let x = y; let x = z; // shadows the earlier binding`
42 declare_lint! {
43     pub SHADOW_UNRELATED, Allow,
44     "The name is re-bound without even using the original value"
45 }
46
47 #[derive(Copy, Clone)]
48 pub struct ShadowPass;
49
50 impl LintPass for ShadowPass {
51     fn get_lints(&self) -> LintArray {
52         lint_array!(SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED)
53     }
54 }
55
56 impl LateLintPass for ShadowPass {
57     fn check_fn(&mut self, cx: &LateContext, _: FnKind, decl: &FnDecl, block: &Block, _: Span, _: NodeId) {
58         if in_external_macro(cx, block.span) {
59             return;
60         }
61         check_fn(cx, decl, block);
62     }
63 }
64
65 fn check_fn(cx: &LateContext, decl: &FnDecl, block: &Block) {
66     let mut bindings = Vec::new();
67     for arg in &decl.inputs {
68         if let PatKind::Ident(_, ident, _) = arg.pat.node {
69             bindings.push((ident.node.unhygienize(), ident.span))
70         }
71     }
72     check_block(cx, block, &mut bindings);
73 }
74
75 fn check_block(cx: &LateContext, block: &Block, bindings: &mut Vec<(Name, Span)>) {
76     let len = bindings.len();
77     for stmt in &block.stmts {
78         match stmt.node {
79             StmtDecl(ref decl, _) => check_decl(cx, decl, bindings),
80             StmtExpr(ref e, _) |
81             StmtSemi(ref e, _) => check_expr(cx, e, bindings),
82         }
83     }
84     if let Some(ref o) = block.expr {
85         check_expr(cx, o, bindings);
86     }
87     bindings.truncate(len);
88 }
89
90 fn check_decl(cx: &LateContext, decl: &Decl, bindings: &mut Vec<(Name, Span)>) {
91     if in_external_macro(cx, decl.span) {
92         return;
93     }
94     if is_from_for_desugar(decl) {
95         return;
96     }
97     if let DeclLocal(ref local) = decl.node {
98         let Local { ref pat, ref ty, ref init, span, .. } = **local;
99         if let Some(ref t) = *ty {
100             check_ty(cx, t, bindings)
101         }
102         if let Some(ref o) = *init {
103             check_expr(cx, o, bindings);
104             check_pat(cx, pat, &Some(o), span, bindings);
105         } else {
106             check_pat(cx, pat, &None, span, bindings);
107         }
108     }
109 }
110
111 fn is_binding(cx: &LateContext, pat: &Pat) -> bool {
112     match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
113         Some(Def::Variant(..)) |
114         Some(Def::Struct(..)) => false,
115         _ => true,
116     }
117 }
118
119 fn check_pat(cx: &LateContext, pat: &Pat, init: &Option<&Expr>, span: Span, bindings: &mut Vec<(Name, Span)>) {
120     // TODO: match more stuff / destructuring
121     match pat.node {
122         PatKind::Ident(_, ref ident, ref inner) => {
123             let name = ident.node.unhygienize();
124             if is_binding(cx, pat) {
125                 let mut new_binding = true;
126                 for tup in bindings.iter_mut() {
127                     if tup.0 == name {
128                         lint_shadow(cx, name, span, pat.span, init, tup.1);
129                         tup.1 = ident.span;
130                         new_binding = false;
131                         break;
132                     }
133                 }
134                 if new_binding {
135                     bindings.push((name, ident.span));
136                 }
137             }
138             if let Some(ref p) = *inner {
139                 check_pat(cx, p, init, span, bindings);
140             }
141         }
142         // PatEnum(Path, Option<Vec<P<Pat>>>),
143         PatKind::Struct(_, ref pfields, _) => {
144             if let Some(ref init_struct) = *init {
145                 if let ExprStruct(_, ref efields, _) = init_struct.node {
146                     for field in pfields {
147                         let name = field.node.name;
148                         let efield = efields.iter()
149                                             .find(|ref f| f.name.node == name)
150                                             .map(|f| &*f.expr);
151                         check_pat(cx, &field.node.pat, &efield, span, bindings);
152                     }
153                 } else {
154                     for field in pfields {
155                         check_pat(cx, &field.node.pat, init, span, bindings);
156                     }
157                 }
158             } else {
159                 for field in pfields {
160                     check_pat(cx, &field.node.pat, &None, span, bindings);
161                 }
162             }
163         }
164         PatKind::Tup(ref inner) => {
165             if let Some(ref init_tup) = *init {
166                 if let ExprTup(ref tup) = init_tup.node {
167                     for (i, p) in inner.iter().enumerate() {
168                         check_pat(cx, p, &Some(&tup[i]), p.span, bindings);
169                     }
170                 } else {
171                     for p in inner {
172                         check_pat(cx, p, init, span, bindings);
173                     }
174                 }
175             } else {
176                 for p in inner {
177                     check_pat(cx, p, &None, span, bindings);
178                 }
179             }
180         }
181         PatKind::Box(ref inner) => {
182             if let Some(ref initp) = *init {
183                 if let ExprBox(ref inner_init) = initp.node {
184                     check_pat(cx, inner, &Some(&**inner_init), span, bindings);
185                 } else {
186                     check_pat(cx, inner, init, span, bindings);
187                 }
188             } else {
189                 check_pat(cx, inner, init, span, bindings);
190             }
191         }
192         PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings),
193         // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
194         _ => (),
195     }
196 }
197
198 fn lint_shadow<T>(cx: &LateContext, name: Name, span: Span, pattern_span: Span, init: &Option<T>, prev_span: Span)
199     where T: Deref<Target = Expr>
200 {
201     fn note_orig(cx: &LateContext, mut db: DiagnosticWrapper, lint: &'static Lint, span: Span) {
202         if cx.current_level(lint) != Level::Allow {
203             db.span_note(span, "previous binding is here");
204         }
205     }
206     if let Some(ref expr) = *init {
207         if is_self_shadow(name, expr) {
208             let db = span_lint(cx,
209                                SHADOW_SAME,
210                                span,
211                                &format!("`{}` is shadowed by itself in `{}`",
212                                         snippet(cx, pattern_span, "_"),
213                                         snippet(cx, expr.span, "..")));
214
215             note_orig(cx, db, SHADOW_SAME, prev_span);
216         } else if contains_self(name, expr) {
217             let db = span_note_and_lint(cx,
218                                         SHADOW_REUSE,
219                                         pattern_span,
220                                         &format!("`{}` is shadowed by `{}` which reuses the original value",
221                                                  snippet(cx, pattern_span, "_"),
222                                                  snippet(cx, expr.span, "..")),
223                                         expr.span,
224                                         "initialization happens here");
225             note_orig(cx, db, SHADOW_REUSE, prev_span);
226         } else {
227             let db = span_note_and_lint(cx,
228                                         SHADOW_UNRELATED,
229                                         pattern_span,
230                                         &format!("`{}` is shadowed by `{}`",
231                                                  snippet(cx, pattern_span, "_"),
232                                                  snippet(cx, expr.span, "..")),
233                                         expr.span,
234                                         "initialization happens here");
235             note_orig(cx, db, SHADOW_UNRELATED, prev_span);
236         }
237
238     } else {
239         let db = span_lint(cx,
240                            SHADOW_UNRELATED,
241                            span,
242                            &format!("{} shadows a previous declaration", snippet(cx, pattern_span, "_")));
243         note_orig(cx, db, SHADOW_UNRELATED, prev_span);
244     }
245 }
246
247 fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
248     if in_external_macro(cx, expr.span) {
249         return;
250     }
251     match expr.node {
252         ExprUnary(_, ref e) |
253         ExprField(ref e, _) |
254         ExprTupField(ref e, _) |
255         ExprAddrOf(_, ref e) |
256         ExprBox(ref e) => check_expr(cx, e, bindings),
257         ExprBlock(ref block) |
258         ExprLoop(ref block, _) => check_block(cx, block, bindings),
259         // ExprCall
260         // ExprMethodCall
261         ExprVec(ref v) | ExprTup(ref v) => {
262             for ref e in v {
263                 check_expr(cx, e, bindings)
264             }
265         }
266         ExprIf(ref cond, ref then, ref otherwise) => {
267             check_expr(cx, cond, bindings);
268             check_block(cx, then, bindings);
269             if let Some(ref o) = *otherwise {
270                 check_expr(cx, o, bindings);
271             }
272         }
273         ExprWhile(ref cond, ref block, _) => {
274             check_expr(cx, cond, bindings);
275             check_block(cx, block, bindings);
276         }
277         ExprMatch(ref init, ref arms, _) => {
278             check_expr(cx, init, bindings);
279             let len = bindings.len();
280             for ref arm in arms {
281                 for ref pat in &arm.pats {
282                     check_pat(cx, pat, &Some(&**init), pat.span, bindings);
283                     // This is ugly, but needed to get the right type
284                     if let Some(ref guard) = arm.guard {
285                         check_expr(cx, guard, bindings);
286                     }
287                     check_expr(cx, &arm.body, bindings);
288                     bindings.truncate(len);
289                 }
290             }
291         }
292         _ => (),
293     }
294 }
295
296 fn check_ty(cx: &LateContext, ty: &Ty, bindings: &mut Vec<(Name, Span)>) {
297     match ty.node {
298         TyObjectSum(ref sty, _) |
299         TyVec(ref sty) => check_ty(cx, sty, bindings),
300         TyFixedLengthVec(ref fty, ref expr) => {
301             check_ty(cx, fty, bindings);
302             check_expr(cx, expr, bindings);
303         }
304         TyPtr(MutTy { ty: ref mty, .. }) |
305         TyRptr(_, MutTy { ty: ref mty, .. }) => check_ty(cx, mty, bindings),
306         TyTup(ref tup) => {
307             for ref t in tup {
308                 check_ty(cx, t, bindings)
309             }
310         }
311         TyTypeof(ref expr) => check_expr(cx, expr, bindings),
312         _ => (),
313     }
314 }
315
316 fn is_self_shadow(name: Name, expr: &Expr) -> bool {
317     match expr.node {
318         ExprBox(ref inner) |
319         ExprAddrOf(_, ref inner) => is_self_shadow(name, inner),
320         ExprBlock(ref block) => {
321             block.stmts.is_empty() && block.expr.as_ref().map_or(false, |ref e| is_self_shadow(name, e))
322         }
323         ExprUnary(op, ref inner) => (UnDeref == op) && is_self_shadow(name, inner),
324         ExprPath(_, ref path) => path_eq_name(name, path),
325         _ => false,
326     }
327 }
328
329 fn path_eq_name(name: Name, path: &Path) -> bool {
330     !path.global && path.segments.len() == 1 && path.segments[0].name.unhygienize() == name
331 }
332
333 struct ContainsSelf {
334     name: Name,
335     result: bool,
336 }
337
338 impl<'v> Visitor<'v> for ContainsSelf {
339     fn visit_name(&mut self, _: Span, name: Name) {
340         if self.name == name.unhygienize() {
341             self.result = true;
342         }
343     }
344 }
345
346 fn contains_self(name: Name, expr: &Expr) -> bool {
347     let mut cs = ContainsSelf {
348         name: name,
349         result: false,
350     };
351     cs.visit_expr(expr);
352     cs.result
353 }