@@ -5,7 +5,7 @@ use crate::tokenize_with_text;
55use  rustc_ast:: ast:: InlineAsmTemplatePiece ; 
66use  rustc_data_structures:: fx:: FxHasher ; 
77use  rustc_hir:: MatchSource :: TryDesugar ; 
8- use  rustc_hir:: def:: Res ; 
8+ use  rustc_hir:: def:: { DefKind ,   Res } ; 
99use  rustc_hir:: { 
1010    ArrayLen ,  AssocItemConstraint ,  BinOpKind ,  BindingMode ,  Block ,  BodyId ,  Closure ,  ConstArg ,  ConstArgKind ,  Expr , 
1111    ExprField ,  ExprKind ,  FnRetTy ,  GenericArg ,  GenericArgs ,  HirId ,  HirIdMap ,  InlineAsmOperand ,  LetExpr ,  Lifetime , 
@@ -17,11 +17,33 @@ use rustc_middle::ty::TypeckResults;
1717use  rustc_span:: { BytePos ,  ExpnKind ,  MacroKind ,  Symbol ,  SyntaxContext ,  sym} ; 
1818use  std:: hash:: { Hash ,  Hasher } ; 
1919use  std:: ops:: Range ; 
20+ use  std:: slice; 
2021
2122/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but 
2223/// other conditions would make them equal. 
2324type  SpanlessEqCallback < ' a >  = dyn  FnMut ( & Expr < ' _ > ,  & Expr < ' _ > )  -> bool  + ' a ; 
2425
26+ /// Determines how paths are hashed and compared for equality. 
27+ #[ derive( Copy ,  Clone ,  Debug ,  Default ) ]  
28+ pub  enum  PathCheck  { 
29+     /// Paths must match exactly and are hashed by their exact HIR tree. 
30+ /// 
31+ /// Thus, `std::iter::Iterator` and `Iterator` are not considered equal even though they refer 
32+ /// to the same item. 
33+ #[ default]  
34+     Exact , 
35+     /// Paths are compared and hashed based on their resolution. 
36+ /// 
37+ /// They can appear different in the HIR tree but are still considered equal 
38+ /// and have equal hashes as long as they refer to the same item. 
39+ /// 
40+ /// Note that this is currently only partially implemented specifically for paths that are 
41+ /// resolved before type-checking, i.e. the final segment must have a non-error resolution. 
42+ /// If a path with an error resolution is encountered, it falls back to the default exact 
43+ /// matching behavior. 
44+ Resolution , 
45+ } 
46+ 
2547/// Type used to check whether two ast are the same. This is different from the 
2648/// operator `==` on ast types as this operator would compare true equality with 
2749/// ID and span. 
@@ -33,6 +55,7 @@ pub struct SpanlessEq<'a, 'tcx> {
3355    maybe_typeck_results :  Option < ( & ' tcx  TypeckResults < ' tcx > ,  & ' tcx  TypeckResults < ' tcx > ) > , 
3456    allow_side_effects :  bool , 
3557    expr_fallback :  Option < Box < SpanlessEqCallback < ' a > > > , 
58+     path_check :  PathCheck , 
3659} 
3760
3861impl < ' a ,  ' tcx >  SpanlessEq < ' a ,  ' tcx >  { 
@@ -42,6 +65,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
4265            maybe_typeck_results :  cx. maybe_typeck_results ( ) . map ( |x| ( x,  x) ) , 
4366            allow_side_effects :  true , 
4467            expr_fallback :  None , 
68+             path_check :  PathCheck :: default ( ) , 
4569        } 
4670    } 
4771
@@ -54,6 +78,16 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
5478        } 
5579    } 
5680
81+     /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more 
82+ /// details. 
83+ #[ must_use]  
84+     pub  fn  paths_by_resolution ( self )  -> Self  { 
85+         Self  { 
86+             path_check :  PathCheck :: Resolution , 
87+             ..self 
88+         } 
89+     } 
90+ 
5791    #[ must_use]  
5892    pub  fn  expr_fallback ( self ,  expr_fallback :  impl  FnMut ( & Expr < ' _ > ,  & Expr < ' _ > )  -> bool  + ' a )  -> Self  { 
5993        Self  { 
@@ -498,7 +532,7 @@ impl HirEqInterExpr<'_, '_, '_> {
498532        match  ( left. res ,  right. res )  { 
499533            ( Res :: Local ( l) ,  Res :: Local ( r) )  => l == r || self . locals . get ( & l)  == Some ( & r) , 
500534            ( Res :: Local ( _) ,  _)  | ( _,  Res :: Local ( _) )  => false , 
501-             _ => over ( left. segments ,  right. segments ,  |l ,  r|  self . eq_path_segment ( l ,  r ) ) , 
535+             _ => self . eq_path_segments ( left. segments ,  right. segments ) , 
502536        } 
503537    } 
504538
@@ -511,17 +545,39 @@ impl HirEqInterExpr<'_, '_, '_> {
511545        } 
512546    } 
513547
514-     pub  fn  eq_path_segments ( & mut  self ,  left :  & [ PathSegment < ' _ > ] ,  right :  & [ PathSegment < ' _ > ] )  -> bool  { 
515-         left. len ( )  == right. len ( )  && left. iter ( ) . zip ( right) . all ( |( l,  r) | self . eq_path_segment ( l,  r) ) 
548+     pub  fn  eq_path_segments < ' tcx > ( 
549+         & mut  self , 
550+         mut  left :  & ' tcx  [ PathSegment < ' tcx > ] , 
551+         mut  right :  & ' tcx  [ PathSegment < ' tcx > ] , 
552+     )  -> bool  { 
553+         if  let  PathCheck :: Resolution  = self . inner . path_check 
554+             && let  Some ( left_seg)  = generic_path_segments ( left) 
555+             && let  Some ( right_seg)  = generic_path_segments ( right) 
556+         { 
557+             // If we compare by resolution, then only check the last segments that could possibly have generic 
558+             // arguments 
559+             left = left_seg; 
560+             right = right_seg; 
561+         } 
562+ 
563+         over ( left,  right,  |l,  r| self . eq_path_segment ( l,  r) ) 
516564    } 
517565
518566    pub  fn  eq_path_segment ( & mut  self ,  left :  & PathSegment < ' _ > ,  right :  & PathSegment < ' _ > )  -> bool  { 
519-         // The == of idents doesn't work with different contexts, 
520-         // we have to be explicit about hygiene 
521-         left. ident . name  == right. ident . name 
522-             && both ( left. args . as_ref ( ) ,  right. args . as_ref ( ) ,  |l,  r| { 
523-                 self . eq_path_parameters ( l,  r) 
524-             } ) 
567+         if  !self . eq_path_parameters ( left. args ( ) ,  right. args ( ) )  { 
568+             return  false ; 
569+         } 
570+ 
571+         if  let  PathCheck :: Resolution  = self . inner . path_check 
572+             && left. res  != Res :: Err 
573+             && right. res  != Res :: Err 
574+         { 
575+             left. res  == right. res 
576+         }  else  { 
577+             // The == of idents doesn't work with different contexts, 
578+             // we have to be explicit about hygiene 
579+             left. ident . name  == right. ident . name 
580+         } 
525581    } 
526582
527583    pub  fn  eq_ty ( & mut  self ,  left :  & Ty < ' _ > ,  right :  & Ty < ' _ > )  -> bool  { 
@@ -684,6 +740,21 @@ pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) ->
684740    SpanlessEq :: new ( cx) . deny_side_effects ( ) . eq_expr ( left,  right) 
685741} 
686742
743+ /// Returns the segments of a path that might have generic parameters. 
744+ /// Usually just the last segment for free items, except for when the path resolves to an associated 
745+ /// item, in which case it is the last two 
746+ fn  generic_path_segments < ' tcx > ( segments :  & ' tcx  [ PathSegment < ' tcx > ] )  -> Option < & ' tcx  [ PathSegment < ' tcx > ] >  { 
747+     match  segments. last ( ) ?. res  { 
748+         Res :: Def ( DefKind :: AssocConst  | DefKind :: AssocFn  | DefKind :: AssocTy ,  _)  => { 
749+             // <Ty as module::Trait<T>>::assoc::<U> 
750+             //        ^^^^^^^^^^^^^^^^   ^^^^^^^^^^ segments: [module, Trait<T>, assoc<U>] 
751+             Some ( & segments[ segments. len ( ) . checked_sub ( 2 ) ?..] ) 
752+         } , 
753+         Res :: Err  => None , 
754+         _ => Some ( slice:: from_ref ( segments. last ( ) ?) ) , 
755+     } 
756+ } 
757+ 
687758/// Type used to hash an ast element. This is different from the `Hash` trait 
688759/// on ast types as this 
689760/// trait would consider IDs and spans. 
@@ -694,17 +765,29 @@ pub struct SpanlessHash<'a, 'tcx> {
694765cx :  & ' a  LateContext < ' tcx > , 
695766    maybe_typeck_results :  Option < & ' tcx  TypeckResults < ' tcx > > , 
696767    s :  FxHasher , 
768+     path_check :  PathCheck , 
697769} 
698770
699771impl < ' a ,  ' tcx >  SpanlessHash < ' a ,  ' tcx >  { 
700772    pub  fn  new ( cx :  & ' a  LateContext < ' tcx > )  -> Self  { 
701773        Self  { 
702774            cx, 
703775            maybe_typeck_results :  cx. maybe_typeck_results ( ) , 
776+             path_check :  PathCheck :: default ( ) , 
704777            s :  FxHasher :: default ( ) , 
705778        } 
706779    } 
707780
781+     /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more 
782+ /// details. 
783+ #[ must_use]  
784+     pub  fn  paths_by_resolution ( self )  -> Self  { 
785+         Self  { 
786+             path_check :  PathCheck :: Resolution , 
787+             ..self 
788+         } 
789+     } 
790+ 
708791    pub  fn  finish ( self )  -> u64  { 
709792        self . s . finish ( ) 
710793    } 
@@ -1042,9 +1125,19 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
10421125            // even though the binding names are different and they have different `HirId`s. 
10431126            Res :: Local ( _)  => 1_usize . hash ( & mut  self . s ) , 
10441127            _ => { 
1045-                 for  seg in  path. segments  { 
1046-                     self . hash_name ( seg. ident . name ) ; 
1047-                     self . hash_generic_args ( seg. args ( ) . args ) ; 
1128+                 if  let  PathCheck :: Resolution  = self . path_check 
1129+                     && let  [ ..,  last]  = path. segments 
1130+                     && let  Some ( segments)  = generic_path_segments ( path. segments ) 
1131+                 { 
1132+                     for  seg in  segments { 
1133+                         self . hash_generic_args ( seg. args ( ) . args ) ; 
1134+                     } 
1135+                     last. res . hash ( & mut  self . s ) ; 
1136+                 }  else  { 
1137+                     for  seg in  path. segments  { 
1138+                         self . hash_name ( seg. ident . name ) ; 
1139+                         self . hash_generic_args ( seg. args ( ) . args ) ; 
1140+                     } 
10481141                } 
10491142            } , 
10501143        } 
0 commit comments