]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/shadow.rs
Auto merge of #83666 - Amanieu:instrprof-order, r=tmandry
[rust.git] / src / tools / clippy / clippy_lints / src / shadow.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::snippet;
3 use clippy_utils::{contains_name, higher, iter_input_pats};
4 use rustc_hir::intravisit::FnKind;
5 use rustc_hir::{
6     Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
7     UnOp,
8 };
9 use rustc_lint::{LateContext, LateLintPass, LintContext};
10 use rustc_middle::lint::in_external_macro;
11 use rustc_middle::ty;
12 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 use rustc_span::source_map::Span;
14 use rustc_span::symbol::Symbol;
15
16 declare_clippy_lint! {
17     /// **What it does:** Checks for bindings that shadow other bindings already in
18     /// scope, while just changing reference level or mutability.
19     ///
20     /// **Why is this bad?** Not much, in fact it's a very common pattern in Rust
21     /// code. Still, some may opt to avoid it in their code base, they can set this
22     /// lint to `Warn`.
23     ///
24     /// **Known problems:** This lint, as the other shadowing related lints,
25     /// currently only catches very simple patterns.
26     ///
27     /// **Example:**
28     /// ```rust
29     /// # let x = 1;
30     /// // Bad
31     /// let x = &x;
32     ///
33     /// // Good
34     /// let y = &x; // use different variable name
35     /// ```
36     pub SHADOW_SAME,
37     restriction,
38     "rebinding a name to itself, e.g., `let mut x = &mut x`"
39 }
40
41 declare_clippy_lint! {
42     /// **What it does:** Checks for bindings that shadow other bindings already in
43     /// scope, while reusing the original value.
44     ///
45     /// **Why is this bad?** Not too much, in fact it's a common pattern in Rust
46     /// code. Still, some argue that name shadowing like this hurts readability,
47     /// because a value may be bound to different things depending on position in
48     /// the code.
49     ///
50     /// **Known problems:** This lint, as the other shadowing related lints,
51     /// currently only catches very simple patterns.
52     ///
53     /// **Example:**
54     /// ```rust
55     /// let x = 2;
56     /// let x = x + 1;
57     /// ```
58     /// use different variable name:
59     /// ```rust
60     /// let x = 2;
61     /// let y = x + 1;
62     /// ```
63     pub SHADOW_REUSE,
64     restriction,
65     "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
66 }
67
68 declare_clippy_lint! {
69     /// **What it does:** Checks for bindings that shadow other bindings already in
70     /// scope, either without a initialization or with one that does not even use
71     /// the original value.
72     ///
73     /// **Why is this bad?** Name shadowing can hurt readability, especially in
74     /// large code bases, because it is easy to lose track of the active binding at
75     /// any place in the code. This can be alleviated by either giving more specific
76     /// names to bindings or introducing more scopes to contain the bindings.
77     ///
78     /// **Known problems:** This lint, as the other shadowing related lints,
79     /// currently only catches very simple patterns. Note that
80     /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level
81     /// for this lint.
82     ///
83     /// **Example:**
84     /// ```rust
85     /// # let y = 1;
86     /// # let z = 2;
87     /// let x = y;
88     ///
89     /// // Bad
90     /// let x = z; // shadows the earlier binding
91     ///
92     /// // Good
93     /// let w = z; // use different variable name
94     /// ```
95     pub SHADOW_UNRELATED,
96     pedantic,
97     "rebinding a name without even using the original value"
98 }
99
100 declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
101
102 impl<'tcx> LateLintPass<'tcx> for Shadow {
103     fn check_fn(
104         &mut self,
105         cx: &LateContext<'tcx>,
106         _: FnKind<'tcx>,
107         decl: &'tcx FnDecl<'_>,
108         body: &'tcx Body<'_>,
109         _: Span,
110         _: HirId,
111     ) {
112         if in_external_macro(cx.sess(), body.value.span) {
113             return;
114         }
115         check_fn(cx, decl, body);
116     }
117 }
118
119 fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>) {
120     let mut bindings = Vec::with_capacity(decl.inputs.len());
121     for arg in iter_input_pats(decl, body) {
122         if let PatKind::Binding(.., ident, _) = arg.pat.kind {
123             bindings.push((ident.name, ident.span))
124         }
125     }
126     check_expr(cx, &body.value, &mut bindings);
127 }
128
129 fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) {
130     let len = bindings.len();
131     for stmt in block.stmts {
132         match stmt.kind {
133             StmtKind::Local(ref local) => check_local(cx, local, bindings),
134             StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => check_expr(cx, e, bindings),
135             StmtKind::Item(..) => {},
136         }
137     }
138     if let Some(ref o) = block.expr {
139         check_expr(cx, o, bindings);
140     }
141     bindings.truncate(len);
142 }
143
144 fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) {
145     if in_external_macro(cx.sess(), local.span) {
146         return;
147     }
148     if higher::is_from_for_desugar(local) {
149         return;
150     }
151     let Local {
152         ref pat,
153         ref ty,
154         ref init,
155         span,
156         ..
157     } = *local;
158     if let Some(ref t) = *ty {
159         check_ty(cx, t, bindings)
160     }
161     if let Some(ref o) = *init {
162         check_expr(cx, o, bindings);
163         check_pat(cx, pat, Some(o), span, bindings);
164     } else {
165         check_pat(cx, pat, None, span, bindings);
166     }
167 }
168
169 fn is_binding(cx: &LateContext<'_>, pat_id: HirId) -> bool {
170     let var_ty = cx.typeck_results().node_type_opt(pat_id);
171     var_ty.map_or(false, |var_ty| !matches!(var_ty.kind(), ty::Adt(..)))
172 }
173
174 fn check_pat<'tcx>(
175     cx: &LateContext<'tcx>,
176     pat: &'tcx Pat<'_>,
177     init: Option<&'tcx Expr<'_>>,
178     span: Span,
179     bindings: &mut Vec<(Symbol, Span)>,
180 ) {
181     // TODO: match more stuff / destructuring
182     match pat.kind {
183         PatKind::Binding(.., ident, ref inner) => {
184             let name = ident.name;
185             if is_binding(cx, pat.hir_id) {
186                 let mut new_binding = true;
187                 for tup in bindings.iter_mut() {
188                     if tup.0 == name {
189                         lint_shadow(cx, name, span, pat.span, init, tup.1);
190                         tup.1 = ident.span;
191                         new_binding = false;
192                         break;
193                     }
194                 }
195                 if new_binding {
196                     bindings.push((name, ident.span));
197                 }
198             }
199             if let Some(ref p) = *inner {
200                 check_pat(cx, p, init, span, bindings);
201             }
202         },
203         PatKind::Struct(_, pfields, _) => {
204             if let Some(init_struct) = init {
205                 if let ExprKind::Struct(_, ref efields, _) = init_struct.kind {
206                     for field in pfields {
207                         let name = field.ident.name;
208                         let efield = efields
209                             .iter()
210                             .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None });
211                         check_pat(cx, &field.pat, efield, span, bindings);
212                     }
213                 } else {
214                     for field in pfields {
215                         check_pat(cx, &field.pat, init, span, bindings);
216                     }
217                 }
218             } else {
219                 for field in pfields {
220                     check_pat(cx, &field.pat, None, span, bindings);
221                 }
222             }
223         },
224         PatKind::Tuple(inner, _) => {
225             if let Some(init_tup) = init {
226                 if let ExprKind::Tup(ref tup) = init_tup.kind {
227                     for (i, p) in inner.iter().enumerate() {
228                         check_pat(cx, p, Some(&tup[i]), p.span, bindings);
229                     }
230                 } else {
231                     for p in inner {
232                         check_pat(cx, p, init, span, bindings);
233                     }
234                 }
235             } else {
236                 for p in inner {
237                     check_pat(cx, p, None, span, bindings);
238                 }
239             }
240         },
241         PatKind::Box(ref inner) => {
242             if let Some(initp) = init {
243                 if let ExprKind::Box(ref inner_init) = initp.kind {
244                     check_pat(cx, inner, Some(&**inner_init), span, bindings);
245                 } else {
246                     check_pat(cx, inner, init, span, bindings);
247                 }
248             } else {
249                 check_pat(cx, inner, init, span, bindings);
250             }
251         },
252         PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings),
253         // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
254         _ => (),
255     }
256 }
257
258 fn lint_shadow<'tcx>(
259     cx: &LateContext<'tcx>,
260     name: Symbol,
261     span: Span,
262     pattern_span: Span,
263     init: Option<&'tcx Expr<'_>>,
264     prev_span: Span,
265 ) {
266     if let Some(expr) = init {
267         if is_self_shadow(name, expr) {
268             span_lint_and_then(
269                 cx,
270                 SHADOW_SAME,
271                 span,
272                 &format!(
273                     "`{}` is shadowed by itself in `{}`",
274                     snippet(cx, pattern_span, "_"),
275                     snippet(cx, expr.span, "..")
276                 ),
277                 |diag| {
278                     diag.span_note(prev_span, "previous binding is here");
279                 },
280             );
281         } else if contains_name(name, expr) {
282             span_lint_and_then(
283                 cx,
284                 SHADOW_REUSE,
285                 pattern_span,
286                 &format!(
287                     "`{}` is shadowed by `{}` which reuses the original value",
288                     snippet(cx, pattern_span, "_"),
289                     snippet(cx, expr.span, "..")
290                 ),
291                 |diag| {
292                     diag.span_note(expr.span, "initialization happens here");
293                     diag.span_note(prev_span, "previous binding is here");
294                 },
295             );
296         } else {
297             span_lint_and_then(
298                 cx,
299                 SHADOW_UNRELATED,
300                 pattern_span,
301                 &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")),
302                 |diag| {
303                     diag.span_note(expr.span, "initialization happens here");
304                     diag.span_note(prev_span, "previous binding is here");
305                 },
306             );
307         }
308     } else {
309         span_lint_and_then(
310             cx,
311             SHADOW_UNRELATED,
312             span,
313             &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
314             |diag| {
315                 diag.span_note(prev_span, "previous binding is here");
316             },
317         );
318     }
319 }
320
321 fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) {
322     if in_external_macro(cx.sess(), expr.span) {
323         return;
324     }
325     match expr.kind {
326         ExprKind::Unary(_, ref e)
327         | ExprKind::Field(ref e, _)
328         | ExprKind::AddrOf(_, _, ref e)
329         | ExprKind::Box(ref e) => check_expr(cx, e, bindings),
330         ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => check_block(cx, block, bindings),
331         // ExprKind::Call
332         // ExprKind::MethodCall
333         ExprKind::Array(v) | ExprKind::Tup(v) => {
334             for e in v {
335                 check_expr(cx, e, bindings)
336             }
337         },
338         ExprKind::If(ref cond, ref then, ref otherwise) => {
339             check_expr(cx, cond, bindings);
340             check_expr(cx, &**then, bindings);
341             if let Some(ref o) = *otherwise {
342                 check_expr(cx, o, bindings);
343             }
344         },
345         ExprKind::Match(ref init, arms, _) => {
346             check_expr(cx, init, bindings);
347             let len = bindings.len();
348             for arm in arms {
349                 check_pat(cx, &arm.pat, Some(&**init), arm.pat.span, bindings);
350                 // This is ugly, but needed to get the right type
351                 if let Some(ref guard) = arm.guard {
352                     match guard {
353                         Guard::If(if_expr) => check_expr(cx, if_expr, bindings),
354                         Guard::IfLet(guard_pat, guard_expr) => {
355                             check_pat(cx, guard_pat, Some(*guard_expr), guard_pat.span, bindings);
356                             check_expr(cx, guard_expr, bindings);
357                         },
358                     }
359                 }
360                 check_expr(cx, &arm.body, bindings);
361                 bindings.truncate(len);
362             }
363         },
364         _ => (),
365     }
366 }
367
368 fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) {
369     match ty.kind {
370         TyKind::Slice(ref sty) => check_ty(cx, sty, bindings),
371         TyKind::Array(ref fty, ref anon_const) => {
372             check_ty(cx, fty, bindings);
373             check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings);
374         },
375         TyKind::Ptr(MutTy { ty: ref mty, .. }) | TyKind::Rptr(_, MutTy { ty: ref mty, .. }) => {
376             check_ty(cx, mty, bindings)
377         },
378         TyKind::Tup(tup) => {
379             for t in tup {
380                 check_ty(cx, t, bindings)
381             }
382         },
383         TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings),
384         _ => (),
385     }
386 }
387
388 fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
389     match expr.kind {
390         ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner),
391         ExprKind::Block(ref block, _) => {
392             block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e))
393         },
394         ExprKind::Unary(op, ref inner) => (UnOp::Deref == op) && is_self_shadow(name, inner),
395         ExprKind::Path(QPath::Resolved(_, ref path)) => path_eq_name(name, path),
396         _ => false,
397     }
398 }
399
400 fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
401     !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name
402 }