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