11use crate :: consts:: { constant_context, constant_simple} ;
22use crate :: utils:: differing_macro_contexts;
33use rustc_ast:: ast:: InlineAsmTemplatePiece ;
4+ use rustc_data_structures:: fx:: FxHashMap ;
45use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
6+ use rustc_hir:: def:: Res ;
57use rustc_hir:: {
68 BinOpKind , Block , BlockCheckMode , BodyId , BorrowKind , CaptureBy , Expr , ExprKind , Field , FieldPat , FnRetTy ,
7- GenericArg , GenericArgs , Guard , InlineAsmOperand , Lifetime , LifetimeName , ParamName , Pat , PatKind , Path ,
9+ GenericArg , GenericArgs , Guard , HirId , InlineAsmOperand , Lifetime , LifetimeName , ParamName , Pat , PatKind , Path ,
810 PathSegment , QPath , Stmt , StmtKind , Ty , TyKind , TypeBinding ,
911} ;
1012use rustc_lint:: LateContext ;
@@ -52,8 +54,47 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
5254 }
5355 }
5456
55- /// Checks whether two statements are the same.
56- pub fn eq_stmt ( & mut self , left : & Stmt < ' _ > , right : & Stmt < ' _ > ) -> bool {
57+ /// Use this method to wrap comparisons that may involve inter-expression context.
58+ /// See `self.locals`.
59+ fn inter_expr ( & mut self ) -> HirEqInterExpr < ' _ , ' a , ' tcx > {
60+ HirEqInterExpr {
61+ inner : self ,
62+ locals : FxHashMap :: default ( ) ,
63+ }
64+ }
65+
66+ pub fn eq_block ( & mut self , left : & Block < ' _ > , right : & Block < ' _ > ) -> bool {
67+ self . inter_expr ( ) . eq_block ( left, right)
68+ }
69+
70+ pub fn eq_expr ( & mut self , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
71+ self . inter_expr ( ) . eq_expr ( left, right)
72+ }
73+
74+ pub fn eq_path_segment ( & mut self , left : & PathSegment < ' _ > , right : & PathSegment < ' _ > ) -> bool {
75+ self . inter_expr ( ) . eq_path_segment ( left, right)
76+ }
77+
78+ pub fn eq_path_segments ( & mut self , left : & [ PathSegment < ' _ > ] , right : & [ PathSegment < ' _ > ] ) -> bool {
79+ self . inter_expr ( ) . eq_path_segments ( left, right)
80+ }
81+
82+ pub fn eq_ty_kind ( & mut self , left : & TyKind < ' _ > , right : & TyKind < ' _ > ) -> bool {
83+ self . inter_expr ( ) . eq_ty_kind ( left, right)
84+ }
85+ }
86+
87+ struct HirEqInterExpr < ' a , ' b , ' tcx > {
88+ inner : & ' a mut SpanlessEq < ' b , ' tcx > ,
89+
90+ // When binding are declared, the binding ID in the left expression is mapped to the one on the
91+ // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
92+ // these blocks are considered equal since `x` is mapped to `y`.
93+ locals : FxHashMap < HirId , HirId > ,
94+ }
95+
96+ impl HirEqInterExpr < ' _ , ' _ , ' _ > {
97+ fn eq_stmt ( & mut self , left : & Stmt < ' _ > , right : & Stmt < ' _ > ) -> bool {
5798 match ( & left. kind , & right. kind ) {
5899 ( & StmtKind :: Local ( ref l) , & StmtKind :: Local ( ref r) ) => {
59100 self . eq_pat ( & l. pat , & r. pat )
@@ -68,21 +109,21 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
68109 }
69110
70111 /// Checks whether two blocks are the same.
71- pub fn eq_block ( & mut self , left : & Block < ' _ > , right : & Block < ' _ > ) -> bool {
112+ fn eq_block ( & mut self , left : & Block < ' _ > , right : & Block < ' _ > ) -> bool {
72113 over ( & left. stmts , & right. stmts , |l, r| self . eq_stmt ( l, r) )
73114 && both ( & left. expr , & right. expr , |l, r| self . eq_expr ( l, r) )
74115 }
75116
76117 #[ allow( clippy:: similar_names) ]
77- pub fn eq_expr ( & mut self , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
78- if !self . allow_side_effects && differing_macro_contexts ( left. span , right. span ) {
118+ fn eq_expr ( & mut self , left : & Expr < ' _ > , right : & Expr < ' _ > ) -> bool {
119+ if !self . inner . allow_side_effects && differing_macro_contexts ( left. span , right. span ) {
79120 return false ;
80121 }
81122
82- if let Some ( typeck_results) = self . maybe_typeck_results {
123+ if let Some ( typeck_results) = self . inner . maybe_typeck_results {
83124 if let ( Some ( l) , Some ( r) ) = (
84- constant_simple ( self . cx , typeck_results, left) ,
85- constant_simple ( self . cx , typeck_results, right) ,
125+ constant_simple ( self . inner . cx , typeck_results, left) ,
126+ constant_simple ( self . inner . cx , typeck_results, right) ,
86127 ) {
87128 if l == r {
88129 return true ;
@@ -98,10 +139,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
98139 both ( & li. label , & ri. label , |l, r| l. ident . name == r. ident . name )
99140 } ,
100141 ( & ExprKind :: Assign ( ref ll, ref lr, _) , & ExprKind :: Assign ( ref rl, ref rr, _) ) => {
101- self . allow_side_effects && self . eq_expr ( ll, rl) && self . eq_expr ( lr, rr)
142+ self . inner . allow_side_effects && self . eq_expr ( ll, rl) && self . eq_expr ( lr, rr)
102143 } ,
103144 ( & ExprKind :: AssignOp ( ref lo, ref ll, ref lr) , & ExprKind :: AssignOp ( ref ro, ref rl, ref rr) ) => {
104- self . allow_side_effects && lo. node == ro. node && self . eq_expr ( ll, rl) && self . eq_expr ( lr, rr)
145+ self . inner . allow_side_effects && lo. node == ro. node && self . eq_expr ( ll, rl) && self . eq_expr ( lr, rr)
105146 } ,
106147 ( & ExprKind :: Block ( ref l, _) , & ExprKind :: Block ( ref r, _) ) => self . eq_block ( l, r) ,
107148 ( & ExprKind :: Binary ( l_op, ref ll, ref lr) , & ExprKind :: Binary ( r_op, ref rl, ref rr) ) => {
@@ -116,7 +157,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
116157 } ,
117158 ( & ExprKind :: Box ( ref l) , & ExprKind :: Box ( ref r) ) => self . eq_expr ( l, r) ,
118159 ( & ExprKind :: Call ( l_fun, l_args) , & ExprKind :: Call ( r_fun, r_args) ) => {
119- self . allow_side_effects && self . eq_expr ( l_fun, r_fun) && self . eq_exprs ( l_args, r_args)
160+ self . inner . allow_side_effects && self . eq_expr ( l_fun, r_fun) && self . eq_exprs ( l_args, r_args)
120161 } ,
121162 ( & ExprKind :: Cast ( ref lx, ref lt) , & ExprKind :: Cast ( ref rx, ref rt) )
122163 | ( & ExprKind :: Type ( ref lx, ref lt) , & ExprKind :: Type ( ref rx, ref rt) ) => {
@@ -139,19 +180,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
139180 ls == rs
140181 && self . eq_expr ( le, re)
141182 && over ( la, ra, |l, r| {
142- self . eq_expr ( & l. body , & r. body )
183+ self . eq_pat ( & l. pat , & r. pat )
143184 && both ( & l. guard , & r. guard , |l, r| self . eq_guard ( l, r) )
144- && self . eq_pat ( & l. pat , & r. pat )
185+ && self . eq_expr ( & l. body , & r. body )
145186 } )
146187 } ,
147188 ( & ExprKind :: MethodCall ( l_path, _, l_args, _) , & ExprKind :: MethodCall ( r_path, _, r_args, _) ) => {
148- self . allow_side_effects && self . eq_path_segment ( l_path, r_path) && self . eq_exprs ( l_args, r_args)
189+ self . inner . allow_side_effects && self . eq_path_segment ( l_path, r_path) && self . eq_exprs ( l_args, r_args)
149190 } ,
150191 ( & ExprKind :: Repeat ( ref le, ref ll_id) , & ExprKind :: Repeat ( ref re, ref rl_id) ) => {
151- let mut celcx = constant_context ( self . cx , self . cx . tcx . typeck_body ( ll_id. body ) ) ;
152- let ll = celcx. expr ( & self . cx . tcx . hir ( ) . body ( ll_id. body ) . value ) ;
153- let mut celcx = constant_context ( self . cx , self . cx . tcx . typeck_body ( rl_id. body ) ) ;
154- let rl = celcx. expr ( & self . cx . tcx . hir ( ) . body ( rl_id. body ) . value ) ;
192+ let mut celcx = constant_context ( self . inner . cx , self . inner . cx . tcx . typeck_body ( ll_id. body ) ) ;
193+ let ll = celcx. expr ( & self . inner . cx . tcx . hir ( ) . body ( ll_id. body ) . value ) ;
194+ let mut celcx = constant_context ( self . inner . cx , self . inner . cx . tcx . typeck_body ( rl_id. body ) ) ;
195+ let rl = celcx. expr ( & self . inner . cx . tcx . hir ( ) . body ( rl_id. body ) . value ) ;
155196
156197 self . eq_expr ( le, re) && ll == rl
157198 } ,
@@ -168,7 +209,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
168209 ( & ExprKind :: DropTemps ( ref le) , & ExprKind :: DropTemps ( ref re) ) => self . eq_expr ( le, re) ,
169210 _ => false ,
170211 } ;
171- is_eq || self . expr_fallback . as_ref ( ) . map_or ( false , |f| f ( left, right) )
212+ is_eq || self . inner . expr_fallback . as_ref ( ) . map_or ( false , |f| f ( left, right) )
172213 }
173214
174215 fn eq_exprs ( & mut self , left : & [ Expr < ' _ > ] , right : & [ Expr < ' _ > ] ) -> bool {
@@ -199,13 +240,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
199240 left. name == right. name
200241 }
201242
202- pub fn eq_fieldpat ( & mut self , left : & FieldPat < ' _ > , right : & FieldPat < ' _ > ) -> bool {
243+ fn eq_fieldpat ( & mut self , left : & FieldPat < ' _ > , right : & FieldPat < ' _ > ) -> bool {
203244 let ( FieldPat { ident : li, pat : lp, .. } , FieldPat { ident : ri, pat : rp, .. } ) = ( & left, & right) ;
204245 li. name == ri. name && self . eq_pat ( lp, rp)
205246 }
206247
207248 /// Checks whether two patterns are the same.
208- pub fn eq_pat ( & mut self , left : & Pat < ' _ > , right : & Pat < ' _ > ) -> bool {
249+ fn eq_pat ( & mut self , left : & Pat < ' _ > , right : & Pat < ' _ > ) -> bool {
209250 match ( & left. kind , & right. kind ) {
210251 ( & PatKind :: Box ( ref l) , & PatKind :: Box ( ref r) ) => self . eq_pat ( l, r) ,
211252 ( & PatKind :: Struct ( ref lp, ref la, ..) , & PatKind :: Struct ( ref rp, ref ra, ..) ) => {
@@ -214,8 +255,12 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
214255 ( & PatKind :: TupleStruct ( ref lp, ref la, ls) , & PatKind :: TupleStruct ( ref rp, ref ra, rs) ) => {
215256 self . eq_qpath ( lp, rp) && over ( la, ra, |l, r| self . eq_pat ( l, r) ) && ls == rs
216257 } ,
217- ( & PatKind :: Binding ( ref lb, .., ref li, ref lp) , & PatKind :: Binding ( ref rb, .., ref ri, ref rp) ) => {
218- lb == rb && li. name == ri. name && both ( lp, rp, |l, r| self . eq_pat ( l, r) )
258+ ( & PatKind :: Binding ( lb, li, _, ref lp) , & PatKind :: Binding ( rb, ri, _, ref rp) ) => {
259+ let eq = lb == rb && both ( lp, rp, |l, r| self . eq_pat ( l, r) ) ;
260+ if eq {
261+ self . locals . insert ( li, ri) ;
262+ }
263+ eq
219264 } ,
220265 ( & PatKind :: Path ( ref l) , & PatKind :: Path ( ref r) ) => self . eq_qpath ( l, r) ,
221266 ( & PatKind :: Lit ( ref l) , & PatKind :: Lit ( ref r) ) => self . eq_expr ( l, r) ,
@@ -251,8 +296,11 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
251296 }
252297
253298 fn eq_path ( & mut self , left : & Path < ' _ > , right : & Path < ' _ > ) -> bool {
254- left. is_global ( ) == right. is_global ( )
255- && over ( & left. segments , & right. segments , |l, r| self . eq_path_segment ( l, r) )
299+ match ( left. res , right. res ) {
300+ ( Res :: Local ( l) , Res :: Local ( r) ) => l == r || self . locals . get ( & l) == Some ( & r) ,
301+ ( Res :: Local ( _) , _) | ( _, Res :: Local ( _) ) => false ,
302+ _ => over ( & left. segments , & right. segments , |l, r| self . eq_path_segment ( l, r) ) ,
303+ }
256304 }
257305
258306 fn eq_path_parameters ( & mut self , left : & GenericArgs < ' _ > , right : & GenericArgs < ' _ > ) -> bool {
@@ -279,28 +327,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
279327 left. ident . name == right. ident . name && both ( & left. args , & right. args , |l, r| self . eq_path_parameters ( l, r) )
280328 }
281329
282- pub fn eq_ty ( & mut self , left : & Ty < ' _ > , right : & Ty < ' _ > ) -> bool {
330+ fn eq_ty ( & mut self , left : & Ty < ' _ > , right : & Ty < ' _ > ) -> bool {
283331 self . eq_ty_kind ( & left. kind , & right. kind )
284332 }
285333
286334 #[ allow( clippy:: similar_names) ]
287- pub fn eq_ty_kind ( & mut self , left : & TyKind < ' _ > , right : & TyKind < ' _ > ) -> bool {
335+ fn eq_ty_kind ( & mut self , left : & TyKind < ' _ > , right : & TyKind < ' _ > ) -> bool {
288336 match ( left, right) {
289337 ( & TyKind :: Slice ( ref l_vec) , & TyKind :: Slice ( ref r_vec) ) => self . eq_ty ( l_vec, r_vec) ,
290338 ( & TyKind :: Array ( ref lt, ref ll_id) , & TyKind :: Array ( ref rt, ref rl_id) ) => {
291- let old_maybe_typeck_results = self . maybe_typeck_results ;
292-
293- let mut celcx = constant_context ( self . cx , self . cx . tcx . typeck_body ( ll_id. body ) ) ;
294- self . maybe_typeck_results = Some ( self . cx . tcx . typeck_body ( ll_id. body ) ) ;
295- let ll = celcx. expr ( & self . cx . tcx . hir ( ) . body ( ll_id. body ) . value ) ;
296-
297- let mut celcx = constant_context ( self . cx , self . cx . tcx . typeck_body ( rl_id. body ) ) ;
298- self . maybe_typeck_results = Some ( self . cx . tcx . typeck_body ( rl_id. body ) ) ;
299- let rl = celcx. expr ( & self . cx . tcx . hir ( ) . body ( rl_id. body ) . value ) ;
300-
301- let eq_ty = self . eq_ty ( lt, rt) ;
302- self . maybe_typeck_results = old_maybe_typeck_results;
303- eq_ty && ll == rl
339+ let cx = self . inner . cx ;
340+ let eval_const =
341+ |body| constant_context ( cx, cx. tcx . typeck_body ( body) ) . expr ( & cx. tcx . hir ( ) . body ( body) . value ) ;
342+ self . eq_ty ( lt, rt) && eval_const ( ll_id. body ) == eval_const ( rl_id. body )
304343 } ,
305344 ( & TyKind :: Ptr ( ref l_mut) , & TyKind :: Ptr ( ref r_mut) ) => {
306345 l_mut. mutbl == r_mut. mutbl && self . eq_ty ( & * l_mut. ty , & * r_mut. ty )
@@ -667,10 +706,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
667706 // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
668707 }
669708
670- pub fn hash_path ( & mut self , p : & Path < ' _ > ) {
671- p. is_global ( ) . hash ( & mut self . s ) ;
672- for p in p. segments {
673- self . hash_name ( p. ident . name ) ;
709+ pub fn hash_path ( & mut self , path : & Path < ' _ > ) {
710+ match path. res {
711+ // constant hash since equality is dependant on inter-expression context
712+ Res :: Local ( _) => 1_usize . hash ( & mut self . s ) ,
713+ _ => {
714+ for seg in path. segments {
715+ self . hash_name ( seg. ident . name ) ;
716+ }
717+ } ,
674718 }
675719 }
676720
0 commit comments