]>
Commit | Line | Data |
---|---|---|
04454e1e | 1 | use crate::consts::constant_simple; |
064997fb | 2 | use crate::macros::macro_backtrace; |
49aad941 FG |
3 | use crate::source::{get_source_text, snippet_opt, walk_span_to_context, SpanRange}; |
4 | use crate::tokenize_with_text; | |
f20569fa | 5 | use rustc_ast::ast::InlineAsmTemplatePiece; |
17df50a5 | 6 | use rustc_data_structures::fx::FxHasher; |
f20569fa | 7 | use rustc_hir::def::Res; |
cdc7bbd5 | 8 | use rustc_hir::HirIdMap; |
f20569fa | 9 | use rustc_hir::{ |
f2b60f7d | 10 | ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, |
487cf647 FG |
11 | GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, |
12 | PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, | |
f20569fa XL |
13 | }; |
14 | use rustc_lexer::{tokenize, TokenKind}; | |
15 | use rustc_lint::LateContext; | |
f20569fa | 16 | use rustc_middle::ty::TypeckResults; |
49aad941 | 17 | use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext}; |
17df50a5 | 18 | use std::hash::{Hash, Hasher}; |
49aad941 | 19 | use std::ops::Range; |
f20569fa | 20 | |
064997fb FG |
21 | /// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but |
22 | /// other conditions would make them equal. | |
23 | type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a; | |
24 | ||
f20569fa | 25 | /// Type used to check whether two ast are the same. This is different from the |
04454e1e FG |
26 | /// operator `==` on ast types as this operator would compare true equality with |
27 | /// ID and span. | |
f20569fa XL |
28 | /// |
29 | /// Note that some expressions kinds are not considered but could be added. | |
30 | pub struct SpanlessEq<'a, 'tcx> { | |
31 | /// Context used to evaluate constant expressions. | |
32 | cx: &'a LateContext<'tcx>, | |
04454e1e | 33 | maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>, |
f20569fa | 34 | allow_side_effects: bool, |
064997fb | 35 | expr_fallback: Option<Box<SpanlessEqCallback<'a>>>, |
f20569fa XL |
36 | } |
37 | ||
38 | impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { | |
39 | pub fn new(cx: &'a LateContext<'tcx>) -> Self { | |
40 | Self { | |
41 | cx, | |
04454e1e | 42 | maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)), |
f20569fa XL |
43 | allow_side_effects: true, |
44 | expr_fallback: None, | |
45 | } | |
46 | } | |
47 | ||
48 | /// Consider expressions containing potential side effects as not equal. | |
a2a8927a | 49 | #[must_use] |
f20569fa XL |
50 | pub fn deny_side_effects(self) -> Self { |
51 | Self { | |
52 | allow_side_effects: false, | |
53 | ..self | |
54 | } | |
55 | } | |
56 | ||
a2a8927a | 57 | #[must_use] |
f20569fa XL |
58 | pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self { |
59 | Self { | |
60 | expr_fallback: Some(Box::new(expr_fallback)), | |
61 | ..self | |
62 | } | |
63 | } | |
64 | ||
65 | /// Use this method to wrap comparisons that may involve inter-expression context. | |
66 | /// See `self.locals`. | |
cdc7bbd5 | 67 | pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { |
f20569fa XL |
68 | HirEqInterExpr { |
69 | inner: self, | |
49aad941 FG |
70 | left_ctxt: SyntaxContext::root(), |
71 | right_ctxt: SyntaxContext::root(), | |
cdc7bbd5 | 72 | locals: HirIdMap::default(), |
f20569fa XL |
73 | } |
74 | } | |
75 | ||
76 | pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { | |
77 | self.inter_expr().eq_block(left, right) | |
78 | } | |
79 | ||
80 | pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { | |
81 | self.inter_expr().eq_expr(left, right) | |
82 | } | |
83 | ||
5e7ed085 FG |
84 | pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { |
85 | self.inter_expr().eq_path(left, right) | |
86 | } | |
87 | ||
f20569fa XL |
88 | pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { |
89 | self.inter_expr().eq_path_segment(left, right) | |
90 | } | |
91 | ||
92 | pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { | |
93 | self.inter_expr().eq_path_segments(left, right) | |
94 | } | |
f20569fa XL |
95 | } |
96 | ||
cdc7bbd5 | 97 | pub struct HirEqInterExpr<'a, 'b, 'tcx> { |
f20569fa | 98 | inner: &'a mut SpanlessEq<'b, 'tcx>, |
49aad941 FG |
99 | left_ctxt: SyntaxContext, |
100 | right_ctxt: SyntaxContext, | |
f20569fa XL |
101 | |
102 | // When binding are declared, the binding ID in the left expression is mapped to the one on the | |
103 | // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, | |
104 | // these blocks are considered equal since `x` is mapped to `y`. | |
923072b8 | 105 | pub locals: HirIdMap<HirId>, |
f20569fa XL |
106 | } |
107 | ||
108 | impl HirEqInterExpr<'_, '_, '_> { | |
cdc7bbd5 | 109 | pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { |
f20569fa | 110 | match (&left.kind, &right.kind) { |
17df50a5 | 111 | (&StmtKind::Local(l), &StmtKind::Local(r)) => { |
cdc7bbd5 XL |
112 | // This additional check ensures that the type of the locals are equivalent even if the init |
113 | // expression or type have some inferred parts. | |
04454e1e FG |
114 | if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { |
115 | let l_ty = typeck_lhs.pat_ty(l.pat); | |
116 | let r_ty = typeck_rhs.pat_ty(r.pat); | |
5099ac24 | 117 | if l_ty != r_ty { |
cdc7bbd5 XL |
118 | return false; |
119 | } | |
120 | } | |
121 | ||
487cf647 | 122 | // eq_pat adds the HirIds to the locals map. We therefore call it last to make sure that |
cdc7bbd5 XL |
123 | // these only get added if the init and type is equal. |
124 | both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) | |
f20569fa | 125 | && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) |
064997fb | 126 | && both(&l.els, &r.els, |l, r| self.eq_block(l, r)) |
17df50a5 | 127 | && self.eq_pat(l.pat, r.pat) |
f20569fa | 128 | }, |
17df50a5 | 129 | (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r), |
f20569fa XL |
130 | _ => false, |
131 | } | |
132 | } | |
133 | ||
134 | /// Checks whether two blocks are the same. | |
49aad941 | 135 | #[expect(clippy::similar_names)] |
f20569fa | 136 | fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { |
49aad941 FG |
137 | use TokenKind::{BlockComment, LineComment, Semi, Whitespace}; |
138 | if left.stmts.len() != right.stmts.len() { | |
139 | return false; | |
140 | } | |
141 | let lspan = left.span.data(); | |
142 | let rspan = right.span.data(); | |
143 | if lspan.ctxt != SyntaxContext::root() && rspan.ctxt != SyntaxContext::root() { | |
144 | // Don't try to check in between statements inside macros. | |
145 | return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right)) | |
146 | && both(&left.expr, &right.expr, |left, right| self.eq_expr(left, right)); | |
147 | } | |
148 | if lspan.ctxt != rspan.ctxt { | |
149 | return false; | |
150 | } | |
151 | ||
152 | let mut lstart = lspan.lo; | |
153 | let mut rstart = rspan.lo; | |
154 | ||
155 | for (left, right) in left.stmts.iter().zip(right.stmts) { | |
156 | if !self.eq_stmt(left, right) { | |
157 | return false; | |
158 | } | |
159 | ||
160 | // Try to detect any `cfg`ed statements or empty macro expansions. | |
161 | let Some(lstmt_span) = walk_span_to_context(left.span, lspan.ctxt) else { | |
162 | return false; | |
163 | }; | |
164 | let Some(rstmt_span) = walk_span_to_context(right.span, rspan.ctxt) else { | |
165 | return false; | |
166 | }; | |
167 | let lstmt_span = lstmt_span.data(); | |
168 | let rstmt_span = rstmt_span.data(); | |
169 | ||
170 | if lstmt_span.lo < lstart && rstmt_span.lo < rstart { | |
171 | // Can happen when macros expand to multiple statements, or rearrange statements. | |
172 | // Nothing in between the statements to check in this case. | |
173 | continue; | |
174 | } | |
175 | if lstmt_span.lo < lstart || rstmt_span.lo < rstart { | |
176 | // Only one of the blocks had a weird macro. | |
177 | return false; | |
178 | } | |
179 | if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| { | |
180 | !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) | |
181 | }) { | |
182 | return false; | |
183 | } | |
184 | ||
185 | lstart = lstmt_span.hi; | |
186 | rstart = rstmt_span.hi; | |
187 | } | |
188 | ||
189 | let (lend, rend) = match (left.expr, right.expr) { | |
190 | (Some(left), Some(right)) => { | |
191 | if !self.eq_expr(left, right) { | |
192 | return false; | |
193 | } | |
194 | let Some(lexpr_span) = walk_span_to_context(left.span, lspan.ctxt) else { | |
195 | return false; | |
196 | }; | |
197 | let Some(rexpr_span) = walk_span_to_context(right.span, rspan.ctxt) else { | |
198 | return false; | |
199 | }; | |
200 | (lexpr_span.lo(), rexpr_span.lo()) | |
201 | }, | |
202 | (None, None) => (lspan.hi, rspan.hi), | |
203 | (Some(_), None) | (None, Some(_)) => return false, | |
204 | }; | |
205 | ||
206 | if lend < lstart && rend < rstart { | |
207 | // Can happen when macros rearrange the input. | |
208 | // Nothing in between the statements to check in this case. | |
209 | return true; | |
210 | } else if lend < lstart || rend < rstart { | |
211 | // Only one of the blocks had a weird macro | |
212 | return false; | |
f20569fa | 213 | } |
49aad941 FG |
214 | eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| { |
215 | !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi) | |
216 | }) | |
f20569fa XL |
217 | } |
218 | ||
064997fb FG |
219 | fn should_ignore(&mut self, expr: &Expr<'_>) -> bool { |
220 | macro_backtrace(expr.span).last().map_or(false, |macro_call| { | |
221 | matches!( | |
222 | &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id), | |
223 | Some(sym::todo_macro | sym::unimplemented_macro) | |
224 | ) | |
225 | }) | |
226 | } | |
227 | ||
a2a8927a | 228 | pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool { |
5099ac24 FG |
229 | match (left, right) { |
230 | (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true, | |
231 | (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body), | |
232 | (_, _) => false, | |
233 | } | |
a2a8927a XL |
234 | } |
235 | ||
17df50a5 | 236 | pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool { |
04454e1e FG |
237 | // swap out TypeckResults when hashing a body |
238 | let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace(( | |
239 | self.inner.cx.tcx.typeck_body(left), | |
240 | self.inner.cx.tcx.typeck_body(right), | |
241 | )); | |
242 | let res = self.eq_expr( | |
f2b60f7d FG |
243 | self.inner.cx.tcx.hir().body(left).value, |
244 | self.inner.cx.tcx.hir().body(right).value, | |
04454e1e FG |
245 | ); |
246 | self.inner.maybe_typeck_results = old_maybe_typeck_results; | |
247 | res | |
17df50a5 XL |
248 | } |
249 | ||
923072b8 | 250 | #[expect(clippy::similar_names)] |
cdc7bbd5 | 251 | pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { |
49aad941 | 252 | if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) { |
f20569fa XL |
253 | return false; |
254 | } | |
255 | ||
04454e1e | 256 | if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { |
f20569fa | 257 | if let (Some(l), Some(r)) = ( |
04454e1e FG |
258 | constant_simple(self.inner.cx, typeck_lhs, left), |
259 | constant_simple(self.inner.cx, typeck_rhs, right), | |
f20569fa XL |
260 | ) { |
261 | if l == r { | |
262 | return true; | |
263 | } | |
264 | } | |
265 | } | |
266 | ||
267 | let is_eq = match ( | |
a2a8927a XL |
268 | reduce_exprkind(self.inner.cx, &left.kind), |
269 | reduce_exprkind(self.inner.cx, &right.kind), | |
f20569fa | 270 | ) { |
17df50a5 | 271 | (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => { |
f20569fa XL |
272 | lb == rb && l_mut == r_mut && self.eq_expr(le, re) |
273 | }, | |
274 | (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => { | |
275 | both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) | |
276 | }, | |
17df50a5 | 277 | (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => { |
f20569fa XL |
278 | self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) |
279 | }, | |
17df50a5 | 280 | (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => { |
f20569fa XL |
281 | self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) |
282 | }, | |
17df50a5 XL |
283 | (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r), |
284 | (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => { | |
f20569fa XL |
285 | l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) |
286 | || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| { | |
287 | l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) | |
288 | }) | |
289 | }, | |
290 | (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => { | |
291 | both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) | |
292 | && both(le, re, |l, r| self.eq_expr(l, r)) | |
293 | }, | |
f20569fa XL |
294 | (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { |
295 | self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) | |
296 | }, | |
17df50a5 | 297 | (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => { |
f20569fa XL |
298 | self.eq_expr(lx, rx) && self.eq_ty(lt, rt) |
299 | }, | |
17df50a5 | 300 | (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => { |
f20569fa XL |
301 | l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) |
302 | }, | |
17df50a5 XL |
303 | (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), |
304 | (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { | |
a2a8927a XL |
305 | self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r)) |
306 | }, | |
307 | (&ExprKind::Let(l), &ExprKind::Let(r)) => { | |
308 | self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) | |
f20569fa | 309 | }, |
487cf647 | 310 | (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, |
17df50a5 | 311 | (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { |
f20569fa XL |
312 | lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) |
313 | }, | |
17df50a5 | 314 | (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => { |
f20569fa XL |
315 | ls == rs |
316 | && self.eq_expr(le, re) | |
317 | && over(la, ra, |l, r| { | |
17df50a5 | 318 | self.eq_pat(l.pat, r.pat) |
f20569fa | 319 | && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r)) |
17df50a5 | 320 | && self.eq_expr(l.body, r.body) |
f20569fa XL |
321 | }) |
322 | }, | |
f2b60f7d FG |
323 | ( |
324 | &ExprKind::MethodCall(l_path, l_receiver, l_args, _), | |
325 | &ExprKind::MethodCall(r_path, r_receiver, r_args, _), | |
326 | ) => { | |
327 | self.inner.allow_side_effects | |
328 | && self.eq_path_segment(l_path, r_path) | |
329 | && self.eq_expr(l_receiver, r_receiver) | |
330 | && self.eq_exprs(l_args, r_args) | |
f20569fa | 331 | }, |
a2a8927a XL |
332 | (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { |
333 | self.eq_expr(le, re) && self.eq_array_length(ll, rl) | |
f20569fa | 334 | }, |
487cf647 FG |
335 | (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)), |
336 | (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), | |
17df50a5 | 337 | (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { |
f20569fa XL |
338 | self.eq_qpath(l_path, r_path) |
339 | && both(lo, ro, |l, r| self.eq_expr(l, r)) | |
cdc7bbd5 | 340 | && over(lf, rf, |l, r| self.eq_expr_field(l, r)) |
f20569fa XL |
341 | }, |
342 | (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup), | |
17df50a5 | 343 | (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re), |
f20569fa | 344 | (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r), |
17df50a5 | 345 | (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re), |
49aad941 FG |
346 | (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => { |
347 | self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name) | |
348 | }, | |
f20569fa XL |
349 | _ => false, |
350 | }; | |
064997fb FG |
351 | (is_eq && (!self.should_ignore(left) || !self.should_ignore(right))) |
352 | || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right)) | |
f20569fa XL |
353 | } |
354 | ||
355 | fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { | |
356 | over(left, right, |l, r| self.eq_expr(l, r)) | |
357 | } | |
358 | ||
cdc7bbd5 | 359 | fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool { |
17df50a5 | 360 | left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr) |
f20569fa XL |
361 | } |
362 | ||
363 | fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool { | |
364 | match (left, right) { | |
365 | (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r), | |
923072b8 FG |
366 | (Guard::IfLet(l), Guard::IfLet(r)) => { |
367 | self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) | |
368 | }, | |
f20569fa XL |
369 | _ => false, |
370 | } | |
371 | } | |
372 | ||
373 | fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool { | |
374 | match (left, right) { | |
17df50a5 | 375 | (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body), |
f20569fa XL |
376 | (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt), |
377 | (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty), | |
94222f64 | 378 | (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()), |
f20569fa XL |
379 | _ => false, |
380 | } | |
381 | } | |
382 | ||
383 | fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool { | |
487cf647 | 384 | left.res == right.res |
f20569fa XL |
385 | } |
386 | ||
cdc7bbd5 | 387 | fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool { |
f20569fa XL |
388 | let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right); |
389 | li.name == ri.name && self.eq_pat(lp, rp) | |
390 | } | |
391 | ||
392 | /// Checks whether two patterns are the same. | |
393 | fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { | |
394 | match (&left.kind, &right.kind) { | |
17df50a5 XL |
395 | (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r), |
396 | (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => { | |
cdc7bbd5 | 397 | self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r)) |
f20569fa | 398 | }, |
17df50a5 | 399 | (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => { |
f20569fa XL |
400 | self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs |
401 | }, | |
402 | (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => { | |
403 | let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r)); | |
404 | if eq { | |
405 | self.locals.insert(li, ri); | |
406 | } | |
407 | eq | |
408 | }, | |
487cf647 | 409 | (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), |
17df50a5 XL |
410 | (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), |
411 | (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), | |
f20569fa XL |
412 | (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { |
413 | both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri) | |
414 | }, | |
17df50a5 XL |
415 | (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re), |
416 | (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => { | |
f20569fa XL |
417 | over(ls, rs, |l, r| self.eq_pat(l, r)) |
418 | && over(le, re, |l, r| self.eq_pat(l, r)) | |
419 | && both(li, ri, |l, r| self.eq_pat(l, r)) | |
420 | }, | |
421 | (&PatKind::Wild, &PatKind::Wild) => true, | |
422 | _ => false, | |
423 | } | |
424 | } | |
425 | ||
923072b8 | 426 | #[expect(clippy::similar_names)] |
f20569fa XL |
427 | fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { |
428 | match (left, right) { | |
17df50a5 | 429 | (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { |
f20569fa XL |
430 | both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath) |
431 | }, | |
17df50a5 | 432 | (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => { |
f20569fa XL |
433 | self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg) |
434 | }, | |
a2a8927a | 435 | (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item, |
f20569fa XL |
436 | _ => false, |
437 | } | |
438 | } | |
439 | ||
5e7ed085 | 440 | pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { |
f20569fa XL |
441 | match (left.res, right.res) { |
442 | (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r), | |
443 | (Res::Local(_), _) | (_, Res::Local(_)) => false, | |
17df50a5 | 444 | _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)), |
f20569fa XL |
445 | } |
446 | } | |
447 | ||
448 | fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { | |
353b0b11 | 449 | if left.parenthesized == right.parenthesized { |
17df50a5 XL |
450 | over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work |
451 | && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r)) | |
f20569fa XL |
452 | } else { |
453 | false | |
454 | } | |
455 | } | |
456 | ||
457 | pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { | |
458 | left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r)) | |
459 | } | |
460 | ||
461 | pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { | |
462 | // The == of idents doesn't work with different contexts, | |
463 | // we have to be explicit about hygiene | |
464 | left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) | |
465 | } | |
466 | ||
5099ac24 | 467 | pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { |
cdc7bbd5 | 468 | match (&left.kind, &right.kind) { |
17df50a5 | 469 | (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), |
5099ac24 | 470 | (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), |
487cf647 | 471 | (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), |
9c376795 | 472 | (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => { |
923072b8 | 473 | l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) |
f20569fa | 474 | }, |
487cf647 | 475 | (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), |
17df50a5 | 476 | (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), |
f20569fa XL |
477 | (&TyKind::Infer, &TyKind::Infer) => true, |
478 | _ => false, | |
479 | } | |
480 | } | |
481 | ||
482 | fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool { | |
17df50a5 | 483 | left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty()) |
f20569fa | 484 | } |
49aad941 FG |
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 | } | |
f20569fa XL |
524 | } |
525 | ||
526 | /// Some simple reductions like `{ return }` => `return` | |
527 | fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> { | |
528 | if let ExprKind::Block(block, _) = kind { | |
529 | match (block.stmts, block.expr) { | |
530 | // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty | |
531 | // block with an empty span. | |
532 | ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]), | |
533 | // `{}` => `()` | |
534 | ([], None) => match snippet_opt(cx, block.span) { | |
535 | // Don't reduce if there are any tokens contained in the braces | |
536 | Some(snip) | |
537 | if tokenize(&snip) | |
538 | .map(|t| t.kind) | |
539 | .filter(|t| { | |
540 | !matches!( | |
541 | t, | |
542 | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace | |
543 | ) | |
544 | }) | |
cdc7bbd5 | 545 | .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) => |
f20569fa XL |
546 | { |
547 | kind | |
548 | }, | |
549 | _ => &ExprKind::Tup(&[]), | |
550 | }, | |
551 | ([], Some(expr)) => match expr.kind { | |
552 | // `{ return .. }` => `return ..` | |
553 | ExprKind::Ret(..) => &expr.kind, | |
554 | _ => kind, | |
555 | }, | |
556 | ([stmt], None) => match stmt.kind { | |
557 | StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind { | |
558 | // `{ return ..; }` => `return ..` | |
559 | ExprKind::Ret(..) => &expr.kind, | |
560 | _ => kind, | |
561 | }, | |
562 | _ => kind, | |
563 | }, | |
564 | _ => kind, | |
565 | } | |
566 | } else { | |
567 | kind | |
568 | } | |
569 | } | |
570 | ||
571 | fn swap_binop<'a>( | |
572 | binop: BinOpKind, | |
573 | lhs: &'a Expr<'a>, | |
574 | rhs: &'a Expr<'a>, | |
575 | ) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> { | |
576 | match binop { | |
577 | BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => { | |
578 | Some((binop, rhs, lhs)) | |
579 | }, | |
580 | BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)), | |
581 | BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)), | |
582 | BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)), | |
583 | BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)), | |
584 | BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698 | |
585 | | BinOpKind::Shl | |
586 | | BinOpKind::Shr | |
587 | | BinOpKind::Rem | |
588 | | BinOpKind::Sub | |
589 | | BinOpKind::Div | |
590 | | BinOpKind::And | |
591 | | BinOpKind::Or => None, | |
592 | } | |
593 | } | |
594 | ||
595 | /// Checks if the two `Option`s are both `None` or some equal values as per | |
596 | /// `eq_fn`. | |
597 | pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { | |
598 | l.as_ref() | |
599 | .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y))) | |
600 | } | |
601 | ||
602 | /// Checks if two slices are equal as per `eq_fn`. | |
603 | pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { | |
604 | left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) | |
605 | } | |
606 | ||
cdc7bbd5 XL |
607 | /// Counts how many elements of the slices are equal as per `eq_fn`. |
608 | pub fn count_eq<X: Sized>( | |
609 | left: &mut dyn Iterator<Item = X>, | |
610 | right: &mut dyn Iterator<Item = X>, | |
611 | mut eq_fn: impl FnMut(&X, &X) -> bool, | |
612 | ) -> usize { | |
613 | left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count() | |
614 | } | |
615 | ||
f20569fa XL |
616 | /// Checks if two expressions evaluate to the same value, and don't contain any side effects. |
617 | pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { | |
618 | SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) | |
619 | } | |
620 | ||
621 | /// Type used to hash an ast element. This is different from the `Hash` trait | |
622 | /// on ast types as this | |
623 | /// trait would consider IDs and spans. | |
624 | /// | |
625 | /// All expressions kind are hashed, but some might have a weaker hash. | |
626 | pub struct SpanlessHash<'a, 'tcx> { | |
627 | /// Context used to evaluate constant expressions. | |
628 | cx: &'a LateContext<'tcx>, | |
629 | maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, | |
17df50a5 | 630 | s: FxHasher, |
f20569fa XL |
631 | } |
632 | ||
633 | impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { | |
634 | pub fn new(cx: &'a LateContext<'tcx>) -> Self { | |
635 | Self { | |
636 | cx, | |
637 | maybe_typeck_results: cx.maybe_typeck_results(), | |
17df50a5 | 638 | s: FxHasher::default(), |
f20569fa XL |
639 | } |
640 | } | |
641 | ||
642 | pub fn finish(self) -> u64 { | |
643 | self.s.finish() | |
644 | } | |
645 | ||
646 | pub fn hash_block(&mut self, b: &Block<'_>) { | |
647 | for s in b.stmts { | |
648 | self.hash_stmt(s); | |
649 | } | |
650 | ||
17df50a5 | 651 | if let Some(e) = b.expr { |
f20569fa XL |
652 | self.hash_expr(e); |
653 | } | |
654 | ||
17df50a5 | 655 | std::mem::discriminant(&b.rules).hash(&mut self.s); |
f20569fa XL |
656 | } |
657 | ||
923072b8 | 658 | #[expect(clippy::too_many_lines)] |
f20569fa XL |
659 | pub fn hash_expr(&mut self, e: &Expr<'_>) { |
660 | let simple_const = self | |
661 | .maybe_typeck_results | |
662 | .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e)); | |
663 | ||
664 | // const hashing may result in the same hash as some unrelated node, so add a sort of | |
665 | // discriminant depending on which path we're choosing next | |
17df50a5 XL |
666 | simple_const.hash(&mut self.s); |
667 | if simple_const.is_some() { | |
668 | return; | |
f20569fa XL |
669 | } |
670 | ||
671 | std::mem::discriminant(&e.kind).hash(&mut self.s); | |
672 | ||
673 | match e.kind { | |
17df50a5 XL |
674 | ExprKind::AddrOf(kind, m, e) => { |
675 | std::mem::discriminant(&kind).hash(&mut self.s); | |
f20569fa XL |
676 | m.hash(&mut self.s); |
677 | self.hash_expr(e); | |
678 | }, | |
679 | ExprKind::Continue(i) => { | |
680 | if let Some(i) = i.label { | |
681 | self.hash_name(i.ident.name); | |
682 | } | |
683 | }, | |
17df50a5 | 684 | ExprKind::Assign(l, r, _) => { |
f20569fa XL |
685 | self.hash_expr(l); |
686 | self.hash_expr(r); | |
687 | }, | |
17df50a5 XL |
688 | ExprKind::AssignOp(ref o, l, r) => { |
689 | std::mem::discriminant(&o.node).hash(&mut self.s); | |
f20569fa XL |
690 | self.hash_expr(l); |
691 | self.hash_expr(r); | |
692 | }, | |
17df50a5 | 693 | ExprKind::Block(b, _) => { |
f20569fa XL |
694 | self.hash_block(b); |
695 | }, | |
17df50a5 XL |
696 | ExprKind::Binary(op, l, r) => { |
697 | std::mem::discriminant(&op.node).hash(&mut self.s); | |
f20569fa XL |
698 | self.hash_expr(l); |
699 | self.hash_expr(r); | |
700 | }, | |
701 | ExprKind::Break(i, ref j) => { | |
702 | if let Some(i) = i.label { | |
703 | self.hash_name(i.ident.name); | |
704 | } | |
17df50a5 | 705 | if let Some(j) = *j { |
923072b8 | 706 | self.hash_expr(j); |
f20569fa XL |
707 | } |
708 | }, | |
353b0b11 | 709 | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { |
f20569fa XL |
710 | self.hash_expr(e); |
711 | }, | |
17df50a5 | 712 | ExprKind::Call(fun, args) => { |
f20569fa XL |
713 | self.hash_expr(fun); |
714 | self.hash_exprs(args); | |
715 | }, | |
17df50a5 | 716 | ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => { |
f20569fa XL |
717 | self.hash_expr(e); |
718 | self.hash_ty(ty); | |
719 | }, | |
064997fb | 720 | ExprKind::Closure(&Closure { |
923072b8 | 721 | capture_clause, body, .. |
064997fb | 722 | }) => { |
923072b8 | 723 | std::mem::discriminant(&capture_clause).hash(&mut self.s); |
f20569fa | 724 | // closures inherit TypeckResults |
f2b60f7d | 725 | self.hash_expr(self.cx.tcx.hir().body(body).value); |
f20569fa | 726 | }, |
17df50a5 | 727 | ExprKind::Field(e, ref f) => { |
f20569fa XL |
728 | self.hash_expr(e); |
729 | self.hash_name(f.name); | |
730 | }, | |
17df50a5 | 731 | ExprKind::Index(a, i) => { |
f20569fa XL |
732 | self.hash_expr(a); |
733 | self.hash_expr(i); | |
734 | }, | |
17df50a5 | 735 | ExprKind::InlineAsm(asm) => { |
f20569fa XL |
736 | for piece in asm.template { |
737 | match piece { | |
738 | InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s), | |
739 | InlineAsmTemplatePiece::Placeholder { | |
740 | operand_idx, | |
741 | modifier, | |
742 | span: _, | |
743 | } => { | |
744 | operand_idx.hash(&mut self.s); | |
745 | modifier.hash(&mut self.s); | |
746 | }, | |
747 | } | |
748 | } | |
749 | asm.options.hash(&mut self.s); | |
750 | for (op, _op_sp) in asm.operands { | |
751 | match op { | |
752 | InlineAsmOperand::In { reg, expr } => { | |
753 | reg.hash(&mut self.s); | |
754 | self.hash_expr(expr); | |
755 | }, | |
756 | InlineAsmOperand::Out { reg, late, expr } => { | |
757 | reg.hash(&mut self.s); | |
758 | late.hash(&mut self.s); | |
759 | if let Some(expr) = expr { | |
760 | self.hash_expr(expr); | |
761 | } | |
762 | }, | |
763 | InlineAsmOperand::InOut { reg, late, expr } => { | |
764 | reg.hash(&mut self.s); | |
765 | late.hash(&mut self.s); | |
766 | self.hash_expr(expr); | |
767 | }, | |
768 | InlineAsmOperand::SplitInOut { | |
769 | reg, | |
770 | late, | |
771 | in_expr, | |
772 | out_expr, | |
773 | } => { | |
774 | reg.hash(&mut self.s); | |
775 | late.hash(&mut self.s); | |
776 | self.hash_expr(in_expr); | |
777 | if let Some(out_expr) = out_expr { | |
778 | self.hash_expr(out_expr); | |
779 | } | |
780 | }, | |
04454e1e FG |
781 | InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => { |
782 | self.hash_body(anon_const.body); | |
783 | }, | |
784 | InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), | |
f20569fa XL |
785 | } |
786 | } | |
787 | }, | |
49aad941 FG |
788 | ExprKind::OffsetOf(container, fields) => { |
789 | self.hash_ty(container); | |
790 | for field in fields { | |
791 | self.hash_name(field.name); | |
792 | } | |
793 | }, | |
a2a8927a XL |
794 | ExprKind::Let(Let { pat, init, ty, .. }) => { |
795 | self.hash_expr(init); | |
796 | if let Some(ty) = ty { | |
797 | self.hash_ty(ty); | |
798 | } | |
94222f64 XL |
799 | self.hash_pat(pat); |
800 | }, | |
9ffffee4 | 801 | ExprKind::Err(_) => {}, |
49aad941 | 802 | ExprKind::Lit(l) => { |
f20569fa XL |
803 | l.node.hash(&mut self.s); |
804 | }, | |
17df50a5 | 805 | ExprKind::Loop(b, ref i, ..) => { |
f20569fa XL |
806 | self.hash_block(b); |
807 | if let Some(i) = *i { | |
808 | self.hash_name(i.ident.name); | |
809 | } | |
810 | }, | |
17df50a5 | 811 | ExprKind::If(cond, then, ref else_opt) => { |
f20569fa | 812 | self.hash_expr(cond); |
17df50a5 XL |
813 | self.hash_expr(then); |
814 | if let Some(e) = *else_opt { | |
f20569fa XL |
815 | self.hash_expr(e); |
816 | } | |
817 | }, | |
17df50a5 | 818 | ExprKind::Match(e, arms, ref s) => { |
f20569fa XL |
819 | self.hash_expr(e); |
820 | ||
821 | for arm in arms { | |
17df50a5 | 822 | self.hash_pat(arm.pat); |
f20569fa XL |
823 | if let Some(ref e) = arm.guard { |
824 | self.hash_guard(e); | |
825 | } | |
17df50a5 | 826 | self.hash_expr(arm.body); |
f20569fa XL |
827 | } |
828 | ||
829 | s.hash(&mut self.s); | |
830 | }, | |
f2b60f7d | 831 | ExprKind::MethodCall(path, receiver, args, ref _fn_span) => { |
f20569fa | 832 | self.hash_name(path.ident.name); |
f2b60f7d | 833 | self.hash_expr(receiver); |
f20569fa XL |
834 | self.hash_exprs(args); |
835 | }, | |
836 | ExprKind::ConstBlock(ref l_id) => { | |
837 | self.hash_body(l_id.body); | |
838 | }, | |
a2a8927a | 839 | ExprKind::Repeat(e, len) => { |
f20569fa | 840 | self.hash_expr(e); |
a2a8927a | 841 | self.hash_array_length(len); |
f20569fa XL |
842 | }, |
843 | ExprKind::Ret(ref e) => { | |
17df50a5 | 844 | if let Some(e) = *e { |
f20569fa XL |
845 | self.hash_expr(e); |
846 | } | |
847 | }, | |
848 | ExprKind::Path(ref qpath) => { | |
849 | self.hash_qpath(qpath); | |
850 | }, | |
17df50a5 | 851 | ExprKind::Struct(path, fields, ref expr) => { |
f20569fa XL |
852 | self.hash_qpath(path); |
853 | ||
854 | for f in fields { | |
855 | self.hash_name(f.ident.name); | |
17df50a5 | 856 | self.hash_expr(f.expr); |
f20569fa XL |
857 | } |
858 | ||
17df50a5 | 859 | if let Some(e) = *expr { |
f20569fa XL |
860 | self.hash_expr(e); |
861 | } | |
862 | }, | |
863 | ExprKind::Tup(tup) => { | |
864 | self.hash_exprs(tup); | |
865 | }, | |
866 | ExprKind::Array(v) => { | |
867 | self.hash_exprs(v); | |
868 | }, | |
17df50a5 XL |
869 | ExprKind::Unary(lop, le) => { |
870 | std::mem::discriminant(&lop).hash(&mut self.s); | |
f20569fa XL |
871 | self.hash_expr(le); |
872 | }, | |
873 | } | |
874 | } | |
875 | ||
876 | pub fn hash_exprs(&mut self, e: &[Expr<'_>]) { | |
877 | for e in e { | |
878 | self.hash_expr(e); | |
879 | } | |
880 | } | |
881 | ||
882 | pub fn hash_name(&mut self, n: Symbol) { | |
17df50a5 | 883 | n.hash(&mut self.s); |
f20569fa XL |
884 | } |
885 | ||
886 | pub fn hash_qpath(&mut self, p: &QPath<'_>) { | |
887 | match *p { | |
17df50a5 | 888 | QPath::Resolved(_, path) => { |
f20569fa XL |
889 | self.hash_path(path); |
890 | }, | |
17df50a5 | 891 | QPath::TypeRelative(_, path) => { |
f20569fa XL |
892 | self.hash_name(path.ident.name); |
893 | }, | |
894 | QPath::LangItem(lang_item, ..) => { | |
17df50a5 | 895 | std::mem::discriminant(&lang_item).hash(&mut self.s); |
f20569fa XL |
896 | }, |
897 | } | |
898 | // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); | |
899 | } | |
900 | ||
17df50a5 XL |
901 | pub fn hash_pat(&mut self, pat: &Pat<'_>) { |
902 | std::mem::discriminant(&pat.kind).hash(&mut self.s); | |
903 | match pat.kind { | |
f2b60f7d FG |
904 | PatKind::Binding(BindingAnnotation(by_ref, mutability), _, _, pat) => { |
905 | std::mem::discriminant(&by_ref).hash(&mut self.s); | |
906 | std::mem::discriminant(&mutability).hash(&mut self.s); | |
17df50a5 XL |
907 | if let Some(pat) = pat { |
908 | self.hash_pat(pat); | |
909 | } | |
910 | }, | |
911 | PatKind::Box(pat) => self.hash_pat(pat), | |
912 | PatKind::Lit(expr) => self.hash_expr(expr), | |
913 | PatKind::Or(pats) => { | |
914 | for pat in pats { | |
915 | self.hash_pat(pat); | |
916 | } | |
917 | }, | |
918 | PatKind::Path(ref qpath) => self.hash_qpath(qpath), | |
919 | PatKind::Range(s, e, i) => { | |
920 | if let Some(s) = s { | |
921 | self.hash_expr(s); | |
922 | } | |
923 | if let Some(e) = e { | |
924 | self.hash_expr(e); | |
925 | } | |
926 | std::mem::discriminant(&i).hash(&mut self.s); | |
927 | }, | |
928 | PatKind::Ref(pat, mu) => { | |
929 | self.hash_pat(pat); | |
930 | std::mem::discriminant(&mu).hash(&mut self.s); | |
931 | }, | |
932 | PatKind::Slice(l, m, r) => { | |
933 | for pat in l { | |
934 | self.hash_pat(pat); | |
935 | } | |
936 | if let Some(pat) = m { | |
937 | self.hash_pat(pat); | |
938 | } | |
939 | for pat in r { | |
940 | self.hash_pat(pat); | |
941 | } | |
942 | }, | |
943 | PatKind::Struct(ref qpath, fields, e) => { | |
944 | self.hash_qpath(qpath); | |
945 | for f in fields { | |
946 | self.hash_name(f.ident.name); | |
947 | self.hash_pat(f.pat); | |
948 | } | |
949 | e.hash(&mut self.s); | |
950 | }, | |
951 | PatKind::Tuple(pats, e) => { | |
952 | for pat in pats { | |
953 | self.hash_pat(pat); | |
954 | } | |
955 | e.hash(&mut self.s); | |
956 | }, | |
957 | PatKind::TupleStruct(ref qpath, pats, e) => { | |
958 | self.hash_qpath(qpath); | |
959 | for pat in pats { | |
960 | self.hash_pat(pat); | |
961 | } | |
962 | e.hash(&mut self.s); | |
963 | }, | |
964 | PatKind::Wild => {}, | |
965 | } | |
966 | } | |
967 | ||
f20569fa XL |
968 | pub fn hash_path(&mut self, path: &Path<'_>) { |
969 | match path.res { | |
970 | // constant hash since equality is dependant on inter-expression context | |
5099ac24 FG |
971 | // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal |
972 | // even though the binding names are different and they have different `HirId`s. | |
f20569fa XL |
973 | Res::Local(_) => 1_usize.hash(&mut self.s), |
974 | _ => { | |
975 | for seg in path.segments { | |
976 | self.hash_name(seg.ident.name); | |
17df50a5 | 977 | self.hash_generic_args(seg.args().args); |
f20569fa XL |
978 | } |
979 | }, | |
980 | } | |
981 | } | |
982 | ||
983 | pub fn hash_stmt(&mut self, b: &Stmt<'_>) { | |
984 | std::mem::discriminant(&b.kind).hash(&mut self.s); | |
985 | ||
986 | match &b.kind { | |
987 | StmtKind::Local(local) => { | |
17df50a5 XL |
988 | self.hash_pat(local.pat); |
989 | if let Some(init) = local.init { | |
f20569fa XL |
990 | self.hash_expr(init); |
991 | } | |
064997fb FG |
992 | if let Some(els) = local.els { |
993 | self.hash_block(els); | |
994 | } | |
f20569fa XL |
995 | }, |
996 | StmtKind::Item(..) => {}, | |
997 | StmtKind::Expr(expr) | StmtKind::Semi(expr) => { | |
998 | self.hash_expr(expr); | |
999 | }, | |
1000 | } | |
1001 | } | |
1002 | ||
1003 | pub fn hash_guard(&mut self, g: &Guard<'_>) { | |
1004 | match g { | |
923072b8 | 1005 | Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => { |
f20569fa XL |
1006 | self.hash_expr(expr); |
1007 | }, | |
1008 | } | |
1009 | } | |
1010 | ||
f2b60f7d | 1011 | pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { |
487cf647 FG |
1012 | lifetime.ident.name.hash(&mut self.s); |
1013 | std::mem::discriminant(&lifetime.res).hash(&mut self.s); | |
1014 | if let LifetimeName::Param(param_id) = lifetime.res { | |
923072b8 | 1015 | param_id.hash(&mut self.s); |
f20569fa XL |
1016 | } |
1017 | } | |
1018 | ||
1019 | pub fn hash_ty(&mut self, ty: &Ty<'_>) { | |
17df50a5 | 1020 | std::mem::discriminant(&ty.kind).hash(&mut self.s); |
94222f64 XL |
1021 | self.hash_tykind(&ty.kind); |
1022 | } | |
1023 | ||
1024 | pub fn hash_tykind(&mut self, ty: &TyKind<'_>) { | |
1025 | match ty { | |
f20569fa XL |
1026 | TyKind::Slice(ty) => { |
1027 | self.hash_ty(ty); | |
1028 | }, | |
a2a8927a | 1029 | &TyKind::Array(ty, len) => { |
f20569fa | 1030 | self.hash_ty(ty); |
a2a8927a | 1031 | self.hash_array_length(len); |
f20569fa | 1032 | }, |
17df50a5 XL |
1033 | TyKind::Ptr(ref mut_ty) => { |
1034 | self.hash_ty(mut_ty.ty); | |
f20569fa XL |
1035 | mut_ty.mutbl.hash(&mut self.s); |
1036 | }, | |
9c376795 | 1037 | TyKind::Ref(lifetime, ref mut_ty) => { |
2b03887a | 1038 | self.hash_lifetime(lifetime); |
17df50a5 | 1039 | self.hash_ty(mut_ty.ty); |
f20569fa XL |
1040 | mut_ty.mutbl.hash(&mut self.s); |
1041 | }, | |
1042 | TyKind::BareFn(bfn) => { | |
1043 | bfn.unsafety.hash(&mut self.s); | |
1044 | bfn.abi.hash(&mut self.s); | |
1045 | for arg in bfn.decl.inputs { | |
17df50a5 | 1046 | self.hash_ty(arg); |
f20569fa | 1047 | } |
17df50a5 | 1048 | std::mem::discriminant(&bfn.decl.output).hash(&mut self.s); |
f20569fa | 1049 | match bfn.decl.output { |
17df50a5 XL |
1050 | FnRetTy::DefaultReturn(_) => {}, |
1051 | FnRetTy::Return(ty) => { | |
f20569fa XL |
1052 | self.hash_ty(ty); |
1053 | }, | |
1054 | } | |
1055 | bfn.decl.c_variadic.hash(&mut self.s); | |
1056 | }, | |
1057 | TyKind::Tup(ty_list) => { | |
94222f64 | 1058 | for ty in *ty_list { |
f20569fa XL |
1059 | self.hash_ty(ty); |
1060 | } | |
1061 | }, | |
17df50a5 | 1062 | TyKind::Path(ref qpath) => self.hash_qpath(qpath), |
f2b60f7d | 1063 | TyKind::OpaqueDef(_, arg_list, in_trait) => { |
f20569fa | 1064 | self.hash_generic_args(arg_list); |
f2b60f7d | 1065 | in_trait.hash(&mut self.s); |
f20569fa XL |
1066 | }, |
1067 | TyKind::TraitObject(_, lifetime, _) => { | |
2b03887a | 1068 | self.hash_lifetime(lifetime); |
f20569fa XL |
1069 | }, |
1070 | TyKind::Typeof(anon_const) => { | |
1071 | self.hash_body(anon_const.body); | |
1072 | }, | |
9ffffee4 | 1073 | TyKind::Err(_) | TyKind::Infer | TyKind::Never => {}, |
f20569fa XL |
1074 | } |
1075 | } | |
1076 | ||
a2a8927a XL |
1077 | pub fn hash_array_length(&mut self, length: ArrayLen) { |
1078 | match length { | |
5099ac24 | 1079 | ArrayLen::Infer(..) => {}, |
a2a8927a XL |
1080 | ArrayLen::Body(anon_const) => self.hash_body(anon_const.body), |
1081 | } | |
1082 | } | |
1083 | ||
f20569fa XL |
1084 | pub fn hash_body(&mut self, body_id: BodyId) { |
1085 | // swap out TypeckResults when hashing a body | |
1086 | let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id)); | |
f2b60f7d | 1087 | self.hash_expr(self.cx.tcx.hir().body(body_id).value); |
f20569fa XL |
1088 | self.maybe_typeck_results = old_maybe_typeck_results; |
1089 | } | |
1090 | ||
1091 | fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) { | |
1092 | for arg in arg_list { | |
17df50a5 XL |
1093 | match *arg { |
1094 | GenericArg::Lifetime(l) => self.hash_lifetime(l), | |
f2b60f7d | 1095 | GenericArg::Type(ty) => self.hash_ty(ty), |
f20569fa | 1096 | GenericArg::Const(ref ca) => self.hash_body(ca.value.body), |
94222f64 | 1097 | GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()), |
f20569fa XL |
1098 | } |
1099 | } | |
1100 | } | |
1101 | } | |
923072b8 FG |
1102 | |
1103 | pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 { | |
1104 | let mut h = SpanlessHash::new(cx); | |
1105 | h.hash_stmt(s); | |
1106 | h.finish() | |
1107 | } | |
1108 | ||
487cf647 FG |
1109 | pub fn is_bool(ty: &Ty<'_>) -> bool { |
1110 | if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { | |
1111 | matches!(path.res, Res::PrimTy(PrimTy::Bool)) | |
1112 | } else { | |
1113 | false | |
1114 | } | |
1115 | } | |
1116 | ||
923072b8 FG |
1117 | pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { |
1118 | let mut h = SpanlessHash::new(cx); | |
1119 | h.hash_expr(e); | |
1120 | h.finish() | |
1121 | } | |
49aad941 FG |
1122 | |
1123 | #[expect(clippy::similar_names)] | |
1124 | fn eq_span_tokens( | |
1125 | cx: &LateContext<'_>, | |
1126 | left: impl SpanRange, | |
1127 | right: impl SpanRange, | |
1128 | pred: impl Fn(TokenKind) -> bool, | |
1129 | ) -> bool { | |
1130 | fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool { | |
1131 | if let Some(lsrc) = get_source_text(cx, left) | |
1132 | && let Some(lsrc) = lsrc.as_str() | |
1133 | && let Some(rsrc) = get_source_text(cx, right) | |
1134 | && let Some(rsrc) = rsrc.as_str() | |
1135 | { | |
1136 | let pred = |t: &(_, _)| pred(t.0); | |
1137 | let map = |(_, x)| x; | |
1138 | ||
1139 | let ltok = tokenize_with_text(lsrc) | |
1140 | .filter(pred) | |
1141 | .map(map); | |
1142 | let rtok = tokenize_with_text(rsrc) | |
1143 | .filter(pred) | |
1144 | .map(map); | |
1145 | ltok.eq(rtok) | |
1146 | } else { | |
1147 | // Unable to access the source. Conservatively assume the blocks aren't equal. | |
1148 | false | |
1149 | } | |
1150 | } | |
1151 | f(cx, left.into_range(), right.into_range(), pred) | |
1152 | } |