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;
6 Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
9 use rustc_lint::{LateContext, LateLintPass, LintContext};
10 use rustc_middle::lint::in_external_macro;
12 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 use rustc_span::source_map::Span;
14 use rustc_span::symbol::Symbol;
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.
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
24 /// **Known problems:** This lint, as the other shadowing related lints,
25 /// currently only catches very simple patterns.
34 /// let y = &x; // use different variable name
38 "rebinding a name to itself, e.g., `let mut x = &mut x`"
41 declare_clippy_lint! {
42 /// **What it does:** Checks for bindings that shadow other bindings already in
43 /// scope, while reusing the original value.
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
50 /// **Known problems:** This lint, as the other shadowing related lints,
51 /// currently only catches very simple patterns.
58 /// use different variable name:
65 "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
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.
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.
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
90 /// let x = z; // shadows the earlier binding
93 /// let w = z; // use different variable name
97 "rebinding a name without even using the original value"
100 declare_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
102 impl<'tcx> LateLintPass<'tcx> for Shadow {
105 cx: &LateContext<'tcx>,
107 decl: &'tcx FnDecl<'_>,
108 body: &'tcx Body<'_>,
112 if in_external_macro(cx.sess(), body.value.span) {
115 check_fn(cx, decl, body);
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))
126 check_expr(cx, &body.value, &mut bindings);
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 {
133 StmtKind::Local(local) => check_local(cx, local, bindings),
134 StmtKind::Expr(e) | StmtKind::Semi(e) => check_expr(cx, e, bindings),
135 StmtKind::Item(..) => {},
138 if let Some(o) = block.expr {
139 check_expr(cx, o, bindings);
141 bindings.truncate(len);
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) {
148 if higher::is_from_for_desugar(local) {
158 if let Some(t) = *ty {
159 check_ty(cx, t, bindings)
161 if let Some(o) = *init {
162 check_expr(cx, o, bindings);
163 check_pat(cx, pat, Some(o), span, bindings);
165 check_pat(cx, pat, None, span, bindings);
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(..)))
175 cx: &LateContext<'tcx>,
177 init: Option<&'tcx Expr<'_>>,
179 bindings: &mut Vec<(Symbol, Span)>,
181 // TODO: match more stuff / destructuring
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() {
189 lint_shadow(cx, name, span, pat.span, init, tup.1);
196 bindings.push((name, ident.span));
199 if let Some(p) = *inner {
200 check_pat(cx, p, init, span, bindings);
203 PatKind::Struct(_, pfields, _) => {
204 if let Some(init_struct) = init {
205 if let ExprKind::Struct(_, efields, _) = init_struct.kind {
206 for field in pfields {
207 let name = field.ident.name;
210 .find_map(|f| if f.ident.name == name { Some(&*f.expr) } else { None });
211 check_pat(cx, field.pat, efield, span, bindings);
214 for field in pfields {
215 check_pat(cx, field.pat, init, span, bindings);
219 for field in pfields {
220 check_pat(cx, field.pat, None, span, bindings);
224 PatKind::Tuple(inner, _) => {
225 if let Some(init_tup) = init {
226 if let ExprKind::Tup(tup) = init_tup.kind {
227 for (i, p) in inner.iter().enumerate() {
228 check_pat(cx, p, Some(&tup[i]), p.span, bindings);
232 check_pat(cx, p, init, span, bindings);
237 check_pat(cx, p, None, span, bindings);
241 PatKind::Box(inner) => {
242 if let Some(initp) = init {
243 if let ExprKind::Box(inner_init) = initp.kind {
244 check_pat(cx, inner, Some(inner_init), span, bindings);
246 check_pat(cx, inner, init, span, bindings);
249 check_pat(cx, inner, init, span, bindings);
252 PatKind::Ref(inner, _) => check_pat(cx, inner, init, span, bindings),
253 // PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
258 fn lint_shadow<'tcx>(
259 cx: &LateContext<'tcx>,
263 init: Option<&'tcx Expr<'_>>,
266 if let Some(expr) = init {
267 if is_self_shadow(name, expr) {
273 "`{}` is shadowed by itself in `{}`",
274 snippet(cx, pattern_span, "_"),
275 snippet(cx, expr.span, "..")
278 diag.span_note(prev_span, "previous binding is here");
281 } else if contains_name(name, expr) {
287 "`{}` is shadowed by `{}` which reuses the original value",
288 snippet(cx, pattern_span, "_"),
289 snippet(cx, expr.span, "..")
292 diag.span_note(expr.span, "initialization happens here");
293 diag.span_note(prev_span, "previous binding is here");
301 &format!("`{}` is being shadowed", snippet(cx, pattern_span, "_")),
303 diag.span_note(expr.span, "initialization happens here");
304 diag.span_note(prev_span, "previous binding is here");
313 &format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
315 diag.span_note(prev_span, "previous binding is here");
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) {
326 ExprKind::Unary(_, e) | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Box(e) => {
327 check_expr(cx, e, bindings)
329 ExprKind::Block(block, _) | ExprKind::Loop(block, ..) => check_block(cx, block, bindings),
331 // ExprKind::MethodCall
332 ExprKind::Array(v) | ExprKind::Tup(v) => {
334 check_expr(cx, e, bindings)
337 ExprKind::If(cond, then, ref otherwise) => {
338 check_expr(cx, cond, bindings);
339 check_expr(cx, then, bindings);
340 if let Some(o) = *otherwise {
341 check_expr(cx, o, bindings);
344 ExprKind::Match(init, arms, _) => {
345 check_expr(cx, init, bindings);
346 let len = bindings.len();
348 check_pat(cx, arm.pat, Some(init), arm.pat.span, bindings);
349 // This is ugly, but needed to get the right type
350 if let Some(ref guard) = arm.guard {
352 Guard::If(if_expr) => check_expr(cx, if_expr, bindings),
353 Guard::IfLet(guard_pat, guard_expr) => {
354 check_pat(cx, guard_pat, Some(*guard_expr), guard_pat.span, bindings);
355 check_expr(cx, guard_expr, bindings);
359 check_expr(cx, arm.body, bindings);
360 bindings.truncate(len);
367 fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) {
369 TyKind::Slice(sty) => check_ty(cx, sty, bindings),
370 TyKind::Array(fty, ref anon_const) => {
371 check_ty(cx, fty, bindings);
372 check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings);
374 TyKind::Ptr(MutTy { ty: mty, .. }) | TyKind::Rptr(_, MutTy { ty: mty, .. }) => check_ty(cx, mty, bindings),
375 TyKind::Tup(tup) => {
377 check_ty(cx, t, bindings)
380 TyKind::Typeof(ref anon_const) => check_expr(cx, &cx.tcx.hir().body(anon_const.body).value, bindings),
385 fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
387 ExprKind::Box(inner) | ExprKind::AddrOf(_, _, inner) => is_self_shadow(name, inner),
388 ExprKind::Block(block, _) => {
389 block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e))
391 ExprKind::Unary(op, inner) => (UnOp::Deref == op) && is_self_shadow(name, inner),
392 ExprKind::Path(QPath::Resolved(_, path)) => path_eq_name(name, path),
397 fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
398 !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.name == name