@@ -14,7 +14,7 @@ use rustc_hir::{
1414use rustc_lexer:: { tokenize, TokenKind } ;
1515use rustc_lint:: LateContext ;
1616use rustc_middle:: ty:: TypeckResults ;
17- use rustc_span:: { sym, BytePos , Symbol , SyntaxContext } ;
17+ use rustc_span:: { sym, BytePos , ExpnKind , MacroKind , Symbol , SyntaxContext } ;
1818use std:: hash:: { Hash , Hasher } ;
1919use std:: ops:: Range ;
2020
@@ -67,6 +67,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
6767 pub fn inter_expr ( & mut self ) -> HirEqInterExpr < ' _ , ' a , ' tcx > {
6868 HirEqInterExpr {
6969 inner : self ,
70+ left_ctxt : SyntaxContext :: root ( ) ,
71+ right_ctxt : SyntaxContext :: root ( ) ,
7072 locals : HirIdMap :: default ( ) ,
7173 }
7274 }
@@ -94,6 +96,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
9496
9597pub struct HirEqInterExpr < ' a , ' b , ' tcx > {
9698 inner : & ' a mut SpanlessEq < ' b , ' tcx > ,
99+ left_ctxt : SyntaxContext ,
100+ right_ctxt : SyntaxContext ,
97101
98102 // When binding are declared, the binding ID in the left expression is mapped to the one on the
99103 // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
@@ -128,6 +132,7 @@ impl HirEqInterExpr<'_, '_, '_> {
128132 }
129133
130134 /// Checks whether two blocks are the same.
135+ #[ expect( clippy:: similar_names) ]
131136 fn eq_block ( & mut self , left : & Block < ' _ > , right : & Block < ' _ > ) -> bool {
132137 use TokenKind :: { BlockComment , LineComment , Semi , Whitespace } ;
133138 if left. stmts . len ( ) != right. stmts . len ( ) {
@@ -166,7 +171,8 @@ impl HirEqInterExpr<'_, '_, '_> {
166171 // Can happen when macros expand to multiple statements, or rearrange statements.
167172 // Nothing in between the statements to check in this case.
168173 continue ;
169- } else if lstmt_span. lo < lstart || rstmt_span. lo < rstart {
174+ }
175+ if lstmt_span. lo < lstart || rstmt_span. lo < rstart {
170176 // Only one of the blocks had a weird macro.
171177 return false ;
172178 }
@@ -243,7 +249,7 @@ impl HirEqInterExpr<'_, '_, '_> {
243249
244250 #[ expect( clippy:: similar_names) ]
245251 pub fn eq_expr ( & mut self , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
246- if !self . inner . allow_side_effects && left. span . ctxt ( ) != right. span . ctxt ( ) {
252+ if !self . check_ctxt ( left. span . ctxt ( ) , right. span . ctxt ( ) ) {
247253 return false ;
248254 }
249255
@@ -476,6 +482,45 @@ impl HirEqInterExpr<'_, '_, '_> {
476482 fn eq_type_binding ( & mut self , left : & TypeBinding < ' _ > , right : & TypeBinding < ' _ > ) -> bool {
477483 left. ident . name == right. ident . name && self . eq_ty ( left. ty ( ) , right. ty ( ) )
478484 }
485+
486+ fn check_ctxt ( & mut self , left : SyntaxContext , right : SyntaxContext ) -> bool {
487+ if self . left_ctxt == left && self . right_ctxt == right {
488+ return true ;
489+ } else if self . left_ctxt == left || self . right_ctxt == right {
490+ // Only one context has changed. This can only happen if the two nodes are written differently.
491+ return false ;
492+ } else if left != SyntaxContext :: root ( ) {
493+ let mut left_data = left. outer_expn_data ( ) ;
494+ let mut right_data = right. outer_expn_data ( ) ;
495+ loop {
496+ use TokenKind :: { BlockComment , LineComment , Whitespace } ;
497+ if left_data. macro_def_id != right_data. macro_def_id
498+ || ( matches ! ( left_data. kind, ExpnKind :: Macro ( MacroKind :: Bang , name) if name == sym:: cfg)
499+ && !eq_span_tokens ( self . inner . cx , left_data. call_site , right_data. call_site , |t| {
500+ !matches ! ( t, Whitespace | LineComment { .. } | BlockComment { .. } )
501+ } ) )
502+ {
503+ // Either a different chain of macro calls, or different arguments to the `cfg` macro.
504+ return false ;
505+ }
506+ let left_ctxt = left_data. call_site . ctxt ( ) ;
507+ let right_ctxt = right_data. call_site . ctxt ( ) ;
508+ if left_ctxt == SyntaxContext :: root ( ) && right_ctxt == SyntaxContext :: root ( ) {
509+ break ;
510+ }
511+ if left_ctxt == SyntaxContext :: root ( ) || right_ctxt == SyntaxContext :: root ( ) {
512+ // Different lengths for the expansion stack. This can only happen if nodes are written differently,
513+ // or shouldn't be compared to start with.
514+ return false ;
515+ }
516+ left_data = left_ctxt. outer_expn_data ( ) ;
517+ right_data = right_ctxt. outer_expn_data ( ) ;
518+ }
519+ }
520+ self . left_ctxt = left;
521+ self . right_ctxt = right;
522+ true
523+ }
479524}
480525
481526/// Some simple reductions like `{ return }` => `return`
@@ -1075,6 +1120,7 @@ pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
10751120 h. finish ( )
10761121}
10771122
1123+ #[ expect( clippy:: similar_names) ]
10781124fn eq_span_tokens (
10791125 cx : & LateContext < ' _ > ,
10801126 left : impl SpanRange ,
0 commit comments