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.unhygienic_name, 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.unhygienic_name;
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, lspan: 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, 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,
219 &format!("{} is shadowed by {} which reuses the original value",
220 snippet(cx, lspan, "_"),
221 snippet(cx, expr.span, "..")),
223 "initialization happens here");
224 note_orig(cx, db, SHADOW_REUSE, prev_span);
226 let db = span_note_and_lint(cx,
229 &format!("{} is shadowed by {}",
230 snippet(cx, lspan, "_"),
231 snippet(cx, expr.span, "..")),
233 "initialization happens here");
234 note_orig(cx, db, SHADOW_UNRELATED, prev_span);
238 let db = span_lint(cx,
241 &format!("{} shadows a previous declaration", snippet(cx, lspan, "_")));
242 note_orig(cx, db, SHADOW_UNRELATED, prev_span);
246 fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
247 if in_external_macro(cx, expr.span) {
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),
260 ExprVec(ref v) | ExprTup(ref v) => {
262 check_expr(cx, e, bindings)
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);
272 ExprWhile(ref cond, ref block, _) => {
273 check_expr(cx, cond, bindings);
274 check_block(cx, block, bindings);
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);
286 check_expr(cx, &arm.body, bindings);
287 bindings.truncate(len);
295 fn check_ty(cx: &LateContext, ty: &Ty, bindings: &mut Vec<(Name, Span)>) {
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);
303 TyPtr(MutTy { ty: ref mty, .. }) |
304 TyRptr(_, MutTy { ty: ref mty, .. }) => check_ty(cx, mty, bindings),
307 check_ty(cx, t, bindings)
310 TyTypeof(ref expr) => check_expr(cx, expr, bindings),
315 fn is_self_shadow(name: Name, expr: &Expr) -> bool {
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))
322 ExprUnary(op, ref inner) => (UnDeref == op) && is_self_shadow(name, inner),
323 ExprPath(_, ref path) => path_eq_name(name, path),
328 fn path_eq_name(name: Name, path: &Path) -> bool {
329 !path.global && path.segments.len() == 1 && path.segments[0].identifier.unhygienic_name == name
332 struct ContainsSelf {
337 impl<'v> Visitor<'v> for ContainsSelf {
338 fn visit_ident(&mut self, _: Span, ident: Ident) {
339 if self.name == ident.unhygienic_name {
345 fn contains_self(name: Name, expr: &Expr) -> bool {
346 let mut cs = ContainsSelf {