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.rs107
1 files changed, 64 insertions, 43 deletions
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 17e9cc5f6..0f062cecf 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
-use rustc_span::{sym, Span};
+use rustc_span::{sym, symbol::Ident, Span};
declare_clippy_lint! {
/// ### What it does
@@ -174,53 +174,74 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
/// Implementation of the `ALMOST_SWAPPED` lint.
fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
- for w in block.stmts.windows(2) {
- if_chain! {
- if let StmtKind::Semi(first) = w[0].kind;
- if let StmtKind::Semi(second) = w[1].kind;
- if first.span.ctxt() == second.span.ctxt();
- if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
- if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
- if eq_expr_value(cx, lhs0, rhs1);
- if eq_expr_value(cx, lhs1, rhs0);
- then {
- let lhs0 = Sugg::hir_opt(cx, lhs0);
- let rhs0 = Sugg::hir_opt(cx, rhs0);
- let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
- (
- format!(" `{first}` and `{second}`"),
- first.mut_addr().to_string(),
- second.mut_addr().to_string(),
- )
- } else {
- (String::new(), String::new(), String::new())
- };
+ for [first, second] in block.stmts.array_windows() {
+ if let Some((lhs0, rhs0)) = parse(first)
+ && let Some((lhs1, rhs1)) = parse(second)
+ && first.span.eq_ctxt(second.span)
+ && is_same(cx, lhs0, rhs1)
+ && is_same(cx, lhs1, rhs0)
+ && let Some(lhs_sugg) = match &lhs0 {
+ ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr),
+ ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())),
+ }
+ && let Some(rhs_sugg) = Sugg::hir_opt(cx, rhs0)
+ {
+ let span = first.span.to(rhs1.span);
+ let Some(sugg) = std_or_core(cx) else { return };
+ span_lint_and_then(
+ cx,
+ ALMOST_SWAPPED,
+ span,
+ &format!("this looks like you are trying to swap `{lhs_sugg}` and `{rhs_sugg}`"),
+ |diag| {
+ diag.span_suggestion(
+ span,
+ "try",
+ format!("{sugg}::mem::swap({}, {})", lhs_sugg.mut_addr(), rhs_sugg.mut_addr()),
+ Applicability::MaybeIncorrect,
+ );
+ diag.note(format!("or maybe you should use `{sugg}::mem::replace`?"));
+ },
+ );
+ }
+ }
+}
+
+fn is_same(cx: &LateContext<'_>, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool {
+ match lhs {
+ ExprOrIdent::Expr(expr) => eq_expr_value(cx, expr, rhs),
+ ExprOrIdent::Ident(ident) => {
+ if let ExprKind::Path(QPath::Resolved(None, path)) = rhs.kind
+ && let [segment] = &path.segments
+ && segment.ident == ident
+ {
+ true
+ } else {
+ false
+ }
+ }
+ }
+}
- let span = first.span.to(second.span);
- let Some(sugg) = std_or_core(cx) else { return };
+#[derive(Debug, Clone, Copy)]
+enum ExprOrIdent<'a> {
+ Expr(&'a Expr<'a>),
+ Ident(Ident),
+}
- span_lint_and_then(cx,
- ALMOST_SWAPPED,
- span,
- &format!("this looks like you are trying to swap{what}"),
- |diag| {
- if !what.is_empty() {
- diag.span_suggestion(
- span,
- "try",
- format!(
- "{sugg}::mem::swap({lhs}, {rhs})",
- ),
- Applicability::MaybeIncorrect,
- );
- diag.note(
- format!("or maybe you should use `{sugg}::mem::replace`?")
- );
- }
- });
+fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<'hir>)> {
+ if let StmtKind::Semi(expr) = stmt.kind {
+ if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
+ return Some((ExprOrIdent::Expr(lhs), rhs));
+ }
+ } else if let StmtKind::Local(expr) = stmt.kind {
+ if let Some(rhs) = expr.init {
+ if let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind {
+ return Some((ExprOrIdent::Ident(ident_l), rhs));
}
}
}
+ None
}
/// Implementation of the xor case for `MANUAL_SWAP` lint.