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