1
1
use crate :: consts:: { constant_context, constant_simple} ;
2
2
use crate :: utils:: differing_macro_contexts;
3
3
use rustc_ast:: ast:: InlineAsmTemplatePiece ;
4
+ use rustc_data_structures:: fx:: FxHashMap ;
4
5
use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
6
+ use rustc_hir:: def:: Res ;
5
7
use rustc_hir:: {
6
8
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 ,
8
10
PathSegment , QPath , Stmt , StmtKind , Ty , TyKind , TypeBinding ,
9
11
} ;
10
12
use rustc_lint:: LateContext ;
@@ -52,8 +54,47 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
52
54
}
53
55
}
54
56
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 {
57
98
match ( & left. kind , & right. kind ) {
58
99
( & StmtKind :: Local ( ref l) , & StmtKind :: Local ( ref r) ) => {
59
100
self . eq_pat ( & l. pat , & r. pat )
@@ -68,21 +109,21 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
68
109
}
69
110
70
111
/// 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 {
72
113
over ( & left. stmts , & right. stmts , |l, r| self . eq_stmt ( l, r) )
73
114
&& both ( & left. expr , & right. expr , |l, r| self . eq_expr ( l, r) )
74
115
}
75
116
76
117
#[ 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 ) {
79
120
return false ;
80
121
}
81
122
82
- if let Some ( typeck_results) = self . maybe_typeck_results {
123
+ if let Some ( typeck_results) = self . inner . maybe_typeck_results {
83
124
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) ,
86
127
) {
87
128
if l == r {
88
129
return true ;
@@ -98,10 +139,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
98
139
both ( & li. label , & ri. label , |l, r| l. ident . name == r. ident . name )
99
140
} ,
100
141
( & 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)
102
143
} ,
103
144
( & 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)
105
146
} ,
106
147
( & ExprKind :: Block ( ref l, _) , & ExprKind :: Block ( ref r, _) ) => self . eq_block ( l, r) ,
107
148
( & 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> {
116
157
} ,
117
158
( & ExprKind :: Box ( ref l) , & ExprKind :: Box ( ref r) ) => self . eq_expr ( l, r) ,
118
159
( & 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)
120
161
} ,
121
162
( & ExprKind :: Cast ( ref lx, ref lt) , & ExprKind :: Cast ( ref rx, ref rt) )
122
163
| ( & ExprKind :: Type ( ref lx, ref lt) , & ExprKind :: Type ( ref rx, ref rt) ) => {
@@ -139,19 +180,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
139
180
ls == rs
140
181
&& self . eq_expr ( le, re)
141
182
&& over ( la, ra, |l, r| {
142
- self . eq_expr ( & l. body , & r. body )
183
+ self . eq_pat ( & l. pat , & r. pat )
143
184
&& 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 )
145
186
} )
146
187
} ,
147
188
( & 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)
149
190
} ,
150
191
( & 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 ) ;
155
196
156
197
self . eq_expr ( le, re) && ll == rl
157
198
} ,
@@ -168,7 +209,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
168
209
( & ExprKind :: DropTemps ( ref le) , & ExprKind :: DropTemps ( ref re) ) => self . eq_expr ( le, re) ,
169
210
_ => false ,
170
211
} ;
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) )
172
213
}
173
214
174
215
fn eq_exprs ( & mut self , left : & [ Expr < ' _ > ] , right : & [ Expr < ' _ > ] ) -> bool {
@@ -199,13 +240,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
199
240
left. name == right. name
200
241
}
201
242
202
- pub fn eq_fieldpat ( & mut self , left : & FieldPat < ' _ > , right : & FieldPat < ' _ > ) -> bool {
243
+ fn eq_fieldpat ( & mut self , left : & FieldPat < ' _ > , right : & FieldPat < ' _ > ) -> bool {
203
244
let ( FieldPat { ident : li, pat : lp, .. } , FieldPat { ident : ri, pat : rp, .. } ) = ( & left, & right) ;
204
245
li. name == ri. name && self . eq_pat ( lp, rp)
205
246
}
206
247
207
248
/// 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 {
209
250
match ( & left. kind , & right. kind ) {
210
251
( & PatKind :: Box ( ref l) , & PatKind :: Box ( ref r) ) => self . eq_pat ( l, r) ,
211
252
( & PatKind :: Struct ( ref lp, ref la, ..) , & PatKind :: Struct ( ref rp, ref ra, ..) ) => {
@@ -214,8 +255,12 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
214
255
( & PatKind :: TupleStruct ( ref lp, ref la, ls) , & PatKind :: TupleStruct ( ref rp, ref ra, rs) ) => {
215
256
self . eq_qpath ( lp, rp) && over ( la, ra, |l, r| self . eq_pat ( l, r) ) && ls == rs
216
257
} ,
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
219
264
} ,
220
265
( & PatKind :: Path ( ref l) , & PatKind :: Path ( ref r) ) => self . eq_qpath ( l, r) ,
221
266
( & PatKind :: Lit ( ref l) , & PatKind :: Lit ( ref r) ) => self . eq_expr ( l, r) ,
@@ -251,8 +296,11 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
251
296
}
252
297
253
298
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
+ }
256
304
}
257
305
258
306
fn eq_path_parameters ( & mut self , left : & GenericArgs < ' _ > , right : & GenericArgs < ' _ > ) -> bool {
@@ -279,28 +327,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
279
327
left. ident . name == right. ident . name && both ( & left. args , & right. args , |l, r| self . eq_path_parameters ( l, r) )
280
328
}
281
329
282
- pub fn eq_ty ( & mut self , left : & Ty < ' _ > , right : & Ty < ' _ > ) -> bool {
330
+ fn eq_ty ( & mut self , left : & Ty < ' _ > , right : & Ty < ' _ > ) -> bool {
283
331
self . eq_ty_kind ( & left. kind , & right. kind )
284
332
}
285
333
286
334
#[ 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 {
288
336
match ( left, right) {
289
337
( & TyKind :: Slice ( ref l_vec) , & TyKind :: Slice ( ref r_vec) ) => self . eq_ty ( l_vec, r_vec) ,
290
338
( & 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 )
304
343
} ,
305
344
( & TyKind :: Ptr ( ref l_mut) , & TyKind :: Ptr ( ref r_mut) ) => {
306
345
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> {
667
706
// self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
668
707
}
669
708
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
+ } ,
674
718
}
675
719
}
676
720
0 commit comments