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