diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 64aeb27df693..7ee4371f1414 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -292,8 +292,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)); } @@ -302,8 +303,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 9b3822f9d8f0..6535e5623b12 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)) }, @@ -125,8 +126,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 { @@ -136,9 +137,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 238ebd4a444c..01120b5697c3 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 @@ -94,8 +94,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/copies.rs b/clippy_lints/src/copies.rs index 4fdb497950f8..2ed8f4d62435 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -179,8 +179,8 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); - lint_same_cond(cx, &conds, &mut self.interior_mut); - lint_same_fns_in_if_cond(cx, &conds); + lint_same_cond(cx, SyntaxContext::root(), &conds, &mut self.interior_mut); + lint_same_fns_in_if_cond(cx, SyntaxContext::root(), &conds); let all_same = !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); if !all_same && conds.len() != blocks.len() { @@ -196,7 +196,10 @@ fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[& .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, @@ -376,7 +379,12 @@ fn eq_stmts( .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(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)] @@ -387,7 +395,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(); @@ -498,6 +506,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 { @@ -603,7 +612,12 @@ fn method_caller_is_mutable<'tcx>( } /// Implementation of `IFS_SAME_COND`. -fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) { +fn lint_same_cond<'tcx>( + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + conds: &[&Expr<'_>], + interior_mut: &mut InteriorMut<'tcx>, +) { for group in search_same( conds, |e| hash_expr(cx, e), @@ -614,10 +628,10 @@ fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mu if method_caller_is_mutable(cx, caller, interior_mut) { false } else { - SpanlessEq::new(cx).eq_expr(lhs, rhs) + SpanlessEq::new(cx).eq_expr(ctxt, lhs, rhs) } } else { - eq_expr_value(cx, lhs, rhs) + eq_expr_value(cx, ctxt, lhs, rhs) } }, ) { @@ -627,17 +641,17 @@ fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mu } /// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. -fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { +fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, ctxt: SyntaxContext, conds: &[&Expr<'_>]) { let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { // Do not lint if any expr originates from a macro if lhs.span.from_expansion() || rhs.span.from_expansion() { 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, ctxt, lhs, rhs) { return false; } - SpanlessEq::new(cx).eq_expr(lhs, rhs) + SpanlessEq::new(cx).eq_expr(ctxt, lhs, rhs) }; for group in search_same(conds, |e| hash_expr(cx, e), eq) { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index a5ec6777b434..a2119f1c4f4b 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -491,11 +491,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; @@ -514,10 +514,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 { @@ -586,11 +586,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.rs b/clippy_lints/src/floating_point_arithmetic.rs index 84d39dd81c91..d628de6e8a24 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use rustc_span::source_map::Spanned; use std::f32::consts as f32_consts; use std::f64::consts as f64_consts; @@ -363,8 +364,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { rmul_lhs, rmul_rhs, ) = add_rhs.kind - && eq_expr_value(cx, lmul_lhs, lmul_rhs) - && eq_expr_value(cx, rmul_lhs, rmul_rhs) + && let ctxt = receiver.span.ctxt() + && eq_expr_value(cx, ctxt, lmul_lhs, lmul_rhs) + && eq_expr_value(cx, ctxt, rmul_lhs, rmul_rhs) { return Some(format!( "{}.hypot({})", @@ -514,11 +516,11 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { /// 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) && eq_expr_value(cx, left, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, ctxt, left, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, ctxt, right, test), _ => false, } } else { @@ -527,11 +529,11 @@ 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) && eq_expr_value(cx, right, test), - BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test), + BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, ctxt, right, test), + BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, ctxt, left, test), _ => false, } } else { @@ -555,14 +557,19 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> 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) + && 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) + && eq_expr_value(cx, ctxt, expr1, expr2_negated) { return Some((true, expr1)); } @@ -577,7 +584,8 @@ fn check_custom_abs(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 positive_abs_sugg = ( "manual implementation of `abs` method", @@ -587,13 +595,13 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { "manual implementation of negation of `abs` method", format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()), ); - let sugg = if is_testing_positive(cx, cond, body) { + let sugg = if is_testing_positive(cx, ctxt, cond, body) { if if_expr_positive { positive_abs_sugg } else { negative_abs_sugg } - } else if is_testing_negative(cx, cond, body) { + } else if is_testing_negative(cx, ctxt, cond, body) { if if_expr_positive { negative_abs_sugg } else { @@ -614,14 +622,14 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { } } -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 @@ -636,7 +644,7 @@ fn check_log_division(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 a99118f90f88..a6f66bddce0d 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() && is_type_diagnostic_item(cx, ty, 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/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index 0fdbf6797381..3900a3e47d7e 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -66,7 +66,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 c634c12e1877..0ab401b5fa95 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -193,7 +193,7 @@ fn check_gt( } fn is_side_effect_free(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - eq_expr_value(cx, expr, expr) + eq_expr_value(cx, expr.span.ctxt(), expr, expr) } #[allow(clippy::too_many_arguments)] @@ -235,7 +235,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)) @@ -257,8 +258,8 @@ fn check_subtraction( Applicability::MachineApplicable, ); } - } 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) { @@ -302,14 +303,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 { @@ -371,7 +373,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 a702e60f1c27..adeac66c7316 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 9527e258db8a..c397b690ea12 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 11edb929d70b..aaf3f3b996e1 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -100,8 +100,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 5814b6815a1e..920122bdea98 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -125,12 +125,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) @@ -139,10 +139,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_clamp.rs b/clippy_lints/src/manual_clamp.rs index 42fe386d2c3c..478474496756 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -17,7 +17,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; @@ -262,6 +262,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), @@ -269,7 +270,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 { @@ -444,6 +445,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, @@ -495,11 +497,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, @@ -551,15 +556,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 { @@ -630,6 +637,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>, @@ -641,8 +649,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>, @@ -658,11 +668,11 @@ fn is_clamp_meta_pattern<'tcx>( path_to_local_id(peel_blocks(first_bin.left), first_hir_id) && path_to_local_id(peel_blocks(second_bin.left), 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, @@ -698,9 +708,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_div_ceil.rs b/clippy_lints/src/manual_div_ceil.rs index ed0cce754b95..409c96d8fec3 100644 --- a/clippy_lints/src/manual_div_ceil.rs +++ b/clippy_lints/src/manual_div_ceil.rs @@ -64,12 +64,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind && self.msrv.meets(cx, msrvs::DIV_CEIL) { + let ctxt = expr.span.ctxt(); + // (x + (y - 1)) / y if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind && inner_op.node == BinOpKind::Add && sub_op.node == BinOpKind::Sub && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, div_rhs) + && SpanlessEq::new(cx).eq_expr(ctxt, sub_lhs, div_rhs) { build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); return; @@ -80,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { && inner_op.node == BinOpKind::Add && sub_op.node == BinOpKind::Sub && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, div_rhs) + && SpanlessEq::new(cx).eq_expr(ctxt, sub_lhs, div_rhs) { build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); return; @@ -91,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { && inner_op.node == BinOpKind::Sub && add_op.node == BinOpKind::Add && check_literal(inner_rhs) - && check_eq_expr(cx, add_rhs, div_rhs) + && SpanlessEq::new(cx).eq_expr(ctxt, add_rhs, div_rhs) { build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); } @@ -153,10 +155,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/manual_is_power_of_two.rs b/clippy_lints/src/manual_is_power_of_two.rs index 4439a28763a2..283bc51e52e6 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_retain.rs b/clippy_lints/src/manual_retain.rs index 7fb88763e640..9c1c6f176fc3 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() && is_type_lang_item(cx, ty, 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 06ee00c2cef3..3ebab708510e 100644 --- a/clippy_lints/src/manual_rotate.rs +++ b/clippy_lints/src/manual_rotate.rs @@ -84,7 +84,7 @@ impl LateLintPass<'_> for ManualRotate { if l_shift_dir == r_shift_dir { return; } - if !clippy_utils::eq_expr_value(cx, l_expr, r_expr) { + if !clippy_utils::eq_expr_value(cx, expr.span.ctxt(), l_expr, r_expr) { return; } let Some(bit_width) = (match cx.typeck_results().expr_ty(expr).kind() { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 07cce4046ca4..ebd1d44321f4 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -16,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext as _}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{Symbol, sym}; +use rustc_span::{Symbol, SyntaxContext, sym}; use std::iter; declare_clippy_lint! { @@ -184,7 +184,7 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t { constant_length(cx, pattern).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/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index aaf559fc4439..0d2a98570983 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -12,32 +12,44 @@ use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{Span, SyntaxContext}; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; 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)) { for arm in arms { - check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv); + check_arm( + cx, + arm.span.ctxt(), + true, + arm.pat, + expr, + arm.body, + arm.guard, + Some(els_arm.body), + msrv, + ); } } } 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); + check_arm(cx, ctxt, false, pat, let_expr, body, None, else_expr, msrv); } #[allow(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, + ctxt: SyntaxContext, outer_is_match: bool, outer_pat: &'tcx Pat<'tcx>, outer_cond: &'tcx Expr<'tcx>, @@ -74,7 +86,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( diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index ae277da089fd..30dd2fbd2806 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -12,7 +12,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; -use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol}; +use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol, SyntaxContext}; use super::MATCH_SAME_ARMS; @@ -90,7 +90,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 6f49c5524118..43708b535cf3 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1139,6 +1139,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 3a2097c3df26..b65af6f847f7 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 is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { return is_res_lang_ctor(cx, path_res(cx, else_expr), 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 2da0f8341b17..d759ea3dc3dd 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -10,8 +10,8 @@ use rustc_hir::def::Res; use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; 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}; @@ -108,6 +108,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, @@ -147,7 +148,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> { && cx.typeck_results().expr_ty_adjusted(a) == cx.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, @@ -320,7 +321,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) @@ -394,6 +397,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<'_>, @@ -419,7 +423,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 5f6fb4c821d5..f2cbaa20a03d 100644 --- a/clippy_lints/src/methods/get_last_with_len.rs +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -28,7 +28,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/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index 32f32f1b2167..367346ff96b2 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 e13df18333e4..9489a75198a0 100644 --- a/clippy_lints/src/methods/range_zip_with_len.rs +++ b/clippy_lints/src/methods/range_zip_with_len.rs @@ -18,7 +18,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 20cf35363d13..1f6ad2ca5708 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -65,7 +65,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 19e9910dfe9d..6d518fe3bdc8 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 cf0c85990b15..bbdb18ff1e62 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -249,7 +249,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 { @@ -307,7 +310,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 854e927aa2f7..4498bdcae182 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 @@ -168,7 +169,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_applicability(cx, cond, "..", &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 10455d3b93a0..3a9aa0f0d1de 100644 --- a/clippy_lints/src/operators/const_comparisons.rs +++ b/clippy_lints/src/operators/const_comparisons.rs @@ -64,7 +64,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 54f50f11e034..090909d730fb 100644 --- a/clippy_lints/src/operators/double_comparison.rs +++ b/clippy_lints/src/operators/double_comparison.rs @@ -15,7 +15,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr }, _ => return, }; - if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) { + let ctxt = span.ctxt(); + if !(eq_expr_value(cx, ctxt, llhs, rlhs) && eq_expr_value(cx, ctxt, lrhs, rrhs)) { return; } macro_rules! lint_double_comparison { 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/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 d3a5a5dddfbe..a4564c7c6464 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -295,7 +295,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; @@ -480,7 +480,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: && ((is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) || 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))) .filter(|e| *e) .is_none() { diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs index ff6e6ef214b5..712095301c3c 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,8 +119,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) { ControlFlow::Break(insert_expr) } else { diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index f497d0700b8e..42ad3a714270 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -10,6 +10,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 57d5900b045e..5978a0d95b2c 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -11,6 +11,7 @@ use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; +use rustc_span::SyntaxContext; use rustc_span::source_map::Spanned; declare_clippy_lint! { @@ -145,7 +146,8 @@ declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN, STRING_SLICE]); 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 { @@ -162,7 +164,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { if let Some(p) = parent && let ExprKind::Assign(target, _, _) = p.kind // avoid duplicate matches - && SpanlessEq::new(cx).eq_expr(target, left) + && SpanlessEq::new(cx).eq_expr(ctxt, target, left) { return; } @@ -176,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } }, ExprKind::Assign(target, src, _) => { - if is_string(cx, target) && is_add(cx, src, target) { + if is_string(cx, target) && is_add(cx, ctxt, src, target) { span_lint( cx, STRING_ADD_ASSIGN, @@ -206,7 +208,7 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), 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 { @@ -214,7 +216,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 76ab3cdae22e..f362692ad927 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -99,12 +99,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(); @@ -190,14 +190,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); @@ -210,11 +211,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())), @@ -242,9 +244,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 @@ -285,10 +287,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 9182a55081f4..ddba18c72580 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 @@ -152,9 +152,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, @@ -247,7 +249,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<'_, '_> { @@ -377,9 +379,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 51116b5eba9e..b7f12fe7258a 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -6,7 +6,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! { @@ -63,15 +63,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); } } } @@ -79,12 +87,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()` @@ -137,10 +146,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) => path_to_local_id(expr, 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_calls.rs b/clippy_lints_internal/src/collapsible_calls.rs index 7c9e7286925e..f666c79f8156 100644 --- a/clippy_lints_internal/src/collapsible_calls.rs +++ b/clippy_lints_internal/src/collapsible_calls.rs @@ -91,8 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { let and_then_snippets = get_and_then_snippets(cx, call_cx.span, call_lint.span, call_sp.span, call_msg.span); let mut sle = SpanlessEq::new(cx).deny_side_effects(); + let ctxt = expr.span.ctxt(); match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(call_sp, &span_call_args[0]) => { + "span_suggestion" if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => { suggest_suggestion( cx, expr, @@ -100,11 +101,11 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { &span_suggestion_snippets(cx, span_call_args), ); }, - "span_help" if sle.eq_expr(call_sp, &span_call_args[0]) => { + "span_help" if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => { let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); }, - "span_note" if sle.eq_expr(call_sp, &span_call_args[0]) => { + "span_note" if sle.eq_expr(ctxt, call_sp, &span_call_args[0]) => { let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); }, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index b79e15cd7170..14ec73033307 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::tokenize_with_text; +use core::mem; use rustc_ast::ast; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; @@ -100,45 +101,56 @@ 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(), } } - 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 }`, @@ -147,7 +159,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 @@ -180,15 +202,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; @@ -283,19 +306,23 @@ 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_simple(left), - ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right), - ) - && 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_simple(left), + ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs) + .eval_simple(right), + ) + && l == r + { + return true; + } + }, + Some(false) => return false, + Some(true) => {}, } let is_eq = match ( @@ -348,7 +375,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) @@ -634,46 +666,68 @@ 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 { + 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 { 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_ctxt = left_data.call_site.ctxt(); - let right_ctxt = right_data.call_site.ctxt(); - if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() { - break; + let left = left_data.call_site.ctxt(); + let right = right_data.call_site.ctxt(); + if left == self.eval_ctxt && right == self.eval_ctxt { + if let ExpnKind::Macro(kind, _) = left_data.kind + && !(matches!(kind, MacroKind::Bang) + && eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| { + !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. }) + })) + { + // Either we can't get the tokens or the tokens are different. + return Some(false); + } + 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 { + return Some(true); + } + 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 } } @@ -771,8 +825,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. @@ -836,7 +895,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)] @@ -852,11 +911,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); }, @@ -873,7 +932,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); }, @@ -884,7 +943,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); }, @@ -907,7 +966,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); }, @@ -1058,11 +1117,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); @@ -1091,14 +1150,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(path.ident.name); }, QPath::LangItem(lang_item, ..) => { - std::mem::discriminant(lang_item).hash(&mut self.s); + mem::discriminant(lang_item).hash(&mut self.s); }, } // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } 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); @@ -1110,7 +1169,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); @@ -1126,12 +1185,12 @@ 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); - std::mem::discriminant(mutability).hash(&mut self.s); + mem::discriminant(by_ref).hash(&mut self.s); + mem::discriminant(mutability).hash(&mut self.s); if let Some(pat) = pat { self.hash_pat(pat); } @@ -1150,11 +1209,11 @@ 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, mu) => { self.hash_pat(pat); - std::mem::discriminant(mu).hash(&mut self.s); + mem::discriminant(mu).hash(&mut self.s); }, PatKind::Guard(pat, guard) => { self.hash_pat(pat); @@ -1223,12 +1282,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) => { @@ -1249,14 +1308,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); } @@ -1288,7 +1347,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 708491df7707..aa6e609f2138 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -121,7 +121,7 @@ use rustc_middle::ty::{ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; -use rustc_span::{InnerSpan, Span}; +use rustc_span::{InnerSpan, Span, SyntaxContext}; use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; @@ -564,19 +564,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. @@ -594,10 +598,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() { @@ -615,11 +623,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, } }