summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/swap.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/swap.rs')
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs116
1 files changed, 68 insertions, 48 deletions
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 0f062cecf..f7eef03d1 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -1,15 +1,17 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
+use rustc_span::SyntaxContext;
use rustc_span::{sym, symbol::Ident, Span};
declare_clippy_lint! {
@@ -80,43 +82,45 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
}
fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
+ let ctxt = span.ctxt();
let mut applicability = Applicability::MachineApplicable;
if !can_mut_borrow_both(cx, e1, e2) {
- if let ExprKind::Index(lhs1, idx1) = e1.kind {
- if let ExprKind::Index(lhs2, idx2) = e2.kind {
- if eq_expr_value(cx, lhs1, lhs2) {
- let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
+ if let ExprKind::Index(lhs1, idx1) = e1.kind
+ && let ExprKind::Index(lhs2, idx2) = e2.kind
+ && eq_expr_value(cx, lhs1, lhs2)
+ && e1.span.ctxt() == ctxt
+ && e2.span.ctxt() == ctxt
+ {
+ let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
- if matches!(ty.kind(), ty::Slice(_))
- || matches!(ty.kind(), ty::Array(_, _))
- || is_type_diagnostic_item(cx, ty, sym::Vec)
- || is_type_diagnostic_item(cx, ty, sym::VecDeque)
- {
- let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
- span_lint_and_sugg(
- cx,
- MANUAL_SWAP,
- span,
- &format!("this looks like you are swapping elements of `{slice}` manually"),
- "try",
- format!(
- "{}.swap({}, {})",
- slice.maybe_par(),
- snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
- snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
- ),
- applicability,
- );
- }
- }
+ if matches!(ty.kind(), ty::Slice(_))
+ || matches!(ty.kind(), ty::Array(_, _))
+ || is_type_diagnostic_item(cx, ty, sym::Vec)
+ || is_type_diagnostic_item(cx, ty, sym::VecDeque)
+ {
+ let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ MANUAL_SWAP,
+ span,
+ &format!("this looks like you are swapping elements of `{slice}` manually"),
+ "try",
+ format!(
+ "{}.swap({}, {});",
+ slice.maybe_par(),
+ snippet_with_context(cx, idx1.span, ctxt, "..", &mut applicability).0,
+ snippet_with_context(cx, idx2.span, ctxt, "..", &mut applicability).0,
+ ),
+ applicability,
+ );
}
}
return;
}
- let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
- let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+ let first = Sugg::hir_with_context(cx, e1, ctxt, "..", &mut applicability);
+ let second = Sugg::hir_with_context(cx, e2, ctxt, "..", &mut applicability);
let Some(sugg) = std_or_core(cx) else { return };
span_lint_and_then(
@@ -128,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa
diag.span_suggestion(
span,
"try",
- format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+ format!("{sugg}::mem::swap({}, {});", first.mut_addr(), second.mut_addr()),
applicability,
);
if !is_xor_based {
@@ -144,19 +148,19 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
return;
}
- for w in block.stmts.windows(3) {
+ for [s1, s2, s3] in block.stmts.array_windows::<3>() {
if_chain! {
// let t = foo();
- if let StmtKind::Local(tmp) = w[0].kind;
+ if let StmtKind::Local(tmp) = s1.kind;
if let Some(tmp_init) = tmp.init;
if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
// foo() = bar();
- if let StmtKind::Semi(first) = w[1].kind;
+ if let StmtKind::Semi(first) = s2.kind;
if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
// bar() = t;
- if let StmtKind::Semi(second) = w[2].kind;
+ if let StmtKind::Semi(second) = s3.kind;
if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
if rhs2.segments.len() == 1;
@@ -164,8 +168,15 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if ident.name == rhs2.segments[0].ident.name;
if eq_expr_value(cx, tmp_init, lhs1);
if eq_expr_value(cx, rhs1, lhs2);
+
+ let ctxt = s1.span.ctxt();
+ if s2.span.ctxt() == ctxt;
+ if s3.span.ctxt() == ctxt;
+ if first.span.ctxt() == ctxt;
+ if second.span.ctxt() == ctxt;
+
then {
- let span = w[0].span.to(second.span);
+ let span = s1.span.to(s3.span);
generate_swap_warning(cx, lhs1, lhs2, span, false);
}
}
@@ -178,8 +189,10 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if let Some((lhs0, rhs0)) = parse(first)
&& let Some((lhs1, rhs1)) = parse(second)
&& first.span.eq_ctxt(second.span)
+ && !in_external_macro(cx.sess(), first.span)
&& is_same(cx, lhs0, rhs1)
&& is_same(cx, lhs1, rhs0)
+ && !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421)
&& let Some(lhs_sugg) = match &lhs0 {
ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr),
ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())),
@@ -246,17 +259,20 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
/// Implementation of the xor case for `MANUAL_SWAP` lint.
fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
- for window in block.stmts.windows(3) {
+ for [s1, s2, s3] in block.stmts.array_windows::<3>() {
+ let ctxt = s1.span.ctxt();
if_chain! {
- if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
- if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
- if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
+ if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt);
+ if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt);
+ if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt);
if eq_expr_value(cx, lhs0, rhs1);
if eq_expr_value(cx, lhs2, rhs1);
if eq_expr_value(cx, lhs1, rhs0);
if eq_expr_value(cx, lhs1, rhs2);
+ if s2.span.ctxt() == ctxt;
+ if s3.span.ctxt() == ctxt;
then {
- let span = window[0].span.to(window[2].span);
+ let span = s1.span.to(s3.span);
generate_swap_warning(cx, lhs0, rhs0, span, true);
}
};
@@ -264,9 +280,12 @@ fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
}
/// Returns the lhs and rhs of an xor assignment statement.
-fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
- if let StmtKind::Semi(expr) = stmt.kind {
- if let ExprKind::AssignOp(
+fn extract_sides_of_xor_assign<'a, 'hir>(
+ stmt: &'a Stmt<'hir>,
+ ctxt: SyntaxContext,
+) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
+ if let StmtKind::Semi(expr) = stmt.kind
+ && let ExprKind::AssignOp(
Spanned {
node: BinOpKind::BitXor,
..
@@ -274,9 +293,10 @@ fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Ex
lhs,
rhs,
) = expr.kind
- {
- return Some((lhs, rhs));
- }
+ && expr.span.ctxt() == ctxt
+ {
+ Some((lhs, rhs))
+ } else {
+ None
}
- None
}