2 use rustc_front::hir::*;
4 use syntax::codemap::Span;
5 use rustc_front::intravisit::{Visitor, FnKind};
8 use rustc::middle::def::Def::{DefVariant, DefStruct};
10 use utils::{is_from_for_desugar, in_external_macro, snippet, span_lint, span_note_and_lint};
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.
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`.
16 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
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.
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.
25 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
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. \
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.
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.
35 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
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");
41 #[derive(Copy, Clone)]
42 pub struct ShadowPass;
44 impl LintPass for ShadowPass {
45 fn get_lints(&self) -> LintArray {
46 lint_array!(SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED)
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);
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.unhygienic_name, ident.span))
66 check_block(cx, block, &mut bindings);
69 fn check_block(cx: &LateContext, block: &Block, bindings: &mut Vec<(Name, Span)>) {
70 let len = bindings.len();
71 for stmt in &block.stmts {
73 StmtDecl(ref decl, _) => check_decl(cx, decl, bindings),
74 StmtExpr(ref e, _) | StmtSemi(ref e, _) =>
75 check_expr(cx, e, bindings)
78 if let Some(ref o) = block.expr { check_expr(cx, o, bindings); }
79 bindings.truncate(len);
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);
92 check_pat(cx, pat, &None, span, bindings);
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,
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
108 PatIdent(_, ref ident, ref inner) => {
109 let name = ident.node.unhygienic_name;
110 if is_binding(cx, pat) {
111 let mut new_binding = true;
112 for tup in bindings.iter_mut() {
114 lint_shadow(cx, name, span, pat.span, init, tup.1);
121 bindings.push((name, ident.span));
124 if let Some(ref p) = *inner { check_pat(cx, p, init, span, bindings); }
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)
135 check_pat(cx, &field.node.pat, &efield, span, bindings);
138 for field in pfields {
139 check_pat(cx, &field.node.pat, init, span, bindings);
143 for field in pfields {
144 check_pat(cx, &field.node.pat, &None, span, bindings);
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);
155 check_pat(cx, p, init, span, bindings);
160 check_pat(cx, p, &None, span, bindings);
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);
168 check_pat(cx, inner, init, span, bindings);
171 check_pat(cx, inner, init, span, bindings);
174 PatRegion(ref inner, _) =>
175 check_pat(cx, inner, init, span, bindings),
176 //PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
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");
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);
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);
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);
213 span_lint(cx, SHADOW_UNRELATED, span, &format!(
214 "{} shadows a previous declaration", snippet(cx, lspan, "_")));
215 note_orig(cx, SHADOW_UNRELATED, prev_span);
219 fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
220 if in_external_macro(cx, expr.span) { return; }
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) }
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); }
236 ExprWhile(ref cond, ref block, _) => {
237 check_expr(cx, cond, bindings);
238 check_block(cx, block, bindings);
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);
250 check_expr(cx, &arm.body, bindings);
251 bindings.truncate(len);
259 fn check_ty(cx: &LateContext, ty: &Ty, bindings: &mut Vec<(Name, Span)>) {
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);
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),
275 fn is_self_shadow(name: Name, expr: &Expr) -> bool {
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),
288 fn path_eq_name(name: Name, path: &Path) -> bool {
289 !path.global && path.segments.len() == 1 &&
290 path.segments[0].identifier.unhygienic_name == name
293 struct ContainsSelf {
298 impl<'v> Visitor<'v> for ContainsSelf {
299 fn visit_ident(&mut self, _: Span, ident: Ident) {
300 if self.name == ident.unhygienic_name {
306 fn contains_self(name: Name, expr: &Expr) -> bool {
307 let mut cs = ContainsSelf { name: name, result: false };