diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index ed963dc3e90e..5446148b0f9d 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -297,8 +297,9 @@ impl<'v> Hir2Qmm<'_, '_, 'v> { return Err("contains never type".to_owned()); } + let ctxt = e.span.ctxt(); for (n, expr) in self.terminals.iter().enumerate() { - if eq_expr_value(self.cx, e, expr) { + if eq_expr_value(self.cx, ctxt, e, expr) { #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -307,8 +308,8 @@ impl<'v> Hir2Qmm<'_, '_, 'v> { && implements_ord(self.cx, e_lhs) && let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind && negate(e_binop.node) == Some(expr_binop.node) - && eq_expr_value(self.cx, e_lhs, expr_lhs) - && eq_expr_value(self.cx, e_rhs, expr_rhs) + && eq_expr_value(self.cx, ctxt, e_lhs, expr_lhs) + && eq_expr_value(self.cx, ctxt, e_rhs, expr_rhs) { #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 5e9009c67197..c80930f0b443 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::Symbol; +use rustc_span::{Symbol, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -62,7 +62,8 @@ impl LateLintPass<'_> for CheckedConversions { }, _ => return, } - && !item.span.in_external_macro(cx.sess().source_map()) + && let ctxt = item.span.ctxt() + && !ctxt.in_external_macro(cx.sess().source_map()) && !is_in_const_context(cx) && let Some(cv) = match op2 { // todo: check for case signed -> larger unsigned == only x >= 0 @@ -71,7 +72,7 @@ impl LateLintPass<'_> for CheckedConversions { let upper_lower = |lt1, gt1, lt2, gt2| { check_upper_bound(lt1, gt1) .zip(check_lower_bound(lt2, gt2)) - .and_then(|(l, r)| l.combine(r, cx)) + .and_then(|(l, r)| l.combine(r, cx, ctxt)) }; upper_lower(lt1, gt1, lt2, gt2).or_else(|| upper_lower(lt2, gt2, lt1, gt1)) }, @@ -126,8 +127,8 @@ fn read_le_ge<'tcx>( impl<'a> Conversion<'a> { /// Combine multiple conversions if the are compatible - pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option> { - if self.is_compatible(&other, cx) { + pub fn combine(self, other: Self, cx: &LateContext<'_>, ctxt: SyntaxContext) -> Option> { + if self.is_compatible(&other, cx, ctxt) { // Prefer a Conversion that contains a type-constraint Some(if self.to_type.is_some() { self } else { other }) } else { @@ -137,9 +138,9 @@ impl<'a> Conversion<'a> { /// Checks if two conversions are compatible /// same type of conversion, same 'castee' and same 'to type' - pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_>) -> bool { + pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_>, ctxt: SyntaxContext) -> bool { (self.cvt == other.cvt) - && (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast)) + && (SpanlessEq::new(cx).eq_expr(ctxt, self.expr_to_cast, other.expr_to_cast)) && (self.has_compatible_to_type(other)) } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index a2ddf3dad7a2..554f1ab18685 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_span::{SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does @@ -90,8 +90,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { // Check that both sets of operands are equal let mut spanless_eq = SpanlessEq::new(cx); - let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2); - let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2); + let same_fixed_operands = spanless_eq.eq_expr(SyntaxContext::root(), lhs1, lhs2) + && spanless_eq.eq_expr(SyntaxContext::root(), rhs1, rhs2); + let same_transposed_operands = spanless_eq.eq_expr(SyntaxContext::root(), lhs1, rhs2) + && spanless_eq.eq_expr(SyntaxContext::root(), rhs1, lhs2); if !same_fixed_operands && !same_transposed_operands { return; diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index f700cb677343..a24cc957df07 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -498,11 +498,11 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } match try_parse_insert(self.cx, expr) { - Some(insert_expr) if self.spanless_eq.eq_expr(self.map, insert_expr.map) => { + Some(insert_expr) if self.spanless_eq.eq_expr(self.ctxt, self.map, insert_expr.map) => { self.visit_insert_expr_arguments(&insert_expr); // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api. if self.is_map_used - || !self.spanless_eq.eq_expr(self.key, insert_expr.key) + || !self.spanless_eq.eq_expr(self.ctxt, self.key, insert_expr.key) || expr.span.ctxt() != self.ctxt { self.can_use_entry = false; @@ -521,10 +521,10 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { self.visit_non_tail_expr(insert_expr.value); self.is_single_insert = is_single_insert; }, - _ if is_any_expr_in_map_used(self.cx, &mut self.spanless_eq, self.map, expr) => { + _ if is_any_expr_in_map_used(self.cx, &mut self.spanless_eq, self.ctxt, self.map, expr) => { self.is_map_used = true; }, - _ if self.spanless_eq.eq_expr(self.key, expr) => { + _ if self.spanless_eq.eq_expr(self.ctxt, self.key, expr) => { self.is_key_used = true; }, _ => match expr.kind { @@ -600,11 +600,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { fn is_any_expr_in_map_used<'tcx>( cx: &LateContext<'tcx>, spanless_eq: &mut SpanlessEq<'_, 'tcx>, + ctxt: SyntaxContext, map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>, ) -> bool { for_each_expr(cx, map, |e| { - if spanless_eq.eq_expr(e, expr) { + if spanless_eq.eq_expr(ctxt, e, expr) { return ControlFlow::Break(()); } ControlFlow::Continue(()) diff --git a/clippy_lints/src/floating_point_arithmetic/custom_abs.rs b/clippy_lints/src/floating_point_arithmetic/custom_abs.rs index f476abae708d..feff92fbf876 100644 --- a/clippy_lints/src/floating_point_arithmetic/custom_abs.rs +++ b/clippy_lints/src/floating_point_arithmetic/custom_abs.rs @@ -14,11 +14,15 @@ use super::SUBOPTIMAL_FLOPS; /// test is positive or an expression which tests whether or not test /// is nonnegative. /// Used for check-custom-abs function below -fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { +fn is_testing_positive(cx: &LateContext<'_>, ctxt: SyntaxContext, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => { + is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, ctxt, left, test) + }, + BinOpKind::Lt | BinOpKind::Le => { + is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, ctxt, right, test) + }, _ => false, } } else { @@ -27,11 +31,15 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - } /// See [`is_testing_positive`] -fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool { +fn is_testing_negative(cx: &LateContext<'_>, ctxt: SyntaxContext, expr: &Expr<'_>, test: &Expr<'_>) -> bool { if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind { match op { - BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => { + is_zero(cx, left, expr.span.ctxt()) && eq_expr_value(cx, ctxt, right, test) + }, + BinOpKind::Lt | BinOpKind::Le => { + is_zero(cx, right, expr.span.ctxt()) && eq_expr_value(cx, ctxt, left, test) + }, _ => false, } } else { @@ -55,14 +63,21 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> bool { /// one of the two expressions /// If the two expressions are not negations of each other, then it /// returns None. -fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> { +fn are_negated<'a>( + cx: &LateContext<'_>, + ctxt: SyntaxContext, + expr1: &'a Expr<'a>, + expr2: &'a Expr<'a>, +) -> Option<(bool, &'a Expr<'a>)> { if let ExprKind::Unary(UnOp::Neg, expr1_negated) = expr1.kind - && eq_expr_value(cx, expr1_negated, expr2) + && expr1_negated.span.ctxt() == ctxt + && eq_expr_value(cx, ctxt, expr1_negated, expr2) { return Some((false, expr2)); } if let ExprKind::Unary(UnOp::Neg, expr2_negated) = expr2.kind - && eq_expr_value(cx, expr1, expr2_negated) + && expr2_negated.span.ctxt() == ctxt + && eq_expr_value(cx, ctxt, expr1, expr2_negated) { return Some((true, expr1)); } @@ -77,11 +92,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { }) = higher::If::hir(expr) && let if_body_expr = peel_blocks(then) && let else_body_expr = peel_blocks(r#else) - && let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr) + && let ctxt = expr.span.ctxt() + && let Some((if_expr_positive, body)) = are_negated(cx, ctxt, if_body_expr, else_body_expr) { - let sugg_positive_abs = if is_testing_positive(cx, cond, body) { + let sugg_positive_abs = if is_testing_positive(cx, ctxt, cond, body) { if_expr_positive - } else if is_testing_negative(cx, cond, body) { + } else if is_testing_negative(cx, ctxt, cond, body) { !if_expr_positive } else { return; diff --git a/clippy_lints/src/floating_point_arithmetic/hypot.rs b/clippy_lints/src/floating_point_arithmetic/hypot.rs index 8d3bd8084db8..f9b42761f3a7 100644 --- a/clippy_lints/src/floating_point_arithmetic/hypot.rs +++ b/clippy_lints/src/floating_point_arithmetic/hypot.rs @@ -19,6 +19,7 @@ pub(super) fn detect(cx: &LateContext<'_>, receiver: &Expr<'_>, app: &mut Applic add_rhs, ) = receiver.kind { + let ctxt = receiver.span.ctxt(); // check if expression of the form x * x + y * y if let ExprKind::Binary( Spanned { @@ -34,8 +35,8 @@ pub(super) fn detect(cx: &LateContext<'_>, receiver: &Expr<'_>, app: &mut Applic rmul_lhs, rmul_rhs, ) = add_rhs.kind - && eq_expr_value(cx, lmul_lhs, lmul_rhs) - && eq_expr_value(cx, rmul_lhs, rmul_rhs) + && eq_expr_value(cx, ctxt, lmul_lhs, lmul_rhs) + && eq_expr_value(cx, ctxt, rmul_lhs, rmul_rhs) { return Some(format!( "{}.hypot({})", diff --git a/clippy_lints/src/floating_point_arithmetic/log_division.rs b/clippy_lints/src/floating_point_arithmetic/log_division.rs index 947935369de1..ad0038a4c242 100644 --- a/clippy_lints/src/floating_point_arithmetic/log_division.rs +++ b/clippy_lints/src/floating_point_arithmetic/log_division.rs @@ -4,18 +4,18 @@ use clippy_utils::{eq_expr_value, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment}; use rustc_lint::LateContext; -use rustc_span::Spanned; +use rustc_span::{Spanned, SyntaxContext}; use super::SUBOPTIMAL_FLOPS; -fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { +fn are_same_base_logs(cx: &LateContext<'_>, ctxt: SyntaxContext, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { if let ExprKind::MethodCall(PathSegment { ident: method_a, .. }, _, args_a, _) = expr_a.kind && let ExprKind::MethodCall(PathSegment { ident: method_b, .. }, _, args_b, _) = expr_b.kind { return method_a.name == method_b.name && args_a.len() == args_b.len() && (matches!(method_a.name, sym::ln | sym::log2 | sym::log10) - || method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])); + || method_a.name == sym::log && args_a.len() == 1 && eq_expr_value(cx, ctxt, &args_a[0], &args_b[0])); } false @@ -30,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { lhs, rhs, ) = expr.kind - && are_same_base_logs(cx, lhs, rhs) + && are_same_base_logs(cx, expr.span.ctxt(), lhs, rhs) && let ExprKind::MethodCall(_, largs_self, ..) = lhs.kind && let ExprKind::MethodCall(_, rargs_self, ..) = rhs.kind { diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index eed2d5d88535..38fda71fccf2 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -7,6 +7,7 @@ use rustc_errors::Diag; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use rustc_span::edition::Edition::Edition2024; declare_clippy_lint! { @@ -61,9 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { if_else: Some(if_else), .. }) = higher::IfLet::hir(cx, expr) - && let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None)) + && let ctxt = expr.span.ctxt() + && let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, ctxt, e, None)) && let Some(arm_mutex) = - for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex))) + for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, ctxt, e, Some(op_mutex))) { let diag = |diag: &mut Diag<'_, ()>| { diag.span_label( @@ -89,6 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { fn mutex_lock_call<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, expr: &'tcx Expr<'_>, op_mutex: Option<&'tcx Expr<'_>>, ) -> ControlFlow<&'tcx Expr<'tcx>> { @@ -96,7 +99,7 @@ fn mutex_lock_call<'tcx>( && path.ident.name == sym::lock && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() && ty.is_diag_item(cx, sym::Mutex) - && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op)) + && op_mutex.is_none_or(|op| eq_expr_value(cx, ctxt, self_arg, op)) { ControlFlow::Break(self_arg) } else { diff --git a/clippy_lints/src/ifs/branches_sharing_code.rs b/clippy_lints/src/ifs/branches_sharing_code.rs index b6e8d047c5cd..06ebd3ac7f5f 100644 --- a/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/clippy_lints/src/ifs/branches_sharing_code.rs @@ -13,7 +13,7 @@ use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, ItemKind, LetStmt, Node, use rustc_lint::LateContext; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, SyntaxContext}; use super::BRANCHES_SHARING_CODE; @@ -196,7 +196,12 @@ fn eq_stmts( .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(cx, s, new_bindings))) } else { true - }) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt))) + }) && blocks.iter().all(|b| { + get_stmt(b).is_some_and(|s| { + eq.set_eval_ctxt(SyntaxContext::root()); + eq.eq_stmt(s, stmt) + }) + }) } #[expect(clippy::too_many_lines)] @@ -207,7 +212,7 @@ fn scan_block_for_eq<'tcx>( blocks: &[&'tcx Block<'_>], ) -> BlockEq { let mut eq = SpanlessEq::new(cx); - let mut eq = eq.inter_expr(); + let mut eq = eq.inter_expr(SyntaxContext::root()); let mut moved_locals = Vec::new(); let mut cond_locals = HirIdSet::default(); @@ -334,6 +339,7 @@ fn scan_block_for_eq<'tcx>( }); if let Some(e) = block.expr { for block in blocks { + eq.set_eval_ctxt(SyntaxContext::root()); if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) { moved_locals.truncate(moved_locals_at_start); return BlockEq { diff --git a/clippy_lints/src/ifs/if_same_then_else.rs b/clippy_lints/src/ifs/if_same_then_else.rs index 69402ec89076..b923559e4b73 100644 --- a/clippy_lints/src/ifs/if_same_then_else.rs +++ b/clippy_lints/src/ifs/if_same_then_else.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::higher::has_let_expr; use rustc_hir::{Block, Expr}; use rustc_lint::LateContext; +use rustc_span::SyntaxContext; use super::IF_SAME_THEN_ELSE; @@ -12,7 +13,10 @@ pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block< .array_windows::<2>() .enumerate() .fold(true, |all_eq, (i, &[lhs, rhs])| { - if eq.eq_block(lhs, rhs) && !has_let_expr(conds[i]) && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) { + if eq.eq_block(SyntaxContext::root(), lhs, rhs) + && !has_let_expr(conds[i]) + && conds.get(i + 1).is_none_or(|e| !has_let_expr(e)) + { span_lint_and_note( cx, IF_SAME_THEN_ELSE, diff --git a/clippy_lints/src/ifs/ifs_same_cond.rs b/clippy_lints/src/ifs/ifs_same_cond.rs index 3ea941320a08..ac010a9314b3 100644 --- a/clippy_lints/src/ifs/ifs_same_cond.rs +++ b/clippy_lints/src/ifs/ifs_same_cond.rs @@ -4,6 +4,7 @@ use clippy_utils::ty::InteriorMut; use clippy_utils::{SpanlessEq, eq_expr_value, find_binding_init, hash_expr, search_same}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_span::SyntaxContext; use super::IFS_SAME_COND; @@ -34,10 +35,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_ if method_caller_is_mutable(cx, caller, interior_mut) { false } else { - SpanlessEq::new(cx).eq_expr(lhs, rhs) + SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), lhs, rhs) } } else { - eq_expr_value(cx, lhs, rhs) + eq_expr_value(cx, SyntaxContext::root(), lhs, rhs) } }, ) { diff --git a/clippy_lints/src/ifs/same_functions_in_if_cond.rs b/clippy_lints/src/ifs/same_functions_in_if_cond.rs index 1f6bf04e22e7..018c2141d4fc 100644 --- a/clippy_lints/src/ifs/same_functions_in_if_cond.rs +++ b/clippy_lints/src/ifs/same_functions_in_if_cond.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::{SpanlessEq, eq_expr_value, hash_expr, search_same}; use rustc_hir::Expr; use rustc_lint::LateContext; +use rustc_span::SyntaxContext; use super::SAME_FUNCTIONS_IN_IF_CONDITION; @@ -13,10 +14,10 @@ pub(super) fn check(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { return false; } // Do not spawn warning if `IFS_SAME_COND` already produced it. - if eq_expr_value(cx, lhs, rhs) { + if eq_expr_value(cx, SyntaxContext::root(), lhs, rhs) { return false; } - SpanlessEq::new(cx).eq_expr(lhs, rhs) + SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), lhs, rhs) }; for group in search_same(conds, |e| hash_expr(cx, e), eq) { diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index f0a2ad59cd28..06342113b104 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { && let ctxt = expr.span.ctxt() && ex.span.ctxt() == ctxt && cond.span.ctxt() == ctxt - && clippy_utils::SpanlessEq::new(cx).eq_expr(l, target) + && clippy_utils::SpanlessEq::new(cx).eq_expr(ctxt, l, target) && AssignOpKind::AddAssign == op1.node && let ExprKind::Lit(lit) = value.kind && let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 666c4cb737f4..718f682c3fc6 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -184,7 +184,7 @@ fn check_gt( msrv: Msrv, is_composited: bool, ) { - if is_side_effect_free(cx, big_expr) && is_side_effect_free(cx, little_expr) { + if !big_expr.can_have_side_effects() && !little_expr.can_have_side_effects() { check_subtraction( cx, condition_span, @@ -199,10 +199,6 @@ fn check_gt( } } -fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - eq_expr_value(cx, expr, expr) -} - #[expect(clippy::too_many_arguments)] fn check_subtraction( cx: &LateContext<'_>, @@ -242,7 +238,8 @@ fn check_subtraction( && let ExprKind::Binary(op, left, right) = if_block.kind && let BinOpKind::Sub = op.node { - if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) { + let ctxt = expr_span.ctxt(); + if eq_expr_value(cx, ctxt, left, big_expr) && eq_expr_value(cx, ctxt, right, little_expr) { // This part of the condition is voluntarily split from the one before to ensure that // if `snippet_opt` fails, it won't try the next conditions. if !is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST) { @@ -277,8 +274,8 @@ fn check_subtraction( }, ); } - } else if eq_expr_value(cx, left, little_expr) - && eq_expr_value(cx, right, big_expr) + } else if eq_expr_value(cx, ctxt, left, little_expr) + && eq_expr_value(cx, ctxt, right, big_expr) && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr) && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) { @@ -322,14 +319,15 @@ fn check_with_condition<'tcx>( // Extracting out the variable name && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind { + let ctxt = expr.span.ctxt(); // Handle symmetric conditions in the if statement - let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { + let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(ctxt, cond_left, target) { if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op { (cond_left, cond_right) } else { return; } - } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { + } else if SpanlessEq::new(cx).eq_expr(ctxt, cond_right, target) { if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op { (cond_right, cond_left) } else { @@ -389,7 +387,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp ExprKind::Assign(target, value, _) => { if let ExprKind::Binary(ref op1, left1, right1) = value.kind && BinOpKind::Sub == op1.node - && SpanlessEq::new(cx).eq_expr(left1, target) + && SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), left1, target) && is_integer_literal(right1, 1) { Some(target) diff --git a/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 4aae602ea051..41022e6a5321 100644 --- a/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -89,13 +89,13 @@ fn check_index_usage<'tcx>( // `Index` directly and no deref to `str` would happen in that case). if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_str() && BYTE_INDEX_METHODS.contains(&segment.ident.name) - && eq_expr_value(cx, chars_recv, recv) => + && eq_expr_value(cx, expr.span.ctxt(), chars_recv, recv) => { "passing a character position to a method that expects a byte index" }, ExprKind::Index(target, ..) if is_string_like(cx.typeck_results().expr_ty_adjusted(target).peel_refs()) - && eq_expr_value(cx, chars_recv, target) => + && eq_expr_value(cx, expr.span.ctxt(), chars_recv, target) => { "indexing into a string with a character position where a byte index is expected" }, diff --git a/clippy_lints/src/loops/manual_while_let_some.rs b/clippy_lints/src/loops/manual_while_let_some.rs index df5e9ffa9863..22c97bcdbd97 100644 --- a/clippy_lints/src/loops/manual_while_let_some.rs +++ b/clippy_lints/src/loops/manual_while_let_some.rs @@ -65,7 +65,7 @@ fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr && let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind { // make sure they're the same `Vec` - SpanlessEq::new(cx).eq_expr(pop_recv, is_empty_recv) + SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), pop_recv, is_empty_recv) } else { false } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index f1b7d4bdfa93..d2513765f631 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -101,8 +101,9 @@ pub(super) fn check<'tcx>( if let ExprKind::Binary(ref op, left, right) = end.kind && op.node == BinOpKind::Add { - let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); - let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); + let ctxt = start.span.ctxt(); + let start_equal_left = SpanlessEq::new(cx).eq_expr(ctxt, start, left); + let start_equal_right = SpanlessEq::new(cx).eq_expr(ctxt, start, right); if start_equal_left { take_expr = right; diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 400cfcd18f2c..b3c2e93a077f 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -121,12 +121,12 @@ fn is_sub_expr( expected_b: &Expr<'_>, expected_ty: Ty<'_>, ) -> bool { - let expr = peel_blocks(expr).kind; + let expr = peel_blocks(expr); if let ty::Int(ty) = expected_ty.kind() { let unsigned = Ty::new_uint(cx.tcx, ty.to_unsigned()); - return if let ExprKind::Cast(expr, cast_ty) = expr + return if let ExprKind::Cast(expr, cast_ty) = expr.kind && cx.typeck_results().node_type(cast_ty.hir_id) == unsigned { is_sub_expr(cx, expr, expected_a, expected_b, unsigned) @@ -135,10 +135,11 @@ fn is_sub_expr( }; } - if let ExprKind::Binary(op, a, b) = expr + let ctxt = expr.span.ctxt(); + if let ExprKind::Binary(op, a, b) = expr.kind && let BinOpKind::Sub = op.node - && eq_expr_value(cx, a, expected_a) - && eq_expr_value(cx, b, expected_b) + && eq_expr_value(cx, ctxt, a, expected_a) + && eq_expr_value(cx, ctxt, b, expected_b) { true } else { diff --git a/clippy_lints/src/manual_checked_ops.rs b/clippy_lints/src/manual_checked_ops.rs index 5fd1e27d5b99..0bc1c192b571 100644 --- a/clippy_lints/src/manual_checked_ops.rs +++ b/clippy_lints/src/manual_checked_ops.rs @@ -5,6 +5,7 @@ use rustc_hir::{AssignOpKind, BinOpKind, Block, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use std::ops::ControlFlow; declare_clippy_lint! { @@ -60,7 +61,7 @@ impl LateLintPass<'_> for ManualCheckedOps { && let Some(block) = branch_block(then, r#else, branch) { let mut eq = SpanlessEq::new(cx).deny_side_effects().paths_by_resolution(); - if !eq.eq_expr(divisor, divisor) { + if !eq.eq_expr(SyntaxContext::root(), divisor, divisor) { return; } @@ -70,7 +71,7 @@ impl LateLintPass<'_> for ManualCheckedOps { let found_early_use = for_each_expr_without_closures(block, |e| { if let ExprKind::Binary(binop, lhs, rhs) = e.kind && binop.node == BinOpKind::Div - && eq.eq_expr(rhs, divisor) + && eq.eq_expr(SyntaxContext::root(), rhs, divisor) && is_unsigned_integer(cx, lhs) { match first_use { @@ -84,7 +85,7 @@ impl LateLintPass<'_> for ManualCheckedOps { ControlFlow::<(), _>::Continue(Descend::No) } else if let ExprKind::AssignOp(op, lhs, rhs) = e.kind && op.node == AssignOpKind::DivAssign - && eq.eq_expr(rhs, divisor) + && eq.eq_expr(SyntaxContext::root(), rhs, divisor) && is_unsigned_integer(cx, lhs) { match first_use { @@ -96,7 +97,7 @@ impl LateLintPass<'_> for ManualCheckedOps { division_spans.push(e.span); ControlFlow::<(), _>::Continue(Descend::No) - } else if eq.eq_expr(e, divisor) { + } else if eq.eq_expr(SyntaxContext::root(), e, divisor) { if first_use.is_none() { first_use = Some(UseKind::Other); return ControlFlow::Break(()); diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index c8fba3e5a7bc..6848af7748f3 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -15,7 +15,7 @@ use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, HirId, PatKind, PathSegme use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::{Span, SyntaxContext}; use std::cmp::Ordering; use std::ops::Deref; @@ -261,6 +261,7 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx { let params = is_clamp_meta_pattern( cx, + expr.span.ctxt(), &BinaryOp::new(peel_blocks(cond))?, &BinaryOp::new(peel_blocks(else_if_cond))?, peel_blocks(then), @@ -268,7 +269,7 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx None, )?; // Contents of the else should be the resolved input. - if !eq_expr_value(cx, params.input, peel_blocks(else_body)) { + if !eq_expr_value(cx, expr.span.ctxt(), params.input, peel_blocks(else_body)) { return None; } Some(ClampSuggestion { @@ -445,6 +446,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt } if let Some(params) = is_clamp_meta_pattern( cx, + expr.span.ctxt(), &first, &second, first_expr, @@ -496,11 +498,14 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> peel_blocks_with_stmt(first_then).kind && let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) = peel_blocks_with_stmt(second_then).kind - && eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) + && let ctxt = first_expr.span.ctxt() + && second_expr.span.ctxt() == ctxt + && eq_expr_value(cx, ctxt, maybe_input_first_path, maybe_input_second_path) && let Some(first_bin) = BinaryOp::new(first_cond) && let Some(second_bin) = BinaryOp::new(second_cond) && let Some(input_min_max) = is_clamp_meta_pattern( cx, + ctxt, &first_bin, &second_bin, maybe_min_max_first, @@ -552,15 +557,17 @@ fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> && let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) = peel_blocks_with_stmt(else_if_then).kind { + let ctxt = expr.span.ctxt(); let params = is_clamp_meta_pattern( cx, + ctxt, &BinaryOp::new(peel_blocks(cond))?, &BinaryOp::new(peel_blocks(else_if_cond))?, peel_blocks(maybe_min_max_first), peel_blocks(maybe_min_max_second), None, )?; - if !eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) { + if !eq_expr_value(cx, ctxt, maybe_input_first_path, maybe_input_second_path) { return None; } Some(ClampSuggestion { @@ -631,6 +638,7 @@ impl<'tcx> BinaryOp<'tcx> { /// result can not be the shared argument in either case. fn is_clamp_meta_pattern<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, first_bin: &BinaryOp<'tcx>, second_bin: &BinaryOp<'tcx>, first_expr: &'tcx Expr<'tcx>, @@ -642,8 +650,10 @@ fn is_clamp_meta_pattern<'tcx>( // be the input variable, not the min or max. input_hir_ids: Option<(HirId, HirId)>, ) -> Option> { + #[expect(clippy::too_many_arguments)] fn check<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, first_bin: &BinaryOp<'tcx>, second_bin: &BinaryOp<'tcx>, first_expr: &'tcx Expr<'tcx>, @@ -659,11 +669,11 @@ fn is_clamp_meta_pattern<'tcx>( peel_blocks(first_bin.left).res_local_id() == Some(first_hir_id) && peel_blocks(second_bin.left).res_local_id() == Some(second_hir_id) }, - None => eq_expr_value(cx, first_bin.left, second_bin.left), + None => eq_expr_value(cx, ctxt, first_bin.left, second_bin.left), }; (refers_to_input - && eq_expr_value(cx, first_bin.right, first_expr) - && eq_expr_value(cx, second_bin.right, second_expr)) + && eq_expr_value(cx, ctxt, first_bin.right, first_expr) + && eq_expr_value(cx, ctxt, second_bin.right, second_expr)) .then_some(InputMinMax { input: first_bin.left, min, @@ -699,9 +709,20 @@ fn is_clamp_meta_pattern<'tcx>( ]; cases.into_iter().find_map(|(first, second)| { - check(cx, &first, &second, first_expr, second_expr, input_hir_ids, is_float).or_else(|| { + check( + cx, + ctxt, + &first, + &second, + first_expr, + second_expr, + input_hir_ids, + is_float, + ) + .or_else(|| { check( cx, + ctxt, &second, &first, second_expr, diff --git a/clippy_lints/src/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs index 4501612540fb..26e0a6e2a51f 100644 --- a/clippy_lints/src/manual_is_power_of_two.rs +++ b/clippy_lints/src/manual_is_power_of_two.rs @@ -9,6 +9,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -109,11 +110,12 @@ fn count_ones_receiver<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio /// Return `greater` if `smaller == greater - 1` fn is_one_less<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, greater: &'tcx Expr<'tcx>, smaller: &Expr<'tcx>, ) -> Option<&'tcx Expr<'tcx>> { if let Some((lhs, rhs)) = unexpanded_binop_operands(smaller, BinOpKind::Sub) - && SpanlessEq::new(cx).eq_expr(greater, lhs) + && SpanlessEq::new(cx).eq_expr(ctxt, greater, lhs) && is_integer_literal(rhs, 1) && matches!(cx.typeck_results().expr_ty_adjusted(greater).kind(), ty::Uint(_)) { @@ -126,7 +128,7 @@ fn is_one_less<'tcx>( /// Return `v` if `expr` is `v & (v - 1)` or `(v - 1) & v` fn is_and_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { let (lhs, rhs) = unexpanded_binop_operands(expr, BinOpKind::BitAnd)?; - is_one_less(cx, lhs, rhs).or_else(|| is_one_less(cx, rhs, lhs)) + is_one_less(cx, expr.span.ctxt(), lhs, rhs).or_else(|| is_one_less(cx, expr.span.ctxt(), rhs, lhs)) } /// Return the operands of the `expr` binary operation if the operator is `op` and none of the diff --git a/clippy_lints/src/manual_pop_if.rs b/clippy_lints/src/manual_pop_if.rs index bf53a8c27a6b..f279f330a3c0 100644 --- a/clippy_lints/src/manual_pop_if.rs +++ b/clippy_lints/src/manual_pop_if.rs @@ -212,7 +212,7 @@ fn check_is_some_and_pattern<'tcx>( && let ExprKind::Closure(closure) = closure_arg.kind && let body = cx.tcx.hir_body(closure.body) && let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method) - && eq_expr_value(cx, collection_expr, pop_collection) + && eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection) && let Some(param) = body.params.first() && let Some(ident) = param.pat.simple_ident() { @@ -274,7 +274,7 @@ fn check_if_let_pattern<'tcx>( if let ExprKind::If(inner_cond, inner_then, None) = inner_if.kind && is_local_used(cx, inner_cond, binding_id) && let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, inner_then, pop_method) - && eq_expr_value(cx, collection_expr, pop_collection) + && eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection) { return Some(ManualPopIfPattern { kind, @@ -327,7 +327,7 @@ fn check_let_chain_pattern<'tcx>( && kind.is_diag_item(cx, collection_expr) && is_local_used(cx, right, binding_id) && let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method) - && eq_expr_value(cx, collection_expr, pop_collection) + && eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection) { return Some(ManualPopIfPattern { kind, @@ -372,7 +372,7 @@ fn check_map_unwrap_or_pattern<'tcx>( && let body = cx.tcx.hir_body(closure.body) && cx.typeck_results().expr_ty(body.value).is_bool() && let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method) - && eq_expr_value(cx, collection_expr, pop_collection) + && eq_expr_value(cx, if_expr_span.ctxt(), collection_expr, pop_collection) && let Some(param) = body.params.first() && let Some(ident) = param.pat.simple_ident() { diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 564d5b53fa70..bc2e67de99d8 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -85,7 +85,7 @@ fn check_into_iter( && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id) && Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn() && match_acceptable_type(cx, left_expr, msrv) - && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) + && SpanlessEq::new(cx).eq_expr(parent_expr_span.ctxt(), left_expr, struct_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = target_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind && let filter_body = cx.tcx.hir_body(closure.body) @@ -132,7 +132,7 @@ fn check_iter( && let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id) && match_acceptable_sym(cx, iter_expr_def_id) && match_acceptable_type(cx, left_expr, msrv) - && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) + && SpanlessEq::new(cx).eq_expr(parent_expr_span.ctxt(), left_expr, struct_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind && let filter_body = cx.tcx.hir_body(closure.body) @@ -190,7 +190,7 @@ fn check_to_owned( && cx.tcx.is_diagnostic_item(sym::str_chars, chars_expr_def_id) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() && ty.is_lang_item(cx, hir::LangItem::String) - && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) + && SpanlessEq::new(cx).eq_expr(parent_expr_span.ctxt(), left_expr, str_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind && let hir::ExprKind::Closure(closure) = closure_expr.kind && let filter_body = cx.tcx.hir_body(closure.body) diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs index c371e5b47df8..a3a937ad713d 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -75,7 +75,8 @@ impl LateLintPass<'_> for ManualRotate { && let Some((l_shift_dir, l_expr, l_amount)) = parse_shift(l) && let Some((r_shift_dir, r_expr, r_amount)) = parse_shift(r) && l_shift_dir != r_shift_dir - && clippy_utils::eq_expr_value(cx, l_expr, r_expr) + && let ctxt = expr.span.ctxt() + && clippy_utils::eq_expr_value(cx, ctxt, l_expr, r_expr) && let Some(bit_width) = match cx.typeck_results().expr_ty(expr).kind() { ty::Int(itype) => itype.bit_width(), ty::Uint(itype) => itype.bit_width(), @@ -84,7 +85,6 @@ impl LateLintPass<'_> for ManualRotate { { let const_eval = ConstEvalCtxt::new(cx); - let ctxt = expr.span.ctxt(); let (shift_function, amount) = if let Some(Constant::Int(l_amount_val)) = const_eval.eval_local(l_amount, ctxt) && let Some(Constant::Int(r_amount_val)) = const_eval.eval_local(r_amount, ctxt) @@ -103,7 +103,7 @@ impl LateLintPass<'_> for ManualRotate { }; if let Some(Constant::Int(minuend)) = const_eval.eval_local(minuend, ctxt) - && clippy_utils::eq_expr_value(cx, amount1, amount2) + && clippy_utils::eq_expr_value(cx, ctxt, amount1, amount2) // (x << s) | (x >> bit_width - s) && ((binop.node == BinOpKind::Sub && u128::from(bit_width) == minuend) // (x << s) | (x >> (bit_width - 1) ^ s) diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index e0a60e3747a0..eaa6539653af 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -188,7 +188,7 @@ fn eq_pattern_length<'tcx>( { constant_length(cx, pattern, ctxt).is_some_and(|length| n == length) } else { - len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg)) + len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, SyntaxContext::root(), pattern, arg)) } } diff --git a/clippy_lints/src/manual_take.rs b/clippy_lints/src/manual_take.rs index dd8b0554a9ce..dec2a641231a 100644 --- a/clippy_lints/src/manual_take.rs +++ b/clippy_lints/src/manual_take.rs @@ -1,4 +1,5 @@ use clippy_config::Conf; +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{MEM_TAKE, Msrv}; use clippy_utils::source::snippet_with_context; @@ -74,10 +75,11 @@ impl LateLintPass<'_> for ManualTake { && let StmtKind::Semi(assignment) = stmt.kind && let ExprKind::Assign(mut_c, possible_false, _) = assignment.kind && let ExprKind::Path(_) = mut_c.kind - && !expr.span.in_external_macro(cx.sess().source_map()) + && let ctxt = expr.span.ctxt() + && !ctxt.in_external_macro(cx.sess().source_map()) && let Some(std_or_core) = clippy_utils::std_or_core(cx) && self.msrv.meets(cx, MEM_TAKE) - && clippy_utils::SpanlessEq::new(cx).eq_expr(cond, mut_c) + && SpanlessEq::new(cx).eq_expr(ctxt, cond, mut_c) && Some(false) == as_const_bool(possible_false) && let Some(then_bool) = as_const_bool(then_expr) && let Some(else_bool) = as_const_bool(else_expr) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index d86b05e5c882..2ee2cabbea07 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -14,12 +14,10 @@ use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, Pl use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; -use rustc_span::symbol::Ident; -use rustc_span::{BytePos, Span}; - -use crate::collapsible_if::{parens_around, peel_parens}; +use rustc_span::{BytePos, Ident, Span, SyntaxContext}; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; +use crate::collapsible_if::{parens_around, peel_parens}; pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { @@ -28,6 +26,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ar let only_wildcards_after = last_non_wildcard.is_none_or(|lnw| idx >= lnw); check_arm( cx, + arm.span.ctxt(), true, arm.pat, expr, @@ -43,18 +42,20 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ar pub(super) fn check_if_let<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, pat: &'tcx Pat<'_>, body: &'tcx Expr<'_>, else_expr: Option<&'tcx Expr<'_>>, let_expr: &'tcx Expr<'_>, msrv: Msrv, ) { - check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv, false); + check_arm(cx, ctxt, false, pat, let_expr, body, None, else_expr, msrv, false); } #[expect(clippy::too_many_arguments, clippy::too_many_lines)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, outer_is_match: bool, outer_pat: &'tcx Pat<'tcx>, outer_cond: &'tcx Expr<'tcx>, @@ -94,7 +95,7 @@ fn check_arm<'tcx>( && match (outer_else_body, inner_else_body) { (None, None) => true, (None, Some(e)) | (Some(e), None) => is_unit_expr(e), - (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), + (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(ctxt, a, b), } // the binding must not be used in the if guard && outer_guard.is_none_or(|e| !is_local_used(cx, e, binding_id)) @@ -145,7 +146,7 @@ fn check_arm<'tcx>( && match (outer_else_body, inner.r#else) { (None, None) => true, (None, Some(e)) | (Some(e), None) => is_unit_expr(e), - (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), + (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(ctxt, a, b), } && !pat_bindings_moved_or_mutated(cx, outer_pat, inner.cond) { diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index a8312a04f36f..caef6a0b3637 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -13,7 +13,7 @@ use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExp use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, TypeckResults}; -use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol, SyntaxContext}; use super::MATCH_SAME_ARMS; @@ -87,7 +87,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { SpanlessEq::new(cx) .expr_fallback(eq_fallback) - .eq_expr(expr_a, expr_b) + .eq_expr(SyntaxContext::root(), expr_a, expr_b) // these checks could be removed to allow unused bindings && bindings_eq(lhs.pat, local_map.keys().copied().collect()) && bindings_eq(rhs.pat, local_map.values().copied().collect()) diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 0e43bba60682..ae0f3ffa0edc 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1138,6 +1138,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { collapsible_match::check_if_let( cx, + if_let.let_span.ctxt(), if_let.let_pat, if_let.if_then, if_let.if_else, diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 9c6cf66019f0..a0f196489615 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -12,10 +12,10 @@ use rustc_hir::{ Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, }; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{SyntaxContext, sym}; pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { - if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) { + if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, expr.span.ctxt(), ex, arms) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -49,7 +49,10 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], /// } /// ``` pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) { - if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) { + if !is_else_clause(cx.tcx, ex) + && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) + && check_if_let_inner(cx, ex.span.ctxt(), if_let) + { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -63,7 +66,7 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: } } -fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool { +fn check_all_arms(cx: &LateContext<'_>, ctxt: SyntaxContext, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool { for arm in arms { let arm_expr = peel_blocks_with_stmt(arm.body); @@ -74,7 +77,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) } if let PatKind::Wild = arm.pat.kind { - if !eq_expr_value(cx, match_expr, arm_expr) { + if !eq_expr_value(cx, ctxt, match_expr, arm_expr) { return false; } } else if !pat_same_as_expr(arm.pat, arm_expr) { @@ -85,7 +88,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) true } -fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { +fn check_if_let_inner(cx: &LateContext<'_>, ctxt: SyntaxContext, if_let: &higher::IfLet<'_>) -> bool { if let Some(if_else) = if_let.if_else { if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) { return false; @@ -93,9 +96,9 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) - && SpanlessEq::new(cx).eq_expr(nested_if_let.let_expr, if_let.let_expr) + && SpanlessEq::new(cx).eq_expr(ctxt, nested_if_let.let_expr, if_let.let_expr) { - return check_if_let_inner(cx, nested_if_let); + return check_if_let_inner(cx, ctxt, nested_if_let); } if matches!(if_else.kind, ExprKind::Block(..)) { @@ -106,9 +109,9 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if let_expr_ty.is_diag_item(cx, sym::Option) { return else_expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) - || eq_expr_value(cx, if_let.let_expr, else_expr); + || eq_expr_value(cx, ctxt, if_let.let_expr, else_expr); } - return eq_expr_value(cx, if_let.let_expr, else_expr); + return eq_expr_value(cx, ctxt, if_let.let_expr, else_expr); } } diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 6d0b944df55d..ce296d45d605 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) && let Some((sym::replace, _, [current_from, current_to], _, _)) = method_call(parent) - && eq_expr_value(cx, to, current_to) + && eq_expr_value(cx, parent.span.ctxt(), to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { return; @@ -46,9 +46,10 @@ fn collect_replace_calls<'tcx>( let mut methods = VecDeque::new(); let mut from_args = VecDeque::new(); + let ctxt = expr.span.ctxt(); let _: Option<()> = for_each_expr_without_closures(expr, |e| { if let Some((sym::replace, _, [from, to], _, _)) = method_call(e) { - if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { + if eq_expr_value(cx, ctxt, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); ControlFlow::Continue(()) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index f4b4eed26090..9ddc3d1bf1d8 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -11,8 +11,8 @@ use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::TypeckResults; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::Span; use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Span, SyntaxContext}; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP}; @@ -109,6 +109,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { pub fn check_map_call( &self, cx: &LateContext<'tcx>, + ctxt: SyntaxContext, map_body: &'tcx Body<'tcx>, map_param_id: HirId, filter_param_id: HirId, @@ -150,7 +151,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { && a_typeck_results.expr_ty_adjusted(a) == b_typeck_results.expr_ty_adjusted(b) }) && (simple_equal - || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(receiver, map_arg_peeled)) + || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(ctxt, receiver, map_arg_peeled)) { Some(CheckResult::Method { map_arg, @@ -323,7 +324,9 @@ pub(super) fn check( return; } - if let Some((map_param_ident, check_result)) = is_find_or_filter(cx, map_recv, filter_arg, map_arg) { + if let Some((map_param_ident, check_result)) = + is_find_or_filter(cx, expr.span.ctxt(), map_recv, filter_arg, map_arg) + { let span = filter_span.with_hi(expr.span.hi()); let (filter_name, lint) = if is_find { ("find", MANUAL_FIND_MAP) @@ -397,6 +400,7 @@ pub(super) fn check( fn is_find_or_filter<'a>( cx: &LateContext<'a>, + ctxt: SyntaxContext, map_recv: &Expr<'_>, filter_arg: &Expr<'_>, map_arg: &Expr<'_>, @@ -422,7 +426,7 @@ fn is_find_or_filter<'a>( && let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind && let Some(check_result) = - offending_expr.check_map_call(cx, map_body, map_param_id, filter_param_id, is_filter_param_ref) + offending_expr.check_map_call(cx, ctxt, map_body, map_param_id, filter_param_id, is_filter_param_ref) { return Some((map_param_ident, check_result)); } diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs index 14e40328a416..bdf8eb3045e0 100644 --- a/clippy_lints/src/methods/get_last_with_len.rs +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: && is_integer_literal(rhs, 1) // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)` - && SpanlessEq::new(cx).eq_expr(recv, lhs_recv) + && SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), recv, lhs_recv) && !recv.can_have_side_effects() { let method = match cx.typeck_results().expr_ty_adjusted(recv).peel_refs().kind() { diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index 472d21977c7a..824e5aff872e 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -256,7 +256,7 @@ pub(super) fn check_or<'tcx>( .expr_ty_adjusted(some_recv) .peel_refs() .is_diag_item(cx, sym::Option) - && SpanlessEq::new(cx).eq_expr(none_recv, some_recv) + && SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), none_recv, some_recv) { (some_recv, some_arg) } else { diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index 9fa51f78c99d..2afda2956beb 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -28,7 +28,7 @@ pub(super) fn check<'tcx>( return; } - if SpanlessEq::new(cx).eq_expr(arg1, arg2) { + if SpanlessEq::new(cx).eq_expr(expr.span.ctxt(), arg1, arg2) { span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); } } diff --git a/clippy_lints/src/methods/range_zip_with_len.rs b/clippy_lints/src/methods/range_zip_with_len.rs index 7ece83ba7ca3..cf6347d466f2 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' // `.iter()` and `.len()` called on same `Path` && let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind && let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind - && SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments) + && SpanlessEq::new(cx).eq_path_segments(expr.span.ctxt(), iter_path.segments, len_path.segments) { span_lint_and_then( cx, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 4142f9f75773..444d0a1d7242 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -66,7 +66,7 @@ pub fn check_for_loop_iter( for_each_expr_without_closures(block, |e| { match e.kind { ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => { - change |= !can_mut_borrow_both(cx, caller, assignee); + change |= !can_mut_borrow_both(cx, body.span.ctxt(), caller, assignee); }, _ => {}, } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index c77b87268910..0bfb28d6b8bf 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -238,7 +238,9 @@ fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { /// of what it means for an expression to be "used". fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { get_parent_expr(cx, expr).is_none_or(|parent| match parent.kind { - ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), + ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => { + SpanlessEq::new(cx).eq_expr(parent.span.ctxt(), rhs, expr) + }, _ => is_used(cx, parent), }) } diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index b5becbdeb30d..885e0c4ed527 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -245,7 +245,10 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni let hash = hash_expr(cx, slice); let indexes = map.entry(hash).or_default(); - let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice)); + let ctxt = expr.span.ctxt(); + let entry = indexes + .iter_mut() + .find(|entry| eq_expr_value(cx, ctxt, entry.slice(), slice)); if let Some(entry) = entry { match entry { @@ -305,7 +308,10 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un let hash = hash_expr(cx, slice); let indexes = map.entry(hash).or_default(); - let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice)); + let ctxt = expr.span.ctxt(); + let entry = indexes + .iter_mut() + .find(|entry| eq_expr_value(cx, ctxt, entry.slice(), slice)); if let Some(entry) = entry { if let IndexEntry::IndexWithoutAssert { diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 064c1b8909fb..7da507c6d395 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -10,6 +10,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -169,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } if let Some((lhs_a, a)) = fetch_assign(then) && let Some((lhs_b, b)) = fetch_assign(else_expr) - && SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) + && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), lhs_a, lhs_b) { let mut applicability = Applicability::MachineApplicable; let cond = Sugg::hir_with_context(cx, cond, e.span.ctxt(), "..", &mut applicability); diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 2d303e40bd1c..5695779425f4 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -89,9 +89,10 @@ pub(super) fn check<'tcx>( } }; + let ctxt = expr.span.ctxt(); let mut found = false; let found_multiple = for_each_expr_without_closures(e, |e| { - if eq_expr_value(cx, assignee, e) { + if eq_expr_value(cx, ctxt, assignee, e) { if found { return ControlFlow::Break(()); } @@ -103,12 +104,12 @@ pub(super) fn check<'tcx>( if found && !found_multiple { // a = a op b - if eq_expr_value(cx, assignee, l) { + if eq_expr_value(cx, ctxt, assignee, l) { lint(assignee, r); } // a = b commutative_op a // Limited to primitive type as these ops are know to be commutative - if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() { + if eq_expr_value(cx, ctxt, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() { match op.node { hir::BinOpKind::Add | hir::BinOpKind::Mul diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs index e5a5b46b7b2e..85128f9114e6 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -63,7 +63,7 @@ pub(super) fn check<'tcx>( && left_type == right_type // Check that the same expression is compared in both comparisons - && SpanlessEq::new(cx).eq_expr(left_expr, right_expr) + && SpanlessEq::new(cx).eq_expr(span.ctxt(), left_expr, right_expr) && !left_expr.can_have_side_effects() diff --git a/clippy_lints/src/operators/double_comparison.rs b/clippy_lints/src/operators/double_comparison.rs index a40a724d2da5..88cd2d11999b 100644 --- a/clippy_lints/src/operators/double_comparison.rs +++ b/clippy_lints/src/operators/double_comparison.rs @@ -11,8 +11,9 @@ use super::DOUBLE_COMPARISONS; pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>, span: Span) { if let ExprKind::Binary(lop, llhs, lrhs) = lhs.kind && let ExprKind::Binary(rop, rlhs, rrhs) = rhs.kind - && eq_expr_value(cx, llhs, rlhs) - && eq_expr_value(cx, lrhs, rrhs) + && let ctxt = span.ctxt() + && eq_expr_value(cx, ctxt, llhs, rlhs) + && eq_expr_value(cx, ctxt, lrhs, rrhs) { let op = match (op, lop.node, rop.node) { // x == y || x < y => x <= y diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index d79101a687df..8e086cea7d91 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -14,7 +14,7 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) ) }) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) - && eq_expr_value(cx, lhs, rhs) + && eq_expr_value(cx, macro_call.span.ctxt(), lhs, rhs) && macro_call.is_local() && !is_in_test_function(cx.tcx, e.hir_id) { @@ -37,7 +37,10 @@ pub(crate) fn check<'tcx>( left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if is_useless_with_eq_exprs(op) && eq_expr_value(cx, left, right) && !is_in_test_function(cx.tcx, e.hir_id) { + if is_useless_with_eq_exprs(op) + && eq_expr_value(cx, e.span.ctxt(), left, right) + && !is_in_test_function(cx.tcx, e.hir_id) + { span_lint_and_then( cx, EQ_OP, diff --git a/clippy_lints/src/operators/manual_div_ceil.rs b/clippy_lints/src/operators/manual_div_ceil.rs index 304c51ba2627..57f5a046cb06 100644 --- a/clippy_lints/src/operators/manual_div_ceil.rs +++ b/clippy_lints/src/operators/manual_div_ceil.rs @@ -21,6 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & && check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(rhs)) && msrv.meets(cx, msrvs::DIV_CEIL) { + let ctxt = expr.span.ctxt(); match lhs.kind { ExprKind::Binary(inner_op, inner_lhs, inner_rhs) => { // (x + (y - 1)) / y @@ -28,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & && inner_op.node == BinOpKind::Add && sub_op.node == BinOpKind::Sub && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, rhs) + && SpanlessEq::new(cx).eq_expr(ctxt, sub_lhs, rhs) { build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); return; @@ -39,7 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & && inner_op.node == BinOpKind::Add && sub_op.node == BinOpKind::Sub && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, rhs) + && SpanlessEq::new(cx).eq_expr(ctxt, sub_lhs, rhs) { build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); return; @@ -50,7 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & && inner_op.node == BinOpKind::Sub && add_op.node == BinOpKind::Add && check_literal(inner_rhs) - && check_eq_expr(cx, add_rhs, rhs) + && SpanlessEq::new(cx).eq_expr(ctxt, add_rhs, rhs) { build_suggestion(cx, expr, add_lhs, rhs, &mut applicability); } @@ -76,7 +77,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) if method.ident.name == sym::next_multiple_of && check_int_ty(cx.typeck_results().expr_ty(receiver)) - && check_eq_expr(cx, next_multiple_of_arg, rhs) => + && SpanlessEq::new(cx).eq_expr(ctxt, next_multiple_of_arg, rhs) => { // x.next_multiple_of(Y) / Y build_suggestion(cx, expr, receiver, rhs, &mut applicability); @@ -88,7 +89,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & .assoc_fn_parent(cx) .opt_impl_ty(cx) && check_int_ty(impl_ty_binder.skip_binder()) - && check_eq_expr(cx, next_multiple_of_arg, rhs) + && SpanlessEq::new(cx).eq_expr(ctxt, next_multiple_of_arg, rhs) { build_suggestion(cx, expr, receiver, rhs, &mut applicability); } @@ -137,10 +138,6 @@ fn check_literal(expr: &Expr<'_>) -> bool { false } -fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { - SpanlessEq::new(cx).eq_expr(lhs, rhs) -} - fn build_suggestion( cx: &LateContext<'_>, expr: &Expr<'_>, diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index 8daedd1c9014..f0b6407a141b 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -19,9 +19,10 @@ pub(super) fn check<'tcx>( return; } // lhs op= l op r - if eq_expr_value(cx, lhs, l) { + let ctxt = expr.span.ctxt(); + if eq_expr_value(cx, ctxt, lhs, l) { lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, r); - } else if is_commutative(op) && eq_expr_value(cx, lhs, r) { + } else if is_commutative(op) && eq_expr_value(cx, ctxt, lhs, r) { // lhs op= l commutative_op r lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, l); } diff --git a/clippy_lints/src/operators/self_assignment.rs b/clippy_lints/src/operators/self_assignment.rs index a932378fbb52..2054cf6ac588 100644 --- a/clippy_lints/src/operators/self_assignment.rs +++ b/clippy_lints/src/operators/self_assignment.rs @@ -7,7 +7,7 @@ use rustc_lint::LateContext; use super::SELF_ASSIGNMENT; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>) { - if eq_expr_value(cx, lhs, rhs) { + if eq_expr_value(cx, e.span.ctxt(), lhs, rhs) { let lhs = snippet(cx, lhs.span, ""); let rhs = snippet(cx, rhs.span, ""); span_lint( diff --git a/clippy_lints/src/panicking_overflow_checks.rs b/clippy_lints/src/panicking_overflow_checks.rs index bc1821a48a34..7e75e0affc72 100644 --- a/clippy_lints/src/panicking_overflow_checks.rs +++ b/clippy_lints/src/panicking_overflow_checks.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for PanickingOverflowChecks { && ty == typeck.expr_ty(op_rhs) && ty == typeck.expr_ty(other) && !expr.span.in_external_macro(cx.tcx.sess.source_map()) - && (eq_expr_value(cx, op_lhs, other) || (commutative && eq_expr_value(cx, op_rhs, other))) + && (eq_expr_value(cx, ctxt, op_lhs, other) || (commutative && eq_expr_value(cx, ctxt, op_rhs, other))) { span_lint( cx, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 4bd6b1696b35..902552fd6bd1 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -299,7 +299,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex let by_ref = !cx.type_is_copy_modulo_regions(caller_ty) && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { - if eq_expr_value(cx, caller, peel_blocks(else_inner)) { + if eq_expr_value(cx, expr.span.ctxt(), caller, peel_blocks(else_inner)) { format!("Some({receiver_str}?)") } else { return; @@ -537,7 +537,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: && let is_option_early_return = is_early_return(sym::Option, cx, &if_block) && (is_option_early_return || is_early_return(sym::Result, cx, &if_block)) && if_else - .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) + .map(|e| eq_expr_value(cx, expr.span.ctxt(), let_expr, peel_blocks(e))) .is_none_or(|e| !e) { if !is_copy(cx, caller_ty) diff --git a/clippy_lints/src/same_length_and_capacity.rs b/clippy_lints/src/same_length_and_capacity.rs index ebf649c24307..1d5d960665ef 100644 --- a/clippy_lints/src/same_length_and_capacity.rs +++ b/clippy_lints/src/same_length_and_capacity.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for SameLengthAndCapacity { && let ExprKind::Path(QPath::TypeRelative(ty, fn_path)) = path_expr.kind && fn_path.ident.name == sym::from_raw_parts && args.len() >= 3 - && eq_expr_value(cx, &args[1], &args[2]) + && eq_expr_value(cx, expr.span.ctxt(), &args[1], &args[2]) { let middle_ty = cx.typeck_results().node_type(ty.hir_id); if middle_ty.is_diag_item(cx, rustc_sym::Vec) { diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index 7482bac4c7b4..4aba49071ee5 100644 --- a/clippy_lints/src/set_contains_or_insert.rs +++ b/clippy_lints/src/set_contains_or_insert.rs @@ -7,8 +7,8 @@ use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while, sym}; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::symbol::Symbol; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -119,7 +119,7 @@ fn is_set_mutated<'tcx>(cx: &LateContext<'tcx>, contains_expr: &OpExpr<'tcx>, ex cx.typeck_results().expr_ty(expr).peel_refs().opt_diag_name(cx), Some(sym::HashSet | sym::BTreeSet) ) - && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, expr.peel_borrows()) + && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, expr.peel_borrows()) } fn find_insert_calls<'tcx>( @@ -129,8 +129,8 @@ fn find_insert_calls<'tcx>( ) -> Option> { for_each_expr(cx, expr, |e| { if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) - && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver) - && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value) + && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, insert_expr.receiver) + && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.value, insert_expr.value) { return ControlFlow::Break(Some(insert_expr)); } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index c99f2e2fb942..ce74a9bea57f 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -8,6 +8,7 @@ use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -265,7 +266,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { { let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // If we have a size expression, check that it is equal to what's passed to `resize` - SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) + SpanlessEq::new(self.cx).eq_expr(SyntaxContext::root(), len_arg, size_expr) || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity) } else { self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg); @@ -287,7 +288,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { { if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr { // Check that len expression is equals to `with_capacity` expression - return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr) + return SpanlessEq::new(self.cx).eq_expr(SyntaxContext::root(), len_arg, size_expr) || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.name == sym::capacity); } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 61b5842ea542..df3ca09dd76d 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -11,7 +11,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::Spanned; +use rustc_span::{Spanned, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -220,7 +220,8 @@ declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); impl<'tcx> LateLintPass<'tcx> for StringAdd { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.in_external_macro(cx.sess().source_map()) { + let ctxt = e.span.ctxt(); + if ctxt.in_external_macro(cx.sess().source_map()) { return; } match e.kind { @@ -235,8 +236,8 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { let parent = get_parent_expr(cx, e); if let Some(p) = parent && let ExprKind::Assign(target, _, _) = p.kind - // avoid duplicate matches - && SpanlessEq::new(cx).eq_expr(target, left) + // avoid duplicate matches + && SpanlessEq::new(cx).eq_expr(ctxt, target, left) { return; } @@ -248,7 +249,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { "you added something to a string. Consider using `String::push_str()` instead", ); }, - ExprKind::Assign(target, src, _) if is_string(cx, target) && is_add(cx, src, target) => { + ExprKind::Assign(target, src, _) if is_string(cx, target) && is_add(cx, ctxt, src, target) => { span_lint( cx, STRING_ADD_ASSIGN, @@ -280,7 +281,7 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { .is_lang_item(cx, LangItem::String) } -fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { +fn is_add(cx: &LateContext<'_>, ctxt: SyntaxContext, src: &Expr<'_>, target: &Expr<'_>) -> bool { match peel_blocks(src).kind { ExprKind::Binary( Spanned { @@ -288,7 +289,7 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { }, left, _, - ) => SpanlessEq::new(cx).eq_expr(target, left), + ) => SpanlessEq::new(cx).eq_expr(ctxt, target, left), _ => false, } } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 262612a2a2d3..871c59d52659 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -98,12 +98,12 @@ fn generate_swap_warning<'tcx>( let ctxt = span.ctxt(); let mut applicability = Applicability::MachineApplicable; - if !can_mut_borrow_both(cx, e1, e2) { + if !can_mut_borrow_both(cx, ctxt, e1, e2) { 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 + && eq_expr_value(cx, ctxt, lhs1, lhs2) { let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); @@ -189,14 +189,15 @@ fn check_manual_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { && rhs2_path.segments.len() == 1 && ident.name == rhs2_path.segments[0].ident.name - && eq_expr_value(cx, tmp_init, lhs1) - && eq_expr_value(cx, rhs1, lhs2) && let ctxt = s1.span.ctxt() && s2.span.ctxt() == ctxt && s3.span.ctxt() == ctxt && first.span.ctxt() == ctxt && second.span.ctxt() == ctxt + + && eq_expr_value(cx, ctxt, tmp_init, lhs1) + && eq_expr_value(cx, ctxt, rhs1, lhs2) { let span = s1.span.to(s3.span); generate_swap_warning(block, cx, lhs1, lhs2, rhs1, rhs2, span, false); @@ -209,11 +210,12 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { 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) - && !first.span.in_external_macro(cx.sess().source_map()) - && is_same(cx, lhs0, rhs1) - && is_same(cx, lhs1, rhs0) - && !is_same(cx, lhs1, rhs1) // Ignore a = b; a = a (#10421) + && let ctxt = first.span.ctxt() + && ctxt == second.span.ctxt() + && !ctxt.in_external_macro(cx.sess().source_map()) + && is_same(cx, ctxt, lhs0, rhs1) + && is_same(cx, ctxt, lhs1, rhs0) + && !is_same(cx, ctxt, 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())), @@ -241,9 +243,9 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { } } -fn is_same(cx: &LateContext<'_>, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool { +fn is_same(cx: &LateContext<'_>, ctxt: SyntaxContext, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool { match lhs { - ExprOrIdent::Expr(expr) => eq_expr_value(cx, expr, rhs), + ExprOrIdent::Expr(expr) => eq_expr_value(cx, ctxt, expr, rhs), ExprOrIdent::Ident(ident) => { if let ExprKind::Path(QPath::Resolved(None, path)) = rhs.kind && let [segment] = &path.segments @@ -284,10 +286,10 @@ fn check_xor_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(s1, ctxt) && let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(s2, ctxt) && let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(s3, ctxt) - && eq_expr_value(cx, lhs0, rhs1) - && eq_expr_value(cx, lhs2, rhs1) - && eq_expr_value(cx, lhs1, rhs0) - && eq_expr_value(cx, lhs1, rhs2) + && eq_expr_value(cx, ctxt, lhs0, rhs1) + && eq_expr_value(cx, ctxt, lhs2, rhs1) + && eq_expr_value(cx, ctxt, lhs1, rhs0) + && eq_expr_value(cx, ctxt, lhs1, rhs2) && s2.span.ctxt() == ctxt && s3.span.ctxt() == ctxt { diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 4cd3707854c4..dd5da9a581c7 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -15,7 +15,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::Span; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -155,9 +155,11 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .filter_map(get_trait_info_from_bound) .for_each(|(trait_item_res, trait_item_segments, span)| { if let Some(self_segments) = self_bounds_map.get(&trait_item_res) - && SpanlessEq::new(cx) - .paths_by_resolution() - .eq_path_segments(self_segments, trait_item_segments) + && SpanlessEq::new(cx).paths_by_resolution().eq_path_segments( + SyntaxContext::root(), + self_segments, + trait_item_segments, + ) { span_lint_and_help( cx, @@ -250,7 +252,7 @@ impl TraitBounds { impl PartialEq for SpanlessTy<'_, '_> { fn eq(&self, other: &Self) -> bool { let mut eq = SpanlessEq::new(self.cx); - eq.inter_expr().eq_ty(self.ty, other.ty) + eq.inter_expr(SyntaxContext::root()).eq_ty(self.ty, other.ty) } } impl Hash for SpanlessTy<'_, '_> { @@ -380,9 +382,11 @@ struct ComparableTraitRef<'a, 'tcx> { impl PartialEq for ComparableTraitRef<'_, '_> { fn eq(&self, other: &Self) -> bool { SpanlessEq::eq_modifiers(self.modifiers, other.modifiers) - && SpanlessEq::new(self.cx) - .paths_by_resolution() - .eq_path(self.trait_ref.path, other.trait_ref.path) + && SpanlessEq::new(self.cx).paths_by_resolution().eq_path( + SyntaxContext::root(), + self.trait_ref.path, + other.trait_ref.path, + ) } } impl Eq for ComparableTraitRef<'_, '_> {} diff --git a/clippy_lints/src/transmute/eager_transmute.rs b/clippy_lints/src/transmute/eager_transmute.rs index 97e68b3df94e..05c0af922854 100644 --- a/clippy_lints/src/transmute/eager_transmute.rs +++ b/clippy_lints/src/transmute/eager_transmute.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; +use rustc_span::SyntaxContext; use super::EAGER_TRANSMUTE; @@ -54,9 +55,9 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_ lang_items.range_to_struct() ].into_iter().any(|did| did == Some(receiver_adt.did())) => { - eq_expr_value(cx, local_expr, arg.peel_borrows()) + eq_expr_value(cx, SyntaxContext::root(), local_expr, arg.peel_borrows()) }, - _ => eq_expr_value(cx, local_expr, expr), + _ => eq_expr_value(cx, SyntaxContext::root(), local_expr, expr), } } diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index df06982904b3..9449d44c009e 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKi use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::Span; +use rustc_span::{Span, SyntaxContext}; // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { @@ -64,15 +64,23 @@ declare_lint_pass!(UninitVec => [UNINIT_VEC]); // Threads: https://github.com/rust-lang/rust-clippy/pull/7682#discussion_r710998368 impl<'tcx> LateLintPass<'tcx> for UninitVec { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - if !block.span.in_external_macro(cx.tcx.sess.source_map()) { - for w in block.stmts.windows(2) { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind { - handle_uninit_vec_pair(cx, &w[0], expr); + let ctxt = block.span.ctxt(); + if !ctxt.in_external_macro(cx.tcx.sess.source_map()) { + for [stmt1, stmt2] in block.stmts.array_windows::<2>() { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt2.kind + && stmt1.span.ctxt() == ctxt + && stmt2.span.ctxt() == ctxt + && expr.span.ctxt() == ctxt + { + handle_uninit_vec_pair(cx, ctxt, stmt1, expr); } } - if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) { - handle_uninit_vec_pair(cx, stmt, expr); + if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) + && stmt.span.ctxt() == ctxt + && expr.span.ctxt() == ctxt + { + handle_uninit_vec_pair(cx, ctxt, stmt, expr); } } } @@ -80,12 +88,13 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec { fn handle_uninit_vec_pair<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, ) { if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve) && let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len) - && vec.location.eq_expr(cx, set_len_self) + && vec.location.eq_expr(cx, ctxt, set_len_self) && let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind() && let ty::Adt(_, args) = vec_ty.kind() // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()` @@ -138,10 +147,10 @@ enum VecLocation<'tcx> { } impl<'tcx> VecLocation<'tcx> { - pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + pub fn eq_expr(self, cx: &LateContext<'tcx>, ctxt: SyntaxContext, expr: &'tcx Expr<'tcx>) -> bool { match self { VecLocation::Local(hir_id) => expr.res_local_id() == Some(hir_id), - VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr), + VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(ctxt, self_expr, expr), } } } diff --git a/clippy_lints_internal/src/collapsible_span_lint_calls.rs b/clippy_lints_internal/src/collapsible_span_lint_calls.rs index bbcef0856ba4..bac05acebf34 100644 --- a/clippy_lints_internal/src/collapsible_span_lint_calls.rs +++ b/clippy_lints_internal/src/collapsible_span_lint_calls.rs @@ -89,10 +89,10 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { && let ExprKind::Path(..) = recv.kind { let mut app = Applicability::MachineApplicable; - let expr_ctxt = expr.span.ctxt(); + let ctxt = expr.span.ctxt(); let and_then_snippets = get_and_then_snippets( cx, - expr_ctxt, + ctxt, call_cx.span, call_lint.span, call_sp.span, @@ -101,28 +101,24 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { ); let mut sle = SpanlessEq::new(cx).deny_side_effects(); match ps.ident.name { - sym::span_suggestion if sle.eq_expr(call_sp, &span_call_args[0]) => { - let snippets = span_suggestion_snippets(cx, expr_ctxt, span_call_args, &mut app); + sym::span_suggestion if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => { + let snippets = span_suggestion_snippets(cx, ctxt, span_call_args, &mut app); suggest_suggestion(cx, expr, &and_then_snippets, &snippets, app); }, - sym::span_help if sle.eq_expr(call_sp, &span_call_args[0]) => { - let help_snippet = - snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, &mut app).0; + sym::span_help if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => { + let help_snippet = snippet_with_context(cx, span_call_args[1].span, ctxt, r#""...""#, &mut app).0; suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true, app); }, - sym::span_note if sle.eq_expr(call_sp, &span_call_args[0]) => { - let note_snippet = - snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, &mut app).0; + sym::span_note if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => { + let note_snippet = snippet_with_context(cx, span_call_args[1].span, ctxt, r#""...""#, &mut app).0; suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true, app); }, sym::help => { - let help_snippet = - snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0; + let help_snippet = snippet_with_context(cx, span_call_args[0].span, ctxt, r#""...""#, &mut app).0; suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false, app); }, sym::note => { - let note_snippet = - snippet_with_context(cx, span_call_args[0].span, expr_ctxt, r#""...""#, &mut app).0; + let note_snippet = snippet_with_context(cx, span_call_args[0].span, ctxt, r#""...""#, &mut app).0; suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false, app); }, _ => (), @@ -140,7 +136,7 @@ struct AndThenSnippets { fn get_and_then_snippets( cx: &LateContext<'_>, - expr_ctxt: SyntaxContext, + ctxt: SyntaxContext, cx_span: Span, lint_span: Span, span_span: Span, @@ -150,7 +146,7 @@ fn get_and_then_snippets( let cx_snippet = snippet_with_applicability(cx, cx_span, "cx", app); let lint_snippet = snippet_with_applicability(cx, lint_span, "..", app); let span_snippet = snippet_with_applicability(cx, span_span, "span", app); - let msg_snippet = snippet_with_context(cx, msg_span, expr_ctxt, r#""...""#, app).0; + let msg_snippet = snippet_with_context(cx, msg_span, ctxt, r#""...""#, app).0; AndThenSnippets { cx: cx_snippet, @@ -168,12 +164,12 @@ struct SpanSuggestionSnippets { fn span_suggestion_snippets<'hir>( cx: &LateContext<'_>, - expr_ctxt: SyntaxContext, + ctxt: SyntaxContext, span_call_args: &'hir [Expr<'hir>], app: &mut Applicability, ) -> SpanSuggestionSnippets { - let help_snippet = snippet_with_context(cx, span_call_args[1].span, expr_ctxt, r#""...""#, app).0; - let sugg_snippet = snippet_with_context(cx, span_call_args[2].span, expr_ctxt, "..", app).0; + let help_snippet = snippet_with_context(cx, span_call_args[1].span, ctxt, r#""...""#, app).0; + let sugg_snippet = snippet_with_context(cx, span_call_args[2].span, ctxt, "..", app).0; let applicability_snippet = snippet_with_applicability(cx, span_call_args[3].span, "Applicability::MachineApplicable", app); diff --git a/clippy_lints_internal/src/repeated_is_diagnostic_item.rs b/clippy_lints_internal/src/repeated_is_diagnostic_item.rs index 4492e3539ffb..b300dfa27b0e 100644 --- a/clippy_lints_internal/src/repeated_is_diagnostic_item.rs +++ b/clippy_lints_internal/src/repeated_is_diagnostic_item.rs @@ -152,6 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatedIsDiagnosticItem { ) { let lint_span = stmt1_span.to(stmt2_span); + let ctxt = lint_span.ctxt(); // if recv1.is_diag_item(cx, sym1) && .. { // .. @@ -161,8 +162,8 @@ impl<'tcx> LateLintPass<'tcx> for RepeatedIsDiagnosticItem { // } if let Some(first @ (span1, (cx1, recv1, _))) = extract_nested_is_diag_item(cx, cond1) && let Some(second @ (span2, (cx2, recv2, _))) = extract_nested_is_diag_item(cx, cond2) - && eq_expr_value(cx, cx1, cx2) - && eq_expr_value(cx, recv1, recv2) + && eq_expr_value(cx, ctxt, cx1, cx2) + && eq_expr_value(cx, ctxt, recv1, recv2) { let recv_ty = with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs())); @@ -208,8 +209,8 @@ impl<'tcx> LateLintPass<'tcx> for RepeatedIsDiagnosticItem { // } if let Some(first @ (span1, (tcx1, did1, _))) = extract_nested_is_diagnostic_item(cx, cond1) && let Some(second @ (span2, (tcx2, did2, _))) = extract_nested_is_diagnostic_item(cx, cond2) - && eq_expr_value(cx, tcx1, tcx2) - && eq_expr_value(cx, did1, did2) + && eq_expr_value(cx, ctxt, tcx1, tcx2) + && eq_expr_value(cx, ctxt, did1, did2) { span_lint_and_then( cx, @@ -264,11 +265,13 @@ impl<'tcx> LateLintPass<'tcx> for RepeatedIsDiagnosticItem { } fn check_ors(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>) { + let ctxt = span.ctxt(); + // recv1.is_diag_item(cx, sym1) || recv2.is_diag_item(cx, sym2) if let Some((cx1, recv1, sym1)) = extract_is_diag_item(cx, left) && let Some((cx2, recv2, sym2)) = extract_is_diag_item(cx, right) - && eq_expr_value(cx, cx1, cx2) - && eq_expr_value(cx, recv1, recv2) + && eq_expr_value(cx, ctxt, cx1, cx2) + && eq_expr_value(cx, ctxt, recv1, recv2) { let recv_ty = with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs())); @@ -300,8 +303,8 @@ fn check_ors(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_> // cx.tcx.is_diagnostic_item(sym1, did) || cx.tcx.is_diagnostic_item(sym2, did) if let Some((tcx1, did1, sym1)) = extract_is_diagnostic_item(cx, left) && let Some((tcx2, did2, sym2)) = extract_is_diagnostic_item(cx, right) - && eq_expr_value(cx, tcx1, tcx2) - && eq_expr_value(cx, did1, did2) + && eq_expr_value(cx, ctxt, tcx1, tcx2) + && eq_expr_value(cx, ctxt, did1, did2) { span_lint_and_then( cx, @@ -328,11 +331,13 @@ fn check_ors(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_> } fn check_ands(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>) { + let ctxt = span.ctxt(); + // !recv1.is_diag_item(cx, sym1) && !recv2.is_diag_item(cx, sym2) if let Some((cx1, recv1, sym1)) = extract_is_diag_item(cx, left) && let Some((cx2, recv2, sym2)) = extract_is_diag_item(cx, right) - && eq_expr_value(cx, cx1, cx2) - && eq_expr_value(cx, recv1, recv2) + && eq_expr_value(cx, ctxt, cx1, cx2) + && eq_expr_value(cx, ctxt, recv1, recv2) { let recv_ty = with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs())); @@ -364,8 +369,8 @@ fn check_ands(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_ // !cx.tcx.is_diagnostic_item(sym1, did) && !cx.tcx.is_diagnostic_item(sym2, did) if let Some((tcx1, did1, sym1)) = extract_is_diagnostic_item(cx, left) && let Some((tcx2, did2, sym2)) = extract_is_diagnostic_item(cx, right) - && eq_expr_value(cx, tcx1, tcx2) - && eq_expr_value(cx, did1, did2) + && eq_expr_value(cx, ctxt, tcx1, tcx2) + && eq_expr_value(cx, ctxt, did1, did2) { span_lint_and_then( cx, @@ -400,9 +405,10 @@ fn check_if_chains<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, conds: Vec<&'t // .. // } let mut found = conds.iter().filter_map(|cond| extract_nested_is_diag_item(cx, cond)); + let ctxt = expr.span.ctxt(); if let Some(first @ (_, (cx_1, recv1, _))) = found.next() - && let other = - found.filter(|(_, (cx_, recv, _))| eq_expr_value(cx, cx_, cx_1) && eq_expr_value(cx, recv, recv1)) + && let other = found + .filter(|(_, (cx_, recv, _))| eq_expr_value(cx, ctxt, cx_, cx_1) && eq_expr_value(cx, ctxt, recv, recv1)) && let results = iter::once(first).chain(other).collect::>() && results.len() > 1 { @@ -457,7 +463,8 @@ fn check_if_chains<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, conds: Vec<&'t .into_iter() .filter_map(|cond| extract_nested_is_diagnostic_item(cx, cond)); if let Some(first @ (_, (tcx1, did1, _))) = found.next() - && let other = found.filter(|(_, (tcx, did, _))| eq_expr_value(cx, tcx, tcx1) && eq_expr_value(cx, did, did1)) + && let other = + found.filter(|(_, (tcx, did, _))| eq_expr_value(cx, ctxt, tcx, tcx1) && eq_expr_value(cx, ctxt, did, did1)) && let results = iter::once(first).chain(other).collect::>() && results.len() > 1 { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index a4d8fd20e4d3..54ff0bdcdd8a 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -2,6 +2,7 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; use crate::{sym, tokenize_with_text}; +use core::mem; use rustc_ast::ast; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::{FxHasher, FxIndexMap}; @@ -107,46 +108,57 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { /// Use this method to wrap comparisons that may involve inter-expression context. /// See `self.locals`. - pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { + pub fn inter_expr(&mut self, ctxt: SyntaxContext) -> HirEqInterExpr<'_, 'a, 'tcx> { HirEqInterExpr { inner: self, - left_ctxt: SyntaxContext::root(), - right_ctxt: SyntaxContext::root(), + eval_ctxt: ctxt, + prev_left_ctxt: ctxt, + prev_right_ctxt: ctxt, locals: HirIdMap::default(), local_items: FxIndexMap::default(), } } - pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - self.inter_expr().eq_block(left, right) + pub fn eq_block(&mut self, ctxt: SyntaxContext, left: &Block<'_>, right: &Block<'_>) -> bool { + self.inter_expr(ctxt).eq_block(left, right) } - pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - self.inter_expr().eq_expr(left, right) + pub fn eq_expr(&mut self, ctxt: SyntaxContext, left: &Expr<'_>, right: &Expr<'_>) -> bool { + self.inter_expr(ctxt).eq_expr(left, right) } - pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { - self.inter_expr().eq_path(left, right) + pub fn eq_path(&mut self, ctxt: SyntaxContext, left: &Path<'_>, right: &Path<'_>) -> bool { + self.inter_expr(ctxt).eq_path(left, right) } - pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { - self.inter_expr().eq_path_segment(left, right) + pub fn eq_path_segment(&mut self, ctxt: SyntaxContext, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { + self.inter_expr(ctxt).eq_path_segment(left, right) } - pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { - self.inter_expr().eq_path_segments(left, right) + pub fn eq_path_segments( + &mut self, + ctxt: SyntaxContext, + left: &[PathSegment<'_>], + right: &[PathSegment<'_>], + ) -> bool { + self.inter_expr(ctxt).eq_path_segments(left, right) } pub fn eq_modifiers(left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool { - std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness) - && std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity) + mem::discriminant(&left.constness) == mem::discriminant(&right.constness) + && mem::discriminant(&left.polarity) == mem::discriminant(&right.polarity) } } pub struct HirEqInterExpr<'a, 'b, 'tcx> { inner: &'a mut SpanlessEq<'b, 'tcx>, - left_ctxt: SyntaxContext, - right_ctxt: SyntaxContext, + + /// The root context to view each side from. + eval_ctxt: SyntaxContext, + + // Optimization to avoid rechecking the context of desugarings. + prev_left_ctxt: SyntaxContext, + prev_right_ctxt: SyntaxContext, // When binding are declared, the binding ID in the left expression is mapped to the one on the // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, @@ -156,7 +168,17 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { } impl HirEqInterExpr<'_, '_, '_> { + pub fn set_eval_ctxt(&mut self, ctxt: SyntaxContext) { + self.eval_ctxt = ctxt; + self.prev_left_ctxt = ctxt; + self.prev_right_ctxt = ctxt; + } + pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { + if self.check_ctxt(left.span.ctxt(), right.span.ctxt()) == Some(false) { + return false; + } + match (&left.kind, &right.kind) { (StmtKind::Let(l), StmtKind::Let(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init @@ -372,15 +394,16 @@ impl HirEqInterExpr<'_, '_, '_> { } let lspan = left.span.data(); let rspan = right.span.data(); - if lspan.ctxt != SyntaxContext::root() && rspan.ctxt != SyntaxContext::root() { - // Don't try to check in between statements inside macros. - return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right)) - && both(left.expr.as_ref(), right.expr.as_ref(), |left, right| { - self.eq_expr(left, right) - }); - } - if lspan.ctxt != rspan.ctxt { - return false; + match self.check_ctxt(lspan.ctxt, rspan.ctxt) { + Some(false) => return false, + None if self.eval_ctxt.is_root() => {}, + _ => { + // Don't try to check in between statements inside macros. + return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right)) + && both(left.expr.as_ref(), right.expr.as_ref(), |left, right| { + self.eq_expr(left, right) + }); + }, } let mut lstart = lspan.lo; @@ -475,26 +498,28 @@ impl HirEqInterExpr<'_, '_, '_> { #[expect(clippy::too_many_lines)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { - return false; - } - - if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results - && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) - && let (Some(l), Some(r)) = ( - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs) - .eval_local(left, self.left_ctxt), - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs) - .eval_local(right, self.right_ctxt), - ) - && l == r - { - return true; + match self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { + None => { + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results + && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right) + && let (Some(l), Some(r)) = ( + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs) + .eval_local(left, self.eval_ctxt), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs) + .eval_local(right, self.eval_ctxt), + ) + && l == r + { + return true; + } + }, + Some(false) => return false, + Some(true) => {}, } let is_eq = match ( - reduce_exprkind(self.inner.cx, &left.kind), - reduce_exprkind(self.inner.cx, &right.kind), + reduce_exprkind(self.inner.cx, self.eval_ctxt, &left.kind), + reduce_exprkind(self.inner.cx, self.eval_ctxt, &right.kind), ) { (ExprKind::AddrOf(lb, l_mut, le), ExprKind::AddrOf(rb, r_mut, re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) @@ -542,7 +567,12 @@ impl HirEqInterExpr<'_, '_, '_> { && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, - (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, + (ExprKind::Lit(l), ExprKind::Lit(r)) => { + if self.check_ctxt(l.span.ctxt(), r.span.ctxt()) == Some(false) { + return false; + } + l.node == r.node + }, (ExprKind::Loop(lb, ll, lls, _), ExprKind::Loop(rb, rl, rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll.as_ref(), rl.as_ref(), |l, r| l.ident.name == r.ident.name) @@ -673,7 +703,7 @@ impl HirEqInterExpr<'_, '_, '_> { } fn eq_const_arg(&mut self, left: &ConstArg<'_>, right: &ConstArg<'_>) -> bool { - if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { + if self.check_ctxt(left.span.ctxt(), right.span.ctxt()) == Some(false) { return false; } @@ -892,46 +922,72 @@ impl HirEqInterExpr<'_, '_, '_> { || both_some_and(left.ct(), right.ct(), |l, r| self.eq_const_arg(l, r))) } - fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool { - if self.left_ctxt == left && self.right_ctxt == right { - return true; - } else if self.left_ctxt == left || self.right_ctxt == right { - // Only one context has changed. This can only happen if the two nodes are written differently. - return false; - } else if left != SyntaxContext::root() { + /// Checks whether either operand is within a macro context, and if so, whether the macro calls + /// are equal. + fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> Option { + let prev_left = mem::replace(&mut self.prev_left_ctxt, left); + let prev_right = mem::replace(&mut self.prev_right_ctxt, right); + + if left == self.eval_ctxt && right == self.eval_ctxt { + None + } else if left == prev_left && right == prev_right { + // Same as the previous context, no need to recheck anything + Some(true) + } else if left == prev_left + || right == prev_right + || left == self.eval_ctxt + || right == self.eval_ctxt + || left.is_root() + || right.is_root() + { + // Either only one context changed, or at least one context is a parent of the + // evaluation context. + // Unfortunately we can't get a span of a metavariable so we have to treat the + // second case as unequal. + Some(false) + } else { + // Walk each context in lockstep up to the evaluation context checking that each + // expansion has the same kind. let mut left_data = left.outer_expn_data(); let mut right_data = right.outer_expn_data(); loop { use TokenKind::{BlockComment, LineComment, Whitespace}; - if left_data.macro_def_id != right_data.macro_def_id - || (matches!( - left_data.kind, - ExpnKind::Macro(MacroKind::Bang, name) - if name == sym::cfg || name == sym::option_env - ) && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| { - !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }) - })) - { - // Either a different chain of macro calls, or different arguments to the `cfg` macro. - return false; + if left_data.macro_def_id != right_data.macro_def_id || left_data.kind != right_data.kind { + return Some(false); + } + let left = left_data.call_site.ctxt(); + let right = right_data.call_site.ctxt(); + if left == self.eval_ctxt && right == self.eval_ctxt { + // Finally if the outermost expansion is a macro call, check if the + // tokens are the same. + if let ExpnKind::Macro(MacroKind::Bang, _) = left_data.kind { + return Some(eq_span_tokens( + self.inner.cx, + left_data.call_site, + right_data.call_site, + |t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }), + )); + } + return Some(true); } - let left_ctxt = left_data.call_site.ctxt(); - let right_ctxt = right_data.call_site.ctxt(); - if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() { - break; + if left == prev_left && right == prev_right { + return Some(true); } - if left_ctxt == SyntaxContext::root() || right_ctxt == SyntaxContext::root() { - // Different lengths for the expansion stack. This can only happen if nodes are written differently, - // or shouldn't be compared to start with. - return false; + if left == prev_left + || right == prev_right + || left == self.eval_ctxt + || right == self.eval_ctxt + || left.is_root() + || right.is_root() + { + // Either there's a different number of expansions, or at least one context is + // a parent of the evaluation context. + return Some(false); } - left_data = left_ctxt.outer_expn_data(); - right_data = right_ctxt.outer_expn_data(); + left_data = left.outer_expn_data(); + right_data = right.outer_expn_data(); } } - self.left_ctxt = left; - self.right_ctxt = right; - true } fn swap_binop<'a>( @@ -970,7 +1026,11 @@ impl HirEqInterExpr<'_, '_, '_> { } /// Some simple reductions like `{ return }` => `return` -fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> { +fn reduce_exprkind<'hir>( + cx: &LateContext<'_>, + eval_ctxt: SyntaxContext, + kind: &'hir ExprKind<'hir>, +) -> &'hir ExprKind<'hir> { if let ExprKind::Block(block, _) = kind { match (block.stmts, block.expr) { // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty @@ -978,17 +1038,20 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &' ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]), // `{}` => `()` ([], None) - if block.span.check_source_text(cx, |src| { - tokenize(src, FrontmatterAllowed::No) - .map(|t| t.kind) - .filter(|t| { - !matches!( - t, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }) - .eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) - }) => + if block.span.ctxt() != eval_ctxt + || block.span.check_source_text(cx, |src| { + tokenize(src, FrontmatterAllowed::No) + .map(|t| t.kind) + .filter(|t| { + !matches!( + t, + TokenKind::LineComment { .. } + | TokenKind::BlockComment { .. } + | TokenKind::Whitespace + ) + }) + .eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) + }) => { &ExprKind::Tup(&[]) }, @@ -1039,8 +1102,13 @@ pub fn count_eq( } /// Checks if two expressions evaluate to the same value, and don't contain any side effects. -pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { - SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) +/// +/// The context argument is the context used to view the two expressions. e.g. when comparing the +/// two arguments in `f(m!(1), m!(2))` the context of the call expression should be used. This is +/// needed to handle the case where two macros expand to the same thing, but the arguments are +/// different. +pub fn eq_expr_value(cx: &LateContext<'_>, ctxt: SyntaxContext, left: &Expr<'_>, right: &Expr<'_>) -> bool { + SpanlessEq::new(cx).deny_side_effects().eq_expr(ctxt, left, right) } /// Returns the segments of a path that might have generic parameters. @@ -1104,7 +1172,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); } - std::mem::discriminant(&b.rules).hash(&mut self.s); + mem::discriminant(&b.rules).hash(&mut self.s); } #[expect(clippy::too_many_lines)] @@ -1120,11 +1188,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { return; } - std::mem::discriminant(&e.kind).hash(&mut self.s); + mem::discriminant(&e.kind).hash(&mut self.s); match &e.kind { ExprKind::AddrOf(kind, m, e) => { - std::mem::discriminant(kind).hash(&mut self.s); + mem::discriminant(kind).hash(&mut self.s); m.hash(&mut self.s); self.hash_expr(e); }, @@ -1141,7 +1209,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(r); }, ExprKind::AssignOp(o, l, r) => { - std::mem::discriminant(&o.node).hash(&mut self.s); + mem::discriminant(&o.node).hash(&mut self.s); self.hash_expr(l); self.hash_expr(r); }, @@ -1152,7 +1220,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_block(b); }, ExprKind::Binary(op, l, r) => { - std::mem::discriminant(&op.node).hash(&mut self.s); + mem::discriminant(&op.node).hash(&mut self.s); self.hash_expr(l); self.hash_expr(r); }, @@ -1175,7 +1243,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Closure(Closure { capture_clause, body, .. }) => { - std::mem::discriminant(capture_clause).hash(&mut self.s); + mem::discriminant(capture_clause).hash(&mut self.s); // closures inherit TypeckResults self.hash_expr(self.cx.tcx.hir_body(*body).value); }, @@ -1326,11 +1394,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(expr); }, ExprKind::Unary(l_op, le) => { - std::mem::discriminant(l_op).hash(&mut self.s); + mem::discriminant(l_op).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { - std::mem::discriminant(kind).hash(&mut self.s); + mem::discriminant(kind).hash(&mut self.s); self.hash_expr(expr); if let Some(ty) = ty { self.hash_ty(ty); @@ -1363,7 +1431,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) { - std::mem::discriminant(&lit.kind).hash(&mut self.s); + mem::discriminant(&lit.kind).hash(&mut self.s); match &lit.kind { PatExprKind::Lit { lit, negated } => { lit.node.hash(&mut self.s); @@ -1374,7 +1442,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) { - std::mem::discriminant(&pat.kind).hash(&mut self.s); + mem::discriminant(&pat.kind).hash(&mut self.s); match pat.kind { TyPatKind::Range(s, e) => { self.hash_const_arg(s); @@ -1390,16 +1458,16 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_pat(&mut self, pat: &Pat<'_>) { - std::mem::discriminant(&pat.kind).hash(&mut self.s); + mem::discriminant(&pat.kind).hash(&mut self.s); match &pat.kind { PatKind::Missing => unreachable!(), PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => { - std::mem::discriminant(by_ref).hash(&mut self.s); + mem::discriminant(by_ref).hash(&mut self.s); if let ByRef::Yes(pi, mu) = by_ref { - std::mem::discriminant(pi).hash(&mut self.s); - std::mem::discriminant(mu).hash(&mut self.s); + mem::discriminant(pi).hash(&mut self.s); + mem::discriminant(mu).hash(&mut self.s); } - std::mem::discriminant(mutability).hash(&mut self.s); + mem::discriminant(mutability).hash(&mut self.s); if let Some(pat) = pat { self.hash_pat(pat); } @@ -1418,12 +1486,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { if let Some(e) = e { self.hash_pat_expr(e); } - std::mem::discriminant(i).hash(&mut self.s); + mem::discriminant(i).hash(&mut self.s); }, PatKind::Ref(pat, pi, mu) => { self.hash_pat(pat); - std::mem::discriminant(pi).hash(&mut self.s); - std::mem::discriminant(mu).hash(&mut self.s); + mem::discriminant(pi).hash(&mut self.s); + mem::discriminant(mu).hash(&mut self.s); }, PatKind::Guard(pat, guard) => { self.hash_pat(pat); @@ -1492,12 +1560,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_modifiers(&mut self, modifiers: TraitBoundModifiers) { let TraitBoundModifiers { constness, polarity } = modifiers; - std::mem::discriminant(&polarity).hash(&mut self.s); - std::mem::discriminant(&constness).hash(&mut self.s); + mem::discriminant(&polarity).hash(&mut self.s); + mem::discriminant(&constness).hash(&mut self.s); } pub fn hash_stmt(&mut self, b: &Stmt<'_>) { - std::mem::discriminant(&b.kind).hash(&mut self.s); + mem::discriminant(&b.kind).hash(&mut self.s); match &b.kind { StmtKind::Let(local) => { @@ -1518,14 +1586,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { lifetime.ident.name.hash(&mut self.s); - std::mem::discriminant(&lifetime.kind).hash(&mut self.s); + mem::discriminant(&lifetime.kind).hash(&mut self.s); if let LifetimeKind::Param(param_id) = lifetime.kind { param_id.hash(&mut self.s); } } pub fn hash_ty(&mut self, ty: &Ty<'_>) { - std::mem::discriminant(&ty.kind).hash(&mut self.s); + mem::discriminant(&ty.kind).hash(&mut self.s); self.hash_tykind(&ty.kind); } @@ -1564,7 +1632,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { for arg in fn_ptr.decl.inputs { self.hash_ty(arg); } - std::mem::discriminant(&fn_ptr.decl.output).hash(&mut self.s); + mem::discriminant(&fn_ptr.decl.output).hash(&mut self.s); match fn_ptr.decl.output { FnRetTy::DefaultReturn(_) => {}, FnRetTy::Return(ty) => { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index a1860adb4407..4b7b1daed286 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -469,19 +469,23 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Opti /// this method will return a tuple, composed of a `Vec` /// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]` /// and an `Expr` for root of them, `v` -fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) { +fn projection_stack<'a, 'hir>( + mut e: &'a Expr<'hir>, + ctxt: SyntaxContext, +) -> Option<(Vec<&'a Expr<'hir>>, &'a Expr<'hir>)> { let mut result = vec![]; let root = loop { match e.kind { - ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => { + ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) if e.span.ctxt() == ctxt => { result.push(e); e = ep; }, + ExprKind::Index(..) | ExprKind::Field(..) => return None, _ => break e, } }; result.reverse(); - (result, root) + Some((result, root)) } /// Gets the mutability of the custom deref adjustment, if any. @@ -499,10 +503,14 @@ pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Optio /// Checks if two expressions can be mutably borrowed simultaneously /// and they aren't dependent on borrowing same thing twice -pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool { - let (s1, r1) = projection_stack(e1); - let (s2, r2) = projection_stack(e2); - if !eq_expr_value(cx, r1, r2) { +pub fn can_mut_borrow_both(cx: &LateContext<'_>, ctxt: SyntaxContext, e1: &Expr<'_>, e2: &Expr<'_>) -> bool { + let Some((s1, r1)) = projection_stack(e1, ctxt) else { + return false; + }; + let Some((s2, r2)) = projection_stack(e2, ctxt) else { + return false; + }; + if !eq_expr_value(cx, ctxt, r1, r2) { return true; } if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() { @@ -520,11 +528,6 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - return true; } }, - (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => { - if !eq_expr_value(cx, i1, i2) { - return false; - } - }, _ => return false, } }