]> git.lizzy.rs Git - rust.git/blob - src/shadow.rs
Fix issue with `DOC_MARKDOWN` and punctuation
[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.unhygienic_name, 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.unhygienic_name;
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, lspan: 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, lspan, "_"),
213                                         snippet(cx, expr.span, "..")));
214             note_orig(cx, db, SHADOW_SAME, prev_span);
215         } else if contains_self(name, expr) {
216             let db = span_note_and_lint(cx,
217                                         SHADOW_REUSE,
218                                         lspan,
219                                         &format!("{} is shadowed by {} which reuses the original value",
220                                                  snippet(cx, lspan, "_"),
221                                                  snippet(cx, expr.span, "..")),
222                                         expr.span,
223                                         "initialization happens here");
224             note_orig(cx, db, SHADOW_REUSE, prev_span);
225         } else {
226             let db = span_note_and_lint(cx,
227                                         SHADOW_UNRELATED,
228                                         lspan,
229                                         &format!("{} is shadowed by {}",
230                                                  snippet(cx, lspan, "_"),
231                                                  snippet(cx, expr.span, "..")),
232                                         expr.span,
233                                         "initialization happens here");
234             note_orig(cx, db, SHADOW_UNRELATED, prev_span);
235         }
236
237     } else {
238         let db = span_lint(cx,
239                            SHADOW_UNRELATED,
240                            span,
241                            &format!("{} shadows a previous declaration", snippet(cx, lspan, "_")));
242         note_orig(cx, db, SHADOW_UNRELATED, prev_span);
243     }
244 }
245
246 fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
247     if in_external_macro(cx, expr.span) {
248         return;
249     }
250     match expr.node {
251         ExprUnary(_, ref e) |
252         ExprField(ref e, _) |
253         ExprTupField(ref e, _) |
254         ExprAddrOf(_, ref e) |
255         ExprBox(ref e) => check_expr(cx, e, bindings),
256         ExprBlock(ref block) |
257         ExprLoop(ref block, _) => check_block(cx, block, bindings),
258         // ExprCall
259         // ExprMethodCall
260         ExprVec(ref v) | ExprTup(ref v) => {
261             for ref e in v {
262                 check_expr(cx, e, bindings)
263             }
264         }
265         ExprIf(ref cond, ref then, ref otherwise) => {
266             check_expr(cx, cond, bindings);
267             check_block(cx, then, bindings);
268             if let Some(ref o) = *otherwise {
269                 check_expr(cx, o, bindings);
270             }
271         }
272         ExprWhile(ref cond, ref block, _) => {
273             check_expr(cx, cond, bindings);
274             check_block(cx, block, bindings);
275         }
276         ExprMatch(ref init, ref arms, _) => {
277             check_expr(cx, init, bindings);
278             let len = bindings.len();
279             for ref arm in arms {
280                 for ref pat in &arm.pats {
281                     check_pat(cx, &pat, &Some(&**init), pat.span, bindings);
282                     // This is ugly, but needed to get the right type
283                     if let Some(ref guard) = arm.guard {
284                         check_expr(cx, guard, bindings);
285                     }
286                     check_expr(cx, &arm.body, bindings);
287                     bindings.truncate(len);
288                 }
289             }
290         }
291         _ => (),
292     }
293 }
294
295 fn check_ty(cx: &LateContext, ty: &Ty, bindings: &mut Vec<(Name, Span)>) {
296     match ty.node {
297         TyObjectSum(ref sty, _) |
298         TyVec(ref sty) => check_ty(cx, sty, bindings),
299         TyFixedLengthVec(ref fty, ref expr) => {
300             check_ty(cx, fty, bindings);
301             check_expr(cx, expr, bindings);
302         }
303         TyPtr(MutTy { ty: ref mty, .. }) |
304         TyRptr(_, MutTy { ty: ref mty, .. }) => check_ty(cx, mty, bindings),
305         TyTup(ref tup) => {
306             for ref t in tup {
307                 check_ty(cx, t, bindings)
308             }
309         }
310         TyTypeof(ref expr) => check_expr(cx, expr, bindings),
311         _ => (),
312     }
313 }
314
315 fn is_self_shadow(name: Name, expr: &Expr) -> bool {
316     match expr.node {
317         ExprBox(ref inner) |
318         ExprAddrOf(_, ref inner) => is_self_shadow(name, inner),
319         ExprBlock(ref block) => {
320             block.stmts.is_empty() && block.expr.as_ref().map_or(false, |ref e| is_self_shadow(name, e))
321         }
322         ExprUnary(op, ref inner) => (UnDeref == op) && is_self_shadow(name, inner),
323         ExprPath(_, ref path) => path_eq_name(name, path),
324         _ => false,
325     }
326 }
327
328 fn path_eq_name(name: Name, path: &Path) -> bool {
329     !path.global && path.segments.len() == 1 && path.segments[0].identifier.unhygienic_name == name
330 }
331
332 struct ContainsSelf {
333     name: Name,
334     result: bool,
335 }
336
337 impl<'v> Visitor<'v> for ContainsSelf {
338     fn visit_ident(&mut self, _: Span, ident: Ident) {
339         if self.name == ident.unhygienic_name {
340             self.result = true;
341         }
342     }
343 }
344
345 fn contains_self(name: Name, expr: &Expr) -> bool {
346     let mut cs = ContainsSelf {
347         name: name,
348         result: false,
349     };
350     cs.visit_expr(expr);
351     cs.result
352 }