]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/shadow.rs
Update upstream source from tag 'upstream/1.52.1+dfsg1'
[rustc.git] / src / tools / clippy / clippy_lints / src / shadow.rs
CommitLineData
f20569fa
XL
1use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
2use rustc_hir::intravisit::FnKind;
3use rustc_hir::{
4 Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
5 UnOp,
6};
7use rustc_lint::{LateContext, LateLintPass, LintContext};
8use rustc_middle::lint::in_external_macro;
9use rustc_middle::ty;
10use rustc_session::{declare_lint_pass, declare_tool_lint};
11use rustc_span::source_map::Span;
12use rustc_span::symbol::Symbol;
13
14declare_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
39declare_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
66declare_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
98declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
99
100impl<'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
117fn 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
127fn 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
142fn 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
167fn 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
172fn 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
256fn 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
319fn 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
366fn 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
386fn 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
398fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
399 !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name
400}