-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix dogfood lints in `redundant_local` keep `redundant_local` from running in proc macros rewrite `redundant_local` as late pass
- Loading branch information
1 parent
384cf37
commit c3d756f
Showing
13 changed files
with
383 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use clippy_utils::{diagnostics::span_lint_and_help, is_from_proc_macro, ty::needs_ordered_drop}; | ||
use rustc_hir::{def::Res, BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; | ||
use rustc_lint::{LateContext, LateLintPass, LintContext}; | ||
use rustc_middle::lint::in_external_macro; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
use rustc_span::symbol::Ident; | ||
|
||
declare_clippy_lint! { | ||
/// ### What it does | ||
/// Checks for redundant redefinitions of local bindings. | ||
/// | ||
/// ### Why is this bad? | ||
/// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended. | ||
/// | ||
/// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation. | ||
/// | ||
/// ### Example | ||
/// ```rust | ||
/// let a = 0; | ||
/// let a = a; | ||
/// | ||
/// fn foo(b: i32) { | ||
/// let b = b; | ||
/// } | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// let a = 0; | ||
/// // no redefinition with the same name | ||
/// | ||
/// fn foo(b: i32) { | ||
/// // no redefinition with the same name | ||
/// } | ||
/// ``` | ||
#[clippy::version = "1.72.0"] | ||
pub REDUNDANT_LOCAL, | ||
correctness, | ||
"redundant redefinition of a local binding" | ||
} | ||
declare_lint_pass!(RedundantLocal => [REDUNDANT_LOCAL]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for RedundantLocal { | ||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { | ||
if_chain! { | ||
// the pattern is a single by-value binding | ||
if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind; | ||
// the binding is not type-ascribed | ||
if local.ty.is_none(); | ||
// the expression is a single-segment path | ||
if let Some(expr) = local.init; | ||
if let ExprKind::Path(qpath @ QPath::Resolved(None, path)) = expr.kind; | ||
// the path is a single segment equal to the local's name | ||
if path.segments.len() == 1; | ||
if path.segments[0].ident == ident; | ||
// resolve the path to its defining binding pattern | ||
if let Res::Local(binding_id) = cx.qpath_res(&qpath, expr.hir_id); | ||
if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id); | ||
// the previous binding has the same mutability | ||
if find_binding(binding_pat, ident).unwrap().1 == mutability; | ||
// the local does not affect the code's drop behavior | ||
if !affects_drop_behavior(cx, binding_id, local.hir_id, expr); | ||
// the local is user-controlled | ||
if !in_external_macro(cx.sess(), local.span); | ||
if !is_from_proc_macro(cx, expr); | ||
then { | ||
span_lint_and_help( | ||
cx, | ||
REDUNDANT_LOCAL, | ||
vec![binding_pat.span, local.span], | ||
"redundant redefinition of a binding", | ||
None, | ||
&format!("remove the redefinition of `{ident}`"), | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Find the annotation of a binding introduced by a pattern, or `None` if it's not introduced. | ||
fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingAnnotation> { | ||
match pat.kind { | ||
PatKind::Binding(annotation, _, ident, _) if ident == name => Some(annotation), | ||
PatKind::Binding(_, _, _, Some(subpat)) => find_binding(subpat, name), | ||
PatKind::Struct(_, fields, _) => fields.iter().find_map(|field| find_binding(field.pat, name)), | ||
PatKind::TupleStruct(_, fields, _) | PatKind::Or(fields) | PatKind::Tuple(fields, _) => { | ||
fields.iter().find_map(|field| find_binding(field, name)) | ||
}, | ||
PatKind::Slice(before, middle, after) => before | ||
.iter() | ||
.chain(middle) | ||
.chain(after.iter()) | ||
.find_map(|field| find_binding(field, name)), | ||
PatKind::Box(inner) | PatKind::Ref(inner, _) => find_binding(inner, name), | ||
PatKind::Wild | ||
| PatKind::Binding(_, _, _, None) | ||
| PatKind::Path(_) | ||
| PatKind::Lit(_) | ||
| PatKind::Range(_, _, _) => None, | ||
} | ||
} | ||
|
||
/// Check if a rebinding of a local affects the code's drop behavior. | ||
fn affects_drop_behavior<'tcx>(cx: &LateContext<'tcx>, bind: HirId, rebind: HirId, rebind_expr: &Expr<'tcx>) -> bool { | ||
let hir = cx.tcx.hir(); | ||
|
||
// the rebinding is in a different scope than the original binding | ||
// and the type of the binding cares about drop order | ||
hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind) | ||
&& needs_ordered_drop(cx, cx.typeck_results().expr_ty(rebind_expr)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.