]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_utils/src/hir_utils.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_utils / src / hir_utils.rs
CommitLineData
04454e1e 1use crate::consts::constant_simple;
064997fb 2use crate::macros::macro_backtrace;
49aad941
FG
3use crate::source::{get_source_text, snippet_opt, walk_span_to_context, SpanRange};
4use crate::tokenize_with_text;
f20569fa 5use rustc_ast::ast::InlineAsmTemplatePiece;
17df50a5 6use rustc_data_structures::fx::FxHasher;
f20569fa 7use rustc_hir::def::Res;
cdc7bbd5 8use rustc_hir::HirIdMap;
f20569fa 9use 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};
14use rustc_lexer::{tokenize, TokenKind};
15use rustc_lint::LateContext;
f20569fa 16use rustc_middle::ty::TypeckResults;
49aad941 17use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext};
17df50a5 18use std::hash::{Hash, Hasher};
49aad941 19use 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.
23type 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.
30pub 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
38impl<'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 97pub 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
108impl 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`
527fn 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
571fn 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`.
597pub 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`.
603pub 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`.
608pub 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.
617pub 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.
626pub 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
633impl<'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
1103pub 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
1109pub 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
1117pub 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)]
1124fn 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}