3 use rustc::hir::def::Def;
5 use rustc::hir::intravisit::{Visitor, FnKind};
7 use syntax::codemap::Span;
8 use utils::{is_from_for_desugar, in_external_macro, snippet, span_lint, span_note_and_lint, DiagnosticWrapper};
10 /// **What it does:** This lint checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.
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`.
14 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
16 /// **Example:** `let x = &x;`
18 pub SHADOW_SAME, Allow,
19 "rebinding a name to itself, e.g. `let mut x = &mut x`"
22 /// **What it does:** This lint checks for bindings that shadow other bindings already in scope, while reusing the original value.
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.
26 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
28 /// **Example:** `let x = x + 1;`
30 pub SHADOW_REUSE, Allow,
31 "rebinding a name to an expression that re-uses the original value, e.g. \
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.
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.
39 /// **Known problems:** This lint, as the other shadowing related lints, currently only catches very simple patterns.
41 /// **Example:** `let x = y; let x = z; // shadows the earlier binding`
43 pub SHADOW_UNRELATED, Allow,
44 "The name is re-bound without even using the original value"
47 #[derive(Copy, Clone)]
48 pub struct ShadowPass;
50 impl LintPass for ShadowPass {
51 fn get_lints(&self) -> LintArray {
52 lint_array!(SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED)
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) {
61 check_fn(cx, decl, block);
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))
72 check_block(cx, block, &mut bindings);
75 fn check_block(cx: &LateContext, block: &Block, bindings: &mut Vec<(Name, Span)>) {
76 let len = bindings.len();
77 for stmt in &block.stmts {
79 StmtDecl(ref decl, _) => check_decl(cx, decl, bindings),
81 StmtSemi(ref e, _) => check_expr(cx, e, bindings),
84 if let Some(ref o) = block.expr {
85 check_expr(cx, o, bindings);
87 bindings.truncate(len);
90 fn check_decl(cx: &LateContext, decl: &Decl, bindings: &mut Vec<(Name, Span)>) {
91 if in_external_macro(cx, decl.span) {
94 if is_from_for_desugar(decl) {
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)
102 if let Some(ref o) = *init {
103 check_expr(cx, o, bindings);
104 check_pat(cx, pat, &Some(o), span, bindings);
106 check_pat(cx, pat, &None, span, bindings);
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,
119 fn check_pat(cx: &LateContext, pat: &Pat, init: &Option<&Expr>, span: Span, bindings: &mut Vec<(Name, Span)>) {
120 // TODO: match more stuff / destructuring
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() {
128 lint_shadow(cx, name, span, pat.span, init, tup.1);
135 bindings.push((name, ident.span));
138 if let Some(ref p) = *inner {
139 check_pat(cx, p, init, span, bindings);
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)
151 check_pat(cx, &field.node.pat, &efield, span, bindings);
154 for field in pfields {
155 check_pat(cx, &field.node.pat, init, span, bindings);
159 for field in pfields {
160 check_pat(cx, &field.node.pat, &None, span, bindings);
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);
172 check_pat(cx, p, init, span, bindings);
177 check_pat(cx, p, &None, span, bindings);
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);
186 check_pat(cx, inner, init, span, bindings);
189 check_pat(cx, inner, init, span, bindings);
192 PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings),
193 // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
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>
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");
206 if let Some(ref expr) = *init {
207 if is_self_shadow(name, expr) {
208 let db = span_lint(cx,
211 &format!("`{}` is shadowed by itself in `{}`",
212 snippet(cx, pattern_span, "_"),
213 snippet(cx, expr.span, "..")));
215 note_orig(cx, db, SHADOW_SAME, prev_span);
216 } else if contains_self(name, expr) {
217 let db = span_note_and_lint(cx,
220 &format!("`{}` is shadowed by `{}` which reuses the original value",
221 snippet(cx, pattern_span, "_"),
222 snippet(cx, expr.span, "..")),
224 "initialization happens here");
225 note_orig(cx, db, SHADOW_REUSE, prev_span);
227 let db = span_note_and_lint(cx,
230 &format!("`{}` is shadowed by `{}`",
231 snippet(cx, pattern_span, "_"),
232 snippet(cx, expr.span, "..")),
234 "initialization happens here");
235 note_orig(cx, db, SHADOW_UNRELATED, prev_span);
239 let db = span_lint(cx,
242 &format!("{} shadows a previous declaration", snippet(cx, pattern_span, "_")));
243 note_orig(cx, db, SHADOW_UNRELATED, prev_span);
247 fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
248 if in_external_macro(cx, expr.span) {
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),
261 ExprVec(ref v) | ExprTup(ref v) => {
263 check_expr(cx, e, bindings)
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);
273 ExprWhile(ref cond, ref block, _) => {
274 check_expr(cx, cond, bindings);
275 check_block(cx, block, bindings);
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);
287 check_expr(cx, &arm.body, bindings);
288 bindings.truncate(len);
296 fn check_ty(cx: &LateContext, ty: &Ty, bindings: &mut Vec<(Name, Span)>) {
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);
304 TyPtr(MutTy { ty: ref mty, .. }) |
305 TyRptr(_, MutTy { ty: ref mty, .. }) => check_ty(cx, mty, bindings),
308 check_ty(cx, t, bindings)
311 TyTypeof(ref expr) => check_expr(cx, expr, bindings),
316 fn is_self_shadow(name: Name, expr: &Expr) -> bool {
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))
323 ExprUnary(op, ref inner) => (UnDeref == op) && is_self_shadow(name, inner),
324 ExprPath(_, ref path) => path_eq_name(name, path),
329 fn path_eq_name(name: Name, path: &Path) -> bool {
330 !path.global && path.segments.len() == 1 && path.segments[0].name.unhygienize() == name
333 struct ContainsSelf {
338 impl<'v> Visitor<'v> for ContainsSelf {
339 fn visit_name(&mut self, _: Span, name: Name) {
340 if self.name == name.unhygienize() {
346 fn contains_self(name: Name, expr: &Expr) -> bool {
347 let mut cs = ContainsSelf {