clippy_utils/
hir_utils.rs

1use crate::consts::ConstEvalCtxt;
2use crate::macros::macro_backtrace;
3use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context};
4use crate::tokenize_with_text;
5use rustc_ast::ast::InlineAsmTemplatePiece;
6use rustc_data_structures::fx::FxHasher;
7use rustc_hir::MatchSource::TryDesugar;
8use rustc_hir::def::{DefKind, Res};
9use rustc_hir::{
10    AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
11    ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName,
12    Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr,
13    TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
14};
15use rustc_lexer::{TokenKind, tokenize};
16use rustc_lint::LateContext;
17use rustc_middle::ty::TypeckResults;
18use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym};
19use std::hash::{Hash, Hasher};
20use std::ops::Range;
21use std::slice;
22
23/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
24/// other conditions would make them equal.
25type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
26
27/// Determines how paths are hashed and compared for equality.
28#[derive(Copy, Clone, Debug, Default)]
29pub enum PathCheck {
30    /// Paths must match exactly and are hashed by their exact HIR tree.
31    ///
32    /// Thus, `std::iter::Iterator` and `Iterator` are not considered equal even though they refer
33    /// to the same item.
34    #[default]
35    Exact,
36    /// Paths are compared and hashed based on their resolution.
37    ///
38    /// They can appear different in the HIR tree but are still considered equal
39    /// and have equal hashes as long as they refer to the same item.
40    ///
41    /// Note that this is currently only partially implemented specifically for paths that are
42    /// resolved before type-checking, i.e. the final segment must have a non-error resolution.
43    /// If a path with an error resolution is encountered, it falls back to the default exact
44    /// matching behavior.
45    Resolution,
46}
47
48/// Type used to check whether two ast are the same. This is different from the
49/// operator `==` on ast types as this operator would compare true equality with
50/// ID and span.
51///
52/// Note that some expressions kinds are not considered but could be added.
53pub struct SpanlessEq<'a, 'tcx> {
54    /// Context used to evaluate constant expressions.
55    cx: &'a LateContext<'tcx>,
56    maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
57    allow_side_effects: bool,
58    expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
59    path_check: PathCheck,
60}
61
62impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
63    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
64        Self {
65            cx,
66            maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
67            allow_side_effects: true,
68            expr_fallback: None,
69            path_check: PathCheck::default(),
70        }
71    }
72
73    /// Consider expressions containing potential side effects as not equal.
74    #[must_use]
75    pub fn deny_side_effects(self) -> Self {
76        Self {
77            allow_side_effects: false,
78            ..self
79        }
80    }
81
82    /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more
83    /// details.
84    #[must_use]
85    pub fn paths_by_resolution(self) -> Self {
86        Self {
87            path_check: PathCheck::Resolution,
88            ..self
89        }
90    }
91
92    #[must_use]
93    pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
94        Self {
95            expr_fallback: Some(Box::new(expr_fallback)),
96            ..self
97        }
98    }
99
100    /// Use this method to wrap comparisons that may involve inter-expression context.
101    /// See `self.locals`.
102    pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
103        HirEqInterExpr {
104            inner: self,
105            left_ctxt: SyntaxContext::root(),
106            right_ctxt: SyntaxContext::root(),
107            locals: HirIdMap::default(),
108        }
109    }
110
111    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
112        self.inter_expr().eq_block(left, right)
113    }
114
115    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
116        self.inter_expr().eq_expr(left, right)
117    }
118
119    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
120        self.inter_expr().eq_path(left, right)
121    }
122
123    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
124        self.inter_expr().eq_path_segment(left, right)
125    }
126
127    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
128        self.inter_expr().eq_path_segments(left, right)
129    }
130
131    pub fn eq_modifiers(left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
132        std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness)
133            && std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity)
134    }
135}
136
137pub struct HirEqInterExpr<'a, 'b, 'tcx> {
138    inner: &'a mut SpanlessEq<'b, 'tcx>,
139    left_ctxt: SyntaxContext,
140    right_ctxt: SyntaxContext,
141
142    // When binding are declared, the binding ID in the left expression is mapped to the one on the
143    // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
144    // these blocks are considered equal since `x` is mapped to `y`.
145    pub locals: HirIdMap<HirId>,
146}
147
148impl HirEqInterExpr<'_, '_, '_> {
149    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
150        match (&left.kind, &right.kind) {
151            (&StmtKind::Let(l), &StmtKind::Let(r)) => {
152                // This additional check ensures that the type of the locals are equivalent even if the init
153                // expression or type have some inferred parts.
154                if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
155                    let l_ty = typeck_lhs.pat_ty(l.pat);
156                    let r_ty = typeck_rhs.pat_ty(r.pat);
157                    if l_ty != r_ty {
158                        return false;
159                    }
160                }
161
162                // eq_pat adds the HirIds to the locals map. We therefore call it last to make sure that
163                // these only get added if the init and type is equal.
164                both(l.init.as_ref(), r.init.as_ref(), |l, r| self.eq_expr(l, r))
165                    && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r))
166                    && both(l.els.as_ref(), r.els.as_ref(), |l, r| self.eq_block(l, r))
167                    && self.eq_pat(l.pat, r.pat)
168            },
169            (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
170            _ => false,
171        }
172    }
173
174    /// Checks whether two blocks are the same.
175    fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
176        use TokenKind::{Semi, Whitespace};
177        if left.stmts.len() != right.stmts.len() {
178            return false;
179        }
180        let lspan = left.span.data();
181        let rspan = right.span.data();
182        if lspan.ctxt != SyntaxContext::root() && rspan.ctxt != SyntaxContext::root() {
183            // Don't try to check in between statements inside macros.
184            return over(left.stmts, right.stmts, |left, right| self.eq_stmt(left, right))
185                && both(left.expr.as_ref(), right.expr.as_ref(), |left, right| {
186                    self.eq_expr(left, right)
187                });
188        }
189        if lspan.ctxt != rspan.ctxt {
190            return false;
191        }
192
193        let mut lstart = lspan.lo;
194        let mut rstart = rspan.lo;
195
196        for (left, right) in left.stmts.iter().zip(right.stmts) {
197            if !self.eq_stmt(left, right) {
198                return false;
199            }
200
201            // Try to detect any `cfg`ed statements or empty macro expansions.
202            let Some(lstmt_span) = walk_span_to_context(left.span, lspan.ctxt) else {
203                return false;
204            };
205            let Some(rstmt_span) = walk_span_to_context(right.span, rspan.ctxt) else {
206                return false;
207            };
208            let lstmt_span = lstmt_span.data();
209            let rstmt_span = rstmt_span.data();
210
211            if lstmt_span.lo < lstart && rstmt_span.lo < rstart {
212                // Can happen when macros expand to multiple statements, or rearrange statements.
213                // Nothing in between the statements to check in this case.
214                continue;
215            }
216            if lstmt_span.lo < lstart || rstmt_span.lo < rstart {
217                // Only one of the blocks had a weird macro.
218                return false;
219            }
220            if !eq_span_tokens(self.inner.cx, lstart..lstmt_span.lo, rstart..rstmt_span.lo, |t| {
221                !matches!(t, Whitespace | Semi)
222            }) {
223                return false;
224            }
225
226            lstart = lstmt_span.hi;
227            rstart = rstmt_span.hi;
228        }
229
230        let (lend, rend) = match (left.expr, right.expr) {
231            (Some(left), Some(right)) => {
232                if !self.eq_expr(left, right) {
233                    return false;
234                }
235                let Some(lexpr_span) = walk_span_to_context(left.span, lspan.ctxt) else {
236                    return false;
237                };
238                let Some(rexpr_span) = walk_span_to_context(right.span, rspan.ctxt) else {
239                    return false;
240                };
241                (lexpr_span.lo(), rexpr_span.lo())
242            },
243            (None, None) => (lspan.hi, rspan.hi),
244            (Some(_), None) | (None, Some(_)) => return false,
245        };
246
247        if lend < lstart && rend < rstart {
248            // Can happen when macros rearrange the input.
249            // Nothing in between the statements to check in this case.
250            return true;
251        } else if lend < lstart || rend < rstart {
252            // Only one of the blocks had a weird macro
253            return false;
254        }
255        eq_span_tokens(self.inner.cx, lstart..lend, rstart..rend, |t| {
256            !matches!(t, Whitespace | Semi)
257        })
258    }
259
260    fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
261        macro_backtrace(expr.span).last().is_some_and(|macro_call| {
262            matches!(
263                &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
264                Some(sym::todo_macro | sym::unimplemented_macro)
265            )
266        })
267    }
268
269    pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
270        // swap out TypeckResults when hashing a body
271        let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
272            self.inner.cx.tcx.typeck_body(left),
273            self.inner.cx.tcx.typeck_body(right),
274        ));
275        let res = self.eq_expr(
276            self.inner.cx.tcx.hir_body(left).value,
277            self.inner.cx.tcx.hir_body(right).value,
278        );
279        self.inner.maybe_typeck_results = old_maybe_typeck_results;
280        res
281    }
282
283    #[expect(clippy::too_many_lines)]
284    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
285        if !self.check_ctxt(left.span.ctxt(), right.span.ctxt()) {
286            return false;
287        }
288
289        if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
290            && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
291            && let (Some(l), Some(r)) = (
292                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left),
293                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right),
294            )
295            && l == r
296        {
297            return true;
298        }
299
300        let is_eq = match (
301            reduce_exprkind(self.inner.cx, &left.kind),
302            reduce_exprkind(self.inner.cx, &right.kind),
303        ) {
304            (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => {
305                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
306            },
307            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
308            (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => {
309                self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
310            },
311            (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => {
312                self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
313            },
314            (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
315            (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
316                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
317                    || swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
318                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
319                    })
320            },
321            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
322                both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name)
323                    && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r))
324            },
325            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
326                self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
327            },
328            (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) => {
329                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
330            },
331            (&ExprKind::Closure(_l), &ExprKind::Closure(_r)) => false,
332            (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body),
333            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
334                both(li.label.as_ref(), ri.label.as_ref(), |l, r| l.ident.name == r.ident.name)
335            },
336            (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
337            (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => {
338                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
339            },
340            (&ExprKind::Index(la, li, _), &ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
341            (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
342                self.eq_expr(lc, rc) && self.eq_expr(lt, rt)
343                    && both(le.as_ref(), re.as_ref(), |l, r| self.eq_expr(l, r))
344            },
345            (&ExprKind::Let(l), &ExprKind::Let(r)) => {
346                self.eq_pat(l.pat, r.pat)
347                    && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| self.eq_ty(l, r))
348                    && self.eq_expr(l.init, r.init)
349            },
350            (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node,
351            (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
352                lls == rls && self.eq_block(lb, rb)
353                    && both(ll.as_ref(), rl.as_ref(), |l, r| l.ident.name == r.ident.name)
354            },
355            (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
356                (ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_)))))
357                    && self.eq_expr(le, re)
358                    && over(la, ra, |l, r| {
359                        self.eq_pat(l.pat, r.pat)
360                            && both(l.guard.as_ref(), r.guard.as_ref(), |l, r| self.eq_expr(l, r))
361                            && self.eq_expr(l.body, r.body)
362                    })
363            },
364            (
365                &ExprKind::MethodCall(l_path, l_receiver, l_args, _),
366                &ExprKind::MethodCall(r_path, r_receiver, r_args, _),
367            ) => {
368                self.inner.allow_side_effects
369                    && self.eq_path_segment(l_path, r_path)
370                    && self.eq_expr(l_receiver, r_receiver)
371                    && self.eq_exprs(l_args, r_args)
372            },
373            (&ExprKind::UnsafeBinderCast(lkind, le, None), &ExprKind::UnsafeBinderCast(rkind, re, None)) =>
374                lkind == rkind && self.eq_expr(le, re),
375            (&ExprKind::UnsafeBinderCast(lkind, le, Some(lt)), &ExprKind::UnsafeBinderCast(rkind, re, Some(rt))) =>
376                lkind == rkind && self.eq_expr(le, re) && self.eq_ty(lt, rt),
377            (&ExprKind::OffsetOf(l_container, l_fields), &ExprKind::OffsetOf(r_container, r_fields)) => {
378                self.eq_ty(l_container, r_container) && over(l_fields, r_fields, |l, r| l.name == r.name)
379            },
380            (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r),
381            (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
382                self.eq_expr(le, re) && self.eq_const_arg(ll, rl)
383            },
384            (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)),
385            (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
386                self.eq_qpath(l_path, r_path)
387                    && match (lo, ro) {
388                        (StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r),
389                        (StructTailExpr::None, StructTailExpr::None) |
390                        (StructTailExpr::DefaultFields(_), StructTailExpr::DefaultFields(_)) => true,
391                        _ => false,
392                    }
393                    && over(lf, rf, |l, r| self.eq_expr_field(l, r))
394            },
395            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
396            (&ExprKind::Use(l_expr, _), &ExprKind::Use(r_expr, _)) => self.eq_expr(l_expr, r_expr),
397            (&ExprKind::Type(le, lt), &ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt),
398            (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
399            (&ExprKind::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re),
400            (
401                // Else branches for branches above, grouped as per `match_same_arms`.
402                | &ExprKind::AddrOf(..)
403                | &ExprKind::Array(..)
404                | &ExprKind::Assign(..)
405                | &ExprKind::AssignOp(..)
406                | &ExprKind::Binary(..)
407                | &ExprKind::Become(..)
408                | &ExprKind::Block(..)
409                | &ExprKind::Break(..)
410                | &ExprKind::Call(..)
411                | &ExprKind::Cast(..)
412                | &ExprKind::ConstBlock(..)
413                | &ExprKind::Continue(..)
414                | &ExprKind::DropTemps(..)
415                | &ExprKind::Field(..)
416                | &ExprKind::Index(..)
417                | &ExprKind::If(..)
418                | &ExprKind::Let(..)
419                | &ExprKind::Lit(..)
420                | &ExprKind::Loop(..)
421                | &ExprKind::Match(..)
422                | &ExprKind::MethodCall(..)
423                | &ExprKind::OffsetOf(..)
424                | &ExprKind::Path(..)
425                | &ExprKind::Repeat(..)
426                | &ExprKind::Ret(..)
427                | &ExprKind::Struct(..)
428                | &ExprKind::Tup(..)
429                | &ExprKind::Use(..)
430                | &ExprKind::Type(..)
431                | &ExprKind::Unary(..)
432                | &ExprKind::Yield(..)
433                | &ExprKind::UnsafeBinderCast(..)
434
435                // --- Special cases that do not have a positive branch.
436
437                // `Err` represents an invalid expression, so let's never assume that
438                // an invalid expressions is equal to anything.
439                | &ExprKind::Err(..)
440
441                // For the time being, we always consider that two closures are unequal.
442                // This behavior may change in the future.
443                | &ExprKind::Closure(..)
444                // For the time being, we always consider that two instances of InlineAsm are different.
445                // This behavior may change in the future.
446                | &ExprKind::InlineAsm(_)
447                , _
448            ) => false,
449        };
450        (is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
451            || self.inner.expr_fallback.as_mut().is_some_and(|f| f(left, right))
452    }
453
454    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
455        over(left, right, |l, r| self.eq_expr(l, r))
456    }
457
458    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
459        left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
460    }
461
462    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
463        match (left, right) {
464            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_const_arg(l.as_unambig_ct(), r.as_unambig_ct()),
465            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
466            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty.as_unambig_ty(), r_ty.as_unambig_ty()),
467            (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
468            _ => false,
469        }
470    }
471
472    fn eq_const_arg(&mut self, left: &ConstArg<'_>, right: &ConstArg<'_>) -> bool {
473        match (&left.kind, &right.kind) {
474            (ConstArgKind::Path(l_p), ConstArgKind::Path(r_p)) => self.eq_qpath(l_p, r_p),
475            (ConstArgKind::Anon(l_an), ConstArgKind::Anon(r_an)) => self.eq_body(l_an.body, r_an.body),
476            (ConstArgKind::Infer(..), ConstArgKind::Infer(..)) => true,
477            // Use explicit match for now since ConstArg is undergoing flux.
478            (ConstArgKind::Path(..), ConstArgKind::Anon(..))
479            | (ConstArgKind::Anon(..), ConstArgKind::Path(..))
480            | (ConstArgKind::Infer(..), _)
481            | (_, ConstArgKind::Infer(..)) => false,
482        }
483    }
484
485    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
486        left.res == right.res
487    }
488
489    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
490        let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
491        li.name == ri.name && self.eq_pat(lp, rp)
492    }
493
494    fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool {
495        match (&left.kind, &right.kind) {
496            (
497                &PatExprKind::Lit {
498                    lit: left,
499                    negated: left_neg,
500                },
501                &PatExprKind::Lit {
502                    lit: right,
503                    negated: right_neg,
504                },
505            ) => left_neg == right_neg && left.node == right.node,
506            (PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body),
507            (PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right),
508            (PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false,
509        }
510    }
511
512    /// Checks whether two patterns are the same.
513    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
514        match (&left.kind, &right.kind) {
515            (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
516            (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
517                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
518            },
519            (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
520                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
521            },
522            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
523                let eq = lb == rb && both(lp.as_ref(), rp.as_ref(), |l, r| self.eq_pat(l, r));
524                if eq {
525                    self.locals.insert(li, ri);
526                }
527                eq
528            },
529            (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r),
530            (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
531            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
532                both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b))
533                    && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
534                    && (li == ri)
535            },
536            (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
537            (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
538                over(ls, rs, |l, r| self.eq_pat(l, r))
539                    && over(le, re, |l, r| self.eq_pat(l, r))
540                    && both(li.as_ref(), ri.as_ref(), |l, r| self.eq_pat(l, r))
541            },
542            (&PatKind::Wild, &PatKind::Wild) => true,
543            _ => false,
544        }
545    }
546
547    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
548        match (left, right) {
549            (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
550                both(lty.as_ref(), rty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
551            },
552            (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
553                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
554            },
555            (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
556            _ => false,
557        }
558    }
559
560    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
561        match (left.res, right.res) {
562            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
563            (Res::Local(_), _) | (_, Res::Local(_)) => false,
564            _ => self.eq_path_segments(left.segments, right.segments),
565        }
566    }
567
568    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
569        if left.parenthesized == right.parenthesized {
570            over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
571                && over(left.constraints, right.constraints, |l, r| self.eq_assoc_eq_constraint(l, r))
572        } else {
573            false
574        }
575    }
576
577    pub fn eq_path_segments<'tcx>(
578        &mut self,
579        mut left: &'tcx [PathSegment<'tcx>],
580        mut right: &'tcx [PathSegment<'tcx>],
581    ) -> bool {
582        if let PathCheck::Resolution = self.inner.path_check
583            && let Some(left_seg) = generic_path_segments(left)
584            && let Some(right_seg) = generic_path_segments(right)
585        {
586            // If we compare by resolution, then only check the last segments that could possibly have generic
587            // arguments
588            left = left_seg;
589            right = right_seg;
590        }
591
592        over(left, right, |l, r| self.eq_path_segment(l, r))
593    }
594
595    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
596        if !self.eq_path_parameters(left.args(), right.args()) {
597            return false;
598        }
599
600        if let PathCheck::Resolution = self.inner.path_check
601            && left.res != Res::Err
602            && right.res != Res::Err
603        {
604            left.res == right.res
605        } else {
606            // The == of idents doesn't work with different contexts,
607            // we have to be explicit about hygiene
608            left.ident.name == right.ident.name
609        }
610    }
611
612    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
613        match (&left.kind, &right.kind) {
614            (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
615            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl),
616            (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty),
617            (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => {
618                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
619            },
620            (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
621            (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
622            (&TyKind::Infer(()), &TyKind::Infer(())) => true,
623            _ => false,
624        }
625    }
626
627    /// Checks whether two constraints designate the same equality constraint (same name, and same
628    /// type or const).
629    fn eq_assoc_eq_constraint(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool {
630        // TODO: this could be extended to check for identical associated item bound constraints
631        left.ident.name == right.ident.name
632            && (both_some_and(left.ty(), right.ty(), |l, r| self.eq_ty(l, r))
633                || both_some_and(left.ct(), right.ct(), |l, r| self.eq_const_arg(l, r)))
634    }
635
636    fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
637        if self.left_ctxt == left && self.right_ctxt == right {
638            return true;
639        } else if self.left_ctxt == left || self.right_ctxt == right {
640            // Only one context has changed. This can only happen if the two nodes are written differently.
641            return false;
642        } else if left != SyntaxContext::root() {
643            let mut left_data = left.outer_expn_data();
644            let mut right_data = right.outer_expn_data();
645            loop {
646                use TokenKind::{BlockComment, LineComment, Whitespace};
647                if left_data.macro_def_id != right_data.macro_def_id
648                    || (matches!(
649                        left_data.kind,
650                        ExpnKind::Macro(MacroKind::Bang, name)
651                        if name == sym::cfg || name == sym::option_env
652                    ) && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| {
653                        !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. })
654                    }))
655                {
656                    // Either a different chain of macro calls, or different arguments to the `cfg` macro.
657                    return false;
658                }
659                let left_ctxt = left_data.call_site.ctxt();
660                let right_ctxt = right_data.call_site.ctxt();
661                if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() {
662                    break;
663                }
664                if left_ctxt == SyntaxContext::root() || right_ctxt == SyntaxContext::root() {
665                    // Different lengths for the expansion stack. This can only happen if nodes are written differently,
666                    // or shouldn't be compared to start with.
667                    return false;
668                }
669                left_data = left_ctxt.outer_expn_data();
670                right_data = right_ctxt.outer_expn_data();
671            }
672        }
673        self.left_ctxt = left;
674        self.right_ctxt = right;
675        true
676    }
677}
678
679/// Some simple reductions like `{ return }` => `return`
680fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
681    if let ExprKind::Block(block, _) = kind {
682        match (block.stmts, block.expr) {
683            // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
684            // block with an empty span.
685            ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
686            // `{}` => `()`
687            ([], None)
688                if block.span.check_source_text(cx, |src| {
689                    tokenize(src)
690                        .map(|t| t.kind)
691                        .filter(|t| {
692                            !matches!(
693                                t,
694                                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
695                            )
696                        })
697                        .eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied())
698                }) =>
699            {
700                &ExprKind::Tup(&[])
701            },
702            ([], Some(expr)) => match expr.kind {
703                // `{ return .. }` => `return ..`
704                ExprKind::Ret(..) => &expr.kind,
705                _ => kind,
706            },
707            ([stmt], None) => match stmt.kind {
708                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
709                    // `{ return ..; }` => `return ..`
710                    ExprKind::Ret(..) => &expr.kind,
711                    _ => kind,
712                },
713                _ => kind,
714            },
715            _ => kind,
716        }
717    } else {
718        kind
719    }
720}
721
722fn swap_binop<'a>(
723    binop: BinOpKind,
724    lhs: &'a Expr<'a>,
725    rhs: &'a Expr<'a>,
726) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
727    match binop {
728        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
729            Some((binop, rhs, lhs))
730        },
731        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
732        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
733        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
734        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
735        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
736        | BinOpKind::Shl
737        | BinOpKind::Shr
738        | BinOpKind::Rem
739        | BinOpKind::Sub
740        | BinOpKind::Div
741        | BinOpKind::And
742        | BinOpKind::Or => None,
743    }
744}
745
746/// Checks if the two `Option`s are both `None` or some equal values as per
747/// `eq_fn`.
748pub fn both<X>(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
749    l.as_ref()
750        .map_or_else(|| r.is_none(), |x| r.as_ref().is_some_and(|y| eq_fn(x, y)))
751}
752
753/// Checks if the two `Option`s are both `Some` and pass the predicate function.
754pub fn both_some_and<X, Y>(l: Option<X>, r: Option<Y>, mut pred: impl FnMut(X, Y) -> bool) -> bool {
755    l.is_some_and(|l| r.is_some_and(|r| pred(l, r)))
756}
757
758/// Checks if two slices are equal as per `eq_fn`.
759pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
760    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
761}
762
763/// Counts how many elements of the slices are equal as per `eq_fn`.
764pub fn count_eq<X: Sized>(
765    left: &mut dyn Iterator<Item = X>,
766    right: &mut dyn Iterator<Item = X>,
767    mut eq_fn: impl FnMut(&X, &X) -> bool,
768) -> usize {
769    left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
770}
771
772/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
773pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
774    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
775}
776
777/// Returns the segments of a path that might have generic parameters.
778/// Usually just the last segment for free items, except for when the path resolves to an associated
779/// item, in which case it is the last two
780fn generic_path_segments<'tcx>(segments: &'tcx [PathSegment<'tcx>]) -> Option<&'tcx [PathSegment<'tcx>]> {
781    match segments.last()?.res {
782        Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) => {
783            // <Ty as module::Trait<T>>::assoc::<U>
784            //        ^^^^^^^^^^^^^^^^   ^^^^^^^^^^ segments: [module, Trait<T>, assoc<U>]
785            Some(&segments[segments.len().checked_sub(2)?..])
786        },
787        Res::Err => None,
788        _ => Some(slice::from_ref(segments.last()?)),
789    }
790}
791
792/// Type used to hash an ast element. This is different from the `Hash` trait
793/// on ast types as this
794/// trait would consider IDs and spans.
795///
796/// All expressions kind are hashed, but some might have a weaker hash.
797pub struct SpanlessHash<'a, 'tcx> {
798    /// Context used to evaluate constant expressions.
799    cx: &'a LateContext<'tcx>,
800    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
801    s: FxHasher,
802    path_check: PathCheck,
803}
804
805impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
806    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
807        Self {
808            cx,
809            maybe_typeck_results: cx.maybe_typeck_results(),
810            s: FxHasher::default(),
811            path_check: PathCheck::default(),
812        }
813    }
814
815    /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more
816    /// details.
817    #[must_use]
818    pub fn paths_by_resolution(self) -> Self {
819        Self {
820            path_check: PathCheck::Resolution,
821            ..self
822        }
823    }
824
825    pub fn finish(self) -> u64 {
826        self.s.finish()
827    }
828
829    pub fn hash_block(&mut self, b: &Block<'_>) {
830        for s in b.stmts {
831            self.hash_stmt(s);
832        }
833
834        if let Some(e) = b.expr {
835            self.hash_expr(e);
836        }
837
838        std::mem::discriminant(&b.rules).hash(&mut self.s);
839    }
840
841    #[expect(clippy::too_many_lines)]
842    pub fn hash_expr(&mut self, e: &Expr<'_>) {
843        let simple_const = self.maybe_typeck_results.and_then(|typeck_results| {
844            ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e)
845        });
846
847        // const hashing may result in the same hash as some unrelated node, so add a sort of
848        // discriminant depending on which path we're choosing next
849        simple_const.hash(&mut self.s);
850        if simple_const.is_some() {
851            return;
852        }
853
854        std::mem::discriminant(&e.kind).hash(&mut self.s);
855
856        match e.kind {
857            ExprKind::AddrOf(kind, m, e) => {
858                std::mem::discriminant(&kind).hash(&mut self.s);
859                m.hash(&mut self.s);
860                self.hash_expr(e);
861            },
862            ExprKind::Continue(i) => {
863                if let Some(i) = i.label {
864                    self.hash_name(i.ident.name);
865                }
866            },
867            ExprKind::Array(v) => {
868                self.hash_exprs(v);
869            },
870            ExprKind::Assign(l, r, _) => {
871                self.hash_expr(l);
872                self.hash_expr(r);
873            },
874            ExprKind::AssignOp(ref o, l, r) => {
875                std::mem::discriminant(&o.node).hash(&mut self.s);
876                self.hash_expr(l);
877                self.hash_expr(r);
878            },
879            ExprKind::Become(f) => {
880                self.hash_expr(f);
881            },
882            ExprKind::Block(b, _) => {
883                self.hash_block(b);
884            },
885            ExprKind::Binary(op, l, r) => {
886                std::mem::discriminant(&op.node).hash(&mut self.s);
887                self.hash_expr(l);
888                self.hash_expr(r);
889            },
890            ExprKind::Break(i, ref j) => {
891                if let Some(i) = i.label {
892                    self.hash_name(i.ident.name);
893                }
894                if let Some(j) = *j {
895                    self.hash_expr(j);
896                }
897            },
898            ExprKind::Call(fun, args) => {
899                self.hash_expr(fun);
900                self.hash_exprs(args);
901            },
902            ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
903                self.hash_expr(e);
904                self.hash_ty(ty);
905            },
906            ExprKind::Closure(&Closure {
907                capture_clause, body, ..
908            }) => {
909                std::mem::discriminant(&capture_clause).hash(&mut self.s);
910                // closures inherit TypeckResults
911                self.hash_expr(self.cx.tcx.hir_body(body).value);
912            },
913            ExprKind::ConstBlock(ref l_id) => {
914                self.hash_body(l_id.body);
915            },
916            ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
917                self.hash_expr(e);
918            },
919            ExprKind::Field(e, ref f) => {
920                self.hash_expr(e);
921                self.hash_name(f.name);
922            },
923            ExprKind::Index(a, i, _) => {
924                self.hash_expr(a);
925                self.hash_expr(i);
926            },
927            ExprKind::InlineAsm(asm) => {
928                for piece in asm.template {
929                    match piece {
930                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
931                        InlineAsmTemplatePiece::Placeholder {
932                            operand_idx,
933                            modifier,
934                            span: _,
935                        } => {
936                            operand_idx.hash(&mut self.s);
937                            modifier.hash(&mut self.s);
938                        },
939                    }
940                }
941                asm.options.hash(&mut self.s);
942                for (op, _op_sp) in asm.operands {
943                    match op {
944                        InlineAsmOperand::In { reg, expr } => {
945                            reg.hash(&mut self.s);
946                            self.hash_expr(expr);
947                        },
948                        InlineAsmOperand::Out { reg, late, expr } => {
949                            reg.hash(&mut self.s);
950                            late.hash(&mut self.s);
951                            if let Some(expr) = expr {
952                                self.hash_expr(expr);
953                            }
954                        },
955                        InlineAsmOperand::InOut { reg, late, expr } => {
956                            reg.hash(&mut self.s);
957                            late.hash(&mut self.s);
958                            self.hash_expr(expr);
959                        },
960                        InlineAsmOperand::SplitInOut {
961                            reg,
962                            late,
963                            in_expr,
964                            out_expr,
965                        } => {
966                            reg.hash(&mut self.s);
967                            late.hash(&mut self.s);
968                            self.hash_expr(in_expr);
969                            if let Some(out_expr) = out_expr {
970                                self.hash_expr(out_expr);
971                            }
972                        },
973                        InlineAsmOperand::SymFn { expr } => {
974                            self.hash_expr(expr);
975                        },
976                        InlineAsmOperand::Const { anon_const } => {
977                            self.hash_body(anon_const.body);
978                        },
979                        InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
980                        InlineAsmOperand::Label { block } => self.hash_block(block),
981                    }
982                }
983            },
984            ExprKind::Let(LetExpr { pat, init, ty, .. }) => {
985                self.hash_expr(init);
986                if let Some(ty) = ty {
987                    self.hash_ty(ty);
988                }
989                self.hash_pat(pat);
990            },
991            ExprKind::Lit(l) => {
992                l.node.hash(&mut self.s);
993            },
994            ExprKind::Loop(b, ref i, ..) => {
995                self.hash_block(b);
996                if let Some(i) = *i {
997                    self.hash_name(i.ident.name);
998                }
999            },
1000            ExprKind::If(cond, then, ref else_opt) => {
1001                self.hash_expr(cond);
1002                self.hash_expr(then);
1003                if let Some(e) = *else_opt {
1004                    self.hash_expr(e);
1005                }
1006            },
1007            ExprKind::Match(e, arms, ref s) => {
1008                self.hash_expr(e);
1009
1010                for arm in arms {
1011                    self.hash_pat(arm.pat);
1012                    if let Some(e) = arm.guard {
1013                        self.hash_expr(e);
1014                    }
1015                    self.hash_expr(arm.body);
1016                }
1017
1018                s.hash(&mut self.s);
1019            },
1020            ExprKind::MethodCall(path, receiver, args, ref _fn_span) => {
1021                self.hash_name(path.ident.name);
1022                self.hash_expr(receiver);
1023                self.hash_exprs(args);
1024            },
1025            ExprKind::OffsetOf(container, fields) => {
1026                self.hash_ty(container);
1027                for field in fields {
1028                    self.hash_name(field.name);
1029                }
1030            },
1031            ExprKind::Path(ref qpath) => {
1032                self.hash_qpath(qpath);
1033            },
1034            ExprKind::Repeat(e, len) => {
1035                self.hash_expr(e);
1036                self.hash_const_arg(len);
1037            },
1038            ExprKind::Ret(ref e) => {
1039                if let Some(e) = *e {
1040                    self.hash_expr(e);
1041                }
1042            },
1043            ExprKind::Struct(path, fields, ref expr) => {
1044                self.hash_qpath(path);
1045
1046                for f in fields {
1047                    self.hash_name(f.ident.name);
1048                    self.hash_expr(f.expr);
1049                }
1050
1051                if let StructTailExpr::Base(e) = *expr {
1052                    self.hash_expr(e);
1053                }
1054            },
1055            ExprKind::Tup(tup) => {
1056                self.hash_exprs(tup);
1057            },
1058            ExprKind::Use(expr, _) => {
1059                self.hash_expr(expr);
1060            },
1061            ExprKind::Unary(lop, le) => {
1062                std::mem::discriminant(&lop).hash(&mut self.s);
1063                self.hash_expr(le);
1064            },
1065            ExprKind::UnsafeBinderCast(kind, expr, ty) => {
1066                std::mem::discriminant(&kind).hash(&mut self.s);
1067                self.hash_expr(expr);
1068                if let Some(ty) = ty {
1069                    self.hash_ty(ty);
1070                }
1071            },
1072            ExprKind::Err(_) => {},
1073        }
1074    }
1075
1076    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
1077        for e in e {
1078            self.hash_expr(e);
1079        }
1080    }
1081
1082    pub fn hash_name(&mut self, n: Symbol) {
1083        n.hash(&mut self.s);
1084    }
1085
1086    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
1087        match *p {
1088            QPath::Resolved(_, path) => {
1089                self.hash_path(path);
1090            },
1091            QPath::TypeRelative(_, path) => {
1092                self.hash_name(path.ident.name);
1093            },
1094            QPath::LangItem(lang_item, ..) => {
1095                std::mem::discriminant(&lang_item).hash(&mut self.s);
1096            },
1097        }
1098        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
1099    }
1100
1101    pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) {
1102        std::mem::discriminant(&lit.kind).hash(&mut self.s);
1103        match &lit.kind {
1104            PatExprKind::Lit { lit, negated } => {
1105                lit.node.hash(&mut self.s);
1106                negated.hash(&mut self.s);
1107            },
1108            PatExprKind::ConstBlock(c) => self.hash_body(c.body),
1109            PatExprKind::Path(qpath) => self.hash_qpath(qpath),
1110        }
1111    }
1112
1113    pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
1114        std::mem::discriminant(&pat.kind).hash(&mut self.s);
1115        match pat.kind {
1116            TyPatKind::Range(s, e) => {
1117                self.hash_const_arg(s);
1118                self.hash_const_arg(e);
1119            },
1120            TyPatKind::Err(_) => {},
1121        }
1122    }
1123
1124    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
1125        std::mem::discriminant(&pat.kind).hash(&mut self.s);
1126        match pat.kind {
1127            PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
1128                std::mem::discriminant(&by_ref).hash(&mut self.s);
1129                std::mem::discriminant(&mutability).hash(&mut self.s);
1130                if let Some(pat) = pat {
1131                    self.hash_pat(pat);
1132                }
1133            },
1134            PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat),
1135            PatKind::Expr(expr) => self.hash_pat_expr(expr),
1136            PatKind::Or(pats) => {
1137                for pat in pats {
1138                    self.hash_pat(pat);
1139                }
1140            },
1141            PatKind::Range(s, e, i) => {
1142                if let Some(s) = s {
1143                    self.hash_pat_expr(s);
1144                }
1145                if let Some(e) = e {
1146                    self.hash_pat_expr(e);
1147                }
1148                std::mem::discriminant(&i).hash(&mut self.s);
1149            },
1150            PatKind::Ref(pat, mu) => {
1151                self.hash_pat(pat);
1152                std::mem::discriminant(&mu).hash(&mut self.s);
1153            },
1154            PatKind::Guard(pat, guard) => {
1155                self.hash_pat(pat);
1156                self.hash_expr(guard);
1157            },
1158            PatKind::Slice(l, m, r) => {
1159                for pat in l {
1160                    self.hash_pat(pat);
1161                }
1162                if let Some(pat) = m {
1163                    self.hash_pat(pat);
1164                }
1165                for pat in r {
1166                    self.hash_pat(pat);
1167                }
1168            },
1169            PatKind::Struct(ref qpath, fields, e) => {
1170                self.hash_qpath(qpath);
1171                for f in fields {
1172                    self.hash_name(f.ident.name);
1173                    self.hash_pat(f.pat);
1174                }
1175                e.hash(&mut self.s);
1176            },
1177            PatKind::Tuple(pats, e) => {
1178                for pat in pats {
1179                    self.hash_pat(pat);
1180                }
1181                e.hash(&mut self.s);
1182            },
1183            PatKind::TupleStruct(ref qpath, pats, e) => {
1184                self.hash_qpath(qpath);
1185                for pat in pats {
1186                    self.hash_pat(pat);
1187                }
1188                e.hash(&mut self.s);
1189            },
1190            PatKind::Never | PatKind::Wild | PatKind::Err(_) => {},
1191        }
1192    }
1193
1194    pub fn hash_path(&mut self, path: &Path<'_>) {
1195        match path.res {
1196            // constant hash since equality is dependant on inter-expression context
1197            // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
1198            // even though the binding names are different and they have different `HirId`s.
1199            Res::Local(_) => 1_usize.hash(&mut self.s),
1200            _ => {
1201                if let PathCheck::Resolution = self.path_check
1202                    && let [.., last] = path.segments
1203                    && let Some(segments) = generic_path_segments(path.segments)
1204                {
1205                    for seg in segments {
1206                        self.hash_generic_args(seg.args().args);
1207                    }
1208                    last.res.hash(&mut self.s);
1209                } else {
1210                    for seg in path.segments {
1211                        self.hash_name(seg.ident.name);
1212                        self.hash_generic_args(seg.args().args);
1213                    }
1214                }
1215            },
1216        }
1217    }
1218
1219    pub fn hash_modifiers(&mut self, modifiers: TraitBoundModifiers) {
1220        let TraitBoundModifiers { constness, polarity } = modifiers;
1221        std::mem::discriminant(&polarity).hash(&mut self.s);
1222        std::mem::discriminant(&constness).hash(&mut self.s);
1223    }
1224
1225    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
1226        std::mem::discriminant(&b.kind).hash(&mut self.s);
1227
1228        match &b.kind {
1229            StmtKind::Let(local) => {
1230                self.hash_pat(local.pat);
1231                if let Some(init) = local.init {
1232                    self.hash_expr(init);
1233                }
1234                if let Some(els) = local.els {
1235                    self.hash_block(els);
1236                }
1237            },
1238            StmtKind::Item(..) => {},
1239            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
1240                self.hash_expr(expr);
1241            },
1242        }
1243    }
1244
1245    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
1246        lifetime.ident.name.hash(&mut self.s);
1247        std::mem::discriminant(&lifetime.res).hash(&mut self.s);
1248        if let LifetimeName::Param(param_id) = lifetime.res {
1249            param_id.hash(&mut self.s);
1250        }
1251    }
1252
1253    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
1254        std::mem::discriminant(&ty.kind).hash(&mut self.s);
1255        self.hash_tykind(&ty.kind);
1256    }
1257
1258    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
1259        match ty {
1260            TyKind::Slice(ty) => {
1261                self.hash_ty(ty);
1262            },
1263            &TyKind::Array(ty, len) => {
1264                self.hash_ty(ty);
1265                self.hash_const_arg(len);
1266            },
1267            TyKind::Pat(ty, pat) => {
1268                self.hash_ty(ty);
1269                self.hash_ty_pat(pat);
1270            },
1271            TyKind::Ptr(mut_ty) => {
1272                self.hash_ty(mut_ty.ty);
1273                mut_ty.mutbl.hash(&mut self.s);
1274            },
1275            TyKind::Ref(lifetime, mut_ty) => {
1276                self.hash_lifetime(lifetime);
1277                self.hash_ty(mut_ty.ty);
1278                mut_ty.mutbl.hash(&mut self.s);
1279            },
1280            TyKind::BareFn(bfn) => {
1281                bfn.safety.hash(&mut self.s);
1282                bfn.abi.hash(&mut self.s);
1283                for arg in bfn.decl.inputs {
1284                    self.hash_ty(arg);
1285                }
1286                std::mem::discriminant(&bfn.decl.output).hash(&mut self.s);
1287                match bfn.decl.output {
1288                    FnRetTy::DefaultReturn(_) => {},
1289                    FnRetTy::Return(ty) => {
1290                        self.hash_ty(ty);
1291                    },
1292                }
1293                bfn.decl.c_variadic.hash(&mut self.s);
1294            },
1295            TyKind::Tup(ty_list) => {
1296                for ty in *ty_list {
1297                    self.hash_ty(ty);
1298                }
1299            },
1300            TyKind::Path(qpath) => self.hash_qpath(qpath),
1301            TyKind::TraitObject(_, lifetime) => {
1302                self.hash_lifetime(lifetime);
1303            },
1304            TyKind::Typeof(anon_const) => {
1305                self.hash_body(anon_const.body);
1306            },
1307            TyKind::UnsafeBinder(binder) => {
1308                self.hash_ty(binder.inner_ty);
1309            },
1310            TyKind::Err(_)
1311            | TyKind::Infer(())
1312            | TyKind::Never
1313            | TyKind::InferDelegation(..)
1314            | TyKind::OpaqueDef(_)
1315            | TyKind::TraitAscription(_) => {},
1316        }
1317    }
1318
1319    pub fn hash_body(&mut self, body_id: BodyId) {
1320        // swap out TypeckResults when hashing a body
1321        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
1322        self.hash_expr(self.cx.tcx.hir_body(body_id).value);
1323        self.maybe_typeck_results = old_maybe_typeck_results;
1324    }
1325
1326    fn hash_const_arg(&mut self, const_arg: &ConstArg<'_>) {
1327        match &const_arg.kind {
1328            ConstArgKind::Path(path) => self.hash_qpath(path),
1329            ConstArgKind::Anon(anon) => self.hash_body(anon.body),
1330            ConstArgKind::Infer(..) => {},
1331        }
1332    }
1333
1334    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
1335        for arg in arg_list {
1336            match *arg {
1337                GenericArg::Lifetime(l) => self.hash_lifetime(l),
1338                GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()),
1339                GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()),
1340                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
1341            }
1342        }
1343    }
1344}
1345
1346pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 {
1347    let mut h = SpanlessHash::new(cx);
1348    h.hash_stmt(s);
1349    h.finish()
1350}
1351
1352pub fn is_bool(ty: &Ty<'_>) -> bool {
1353    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
1354        matches!(path.res, Res::PrimTy(PrimTy::Bool))
1355    } else {
1356        false
1357    }
1358}
1359
1360pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
1361    let mut h = SpanlessHash::new(cx);
1362    h.hash_expr(e);
1363    h.finish()
1364}
1365
1366fn eq_span_tokens(
1367    cx: &LateContext<'_>,
1368    left: impl SpanRange,
1369    right: impl SpanRange,
1370    pred: impl Fn(TokenKind) -> bool,
1371) -> bool {
1372    fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
1373        if let Some(lsrc) = left.get_source_range(cx)
1374            && let Some(lsrc) = lsrc.as_str()
1375            && let Some(rsrc) = right.get_source_range(cx)
1376            && let Some(rsrc) = rsrc.as_str()
1377        {
1378            let pred = |&(token, ..): &(TokenKind, _, _)| pred(token);
1379            let map = |(_, source, _)| source;
1380
1381            let ltok = tokenize_with_text(lsrc).filter(pred).map(map);
1382            let rtok = tokenize_with_text(rsrc).filter(pred).map(map);
1383            ltok.eq(rtok)
1384        } else {
1385            // Unable to access the source. Conservatively assume the blocks aren't equal.
1386            false
1387        }
1388    }
1389    f(cx, left.into_range(), right.into_range(), pred)
1390}