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::Type(le, lt), &ExprKind::Type(re, rt)) => self.eq_expr(le, re) && self.eq_ty(lt, rt),
397            (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
398            (&ExprKind::Yield(le, _), &ExprKind::Yield(re, _)) => return self.eq_expr(le, re),
399            (
400                // Else branches for branches above, grouped as per `match_same_arms`.
401                | &ExprKind::AddrOf(..)
402                | &ExprKind::Array(..)
403                | &ExprKind::Assign(..)
404                | &ExprKind::AssignOp(..)
405                | &ExprKind::Binary(..)
406                | &ExprKind::Become(..)
407                | &ExprKind::Block(..)
408                | &ExprKind::Break(..)
409                | &ExprKind::Call(..)
410                | &ExprKind::Cast(..)
411                | &ExprKind::ConstBlock(..)
412                | &ExprKind::Continue(..)
413                | &ExprKind::DropTemps(..)
414                | &ExprKind::Field(..)
415                | &ExprKind::Index(..)
416                | &ExprKind::If(..)
417                | &ExprKind::Let(..)
418                | &ExprKind::Lit(..)
419                | &ExprKind::Loop(..)
420                | &ExprKind::Match(..)
421                | &ExprKind::MethodCall(..)
422                | &ExprKind::OffsetOf(..)
423                | &ExprKind::Path(..)
424                | &ExprKind::Repeat(..)
425                | &ExprKind::Ret(..)
426                | &ExprKind::Struct(..)
427                | &ExprKind::Tup(..)
428                | &ExprKind::Type(..)
429                | &ExprKind::Unary(..)
430                | &ExprKind::Yield(..)
431                | &ExprKind::UnsafeBinderCast(..)
432
433                // --- Special cases that do not have a positive branch.
434
435                // `Err` represents an invalid expression, so let's never assume that
436                // an invalid expressions is equal to anything.
437                | &ExprKind::Err(..)
438
439                // For the time being, we always consider that two closures are unequal.
440                // This behavior may change in the future.
441                | &ExprKind::Closure(..)
442                // For the time being, we always consider that two instances of InlineAsm are different.
443                // This behavior may change in the future.
444                | &ExprKind::InlineAsm(_)
445                , _
446            ) => false,
447        };
448        (is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
449            || self.inner.expr_fallback.as_mut().is_some_and(|f| f(left, right))
450    }
451
452    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
453        over(left, right, |l, r| self.eq_expr(l, r))
454    }
455
456    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
457        left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
458    }
459
460    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
461        match (left, right) {
462            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_const_arg(l.as_unambig_ct(), r.as_unambig_ct()),
463            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
464            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty.as_unambig_ty(), r_ty.as_unambig_ty()),
465            (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
466            _ => false,
467        }
468    }
469
470    fn eq_const_arg(&mut self, left: &ConstArg<'_>, right: &ConstArg<'_>) -> bool {
471        match (&left.kind, &right.kind) {
472            (ConstArgKind::Path(l_p), ConstArgKind::Path(r_p)) => self.eq_qpath(l_p, r_p),
473            (ConstArgKind::Anon(l_an), ConstArgKind::Anon(r_an)) => self.eq_body(l_an.body, r_an.body),
474            (ConstArgKind::Infer(..), ConstArgKind::Infer(..)) => true,
475            // Use explicit match for now since ConstArg is undergoing flux.
476            (ConstArgKind::Path(..), ConstArgKind::Anon(..))
477            | (ConstArgKind::Anon(..), ConstArgKind::Path(..))
478            | (ConstArgKind::Infer(..), _)
479            | (_, ConstArgKind::Infer(..)) => false,
480        }
481    }
482
483    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
484        left.res == right.res
485    }
486
487    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
488        let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
489        li.name == ri.name && self.eq_pat(lp, rp)
490    }
491
492    fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool {
493        match (&left.kind, &right.kind) {
494            (
495                &PatExprKind::Lit {
496                    lit: left,
497                    negated: left_neg,
498                },
499                &PatExprKind::Lit {
500                    lit: right,
501                    negated: right_neg,
502                },
503            ) => left_neg == right_neg && left.node == right.node,
504            (PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body),
505            (PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right),
506            (PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false,
507        }
508    }
509
510    /// Checks whether two patterns are the same.
511    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
512        match (&left.kind, &right.kind) {
513            (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
514            (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
515                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
516            },
517            (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
518                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
519            },
520            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
521                let eq = lb == rb && both(lp.as_ref(), rp.as_ref(), |l, r| self.eq_pat(l, r));
522                if eq {
523                    self.locals.insert(li, ri);
524                }
525                eq
526            },
527            (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r),
528            (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
529            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
530                both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b))
531                    && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
532                    && (li == ri)
533            },
534            (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
535            (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
536                over(ls, rs, |l, r| self.eq_pat(l, r))
537                    && over(le, re, |l, r| self.eq_pat(l, r))
538                    && both(li.as_ref(), ri.as_ref(), |l, r| self.eq_pat(l, r))
539            },
540            (&PatKind::Wild, &PatKind::Wild) => true,
541            _ => false,
542        }
543    }
544
545    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
546        match (left, right) {
547            (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
548                both(lty.as_ref(), rty.as_ref(), |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
549            },
550            (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
551                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
552            },
553            (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
554            _ => false,
555        }
556    }
557
558    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
559        match (left.res, right.res) {
560            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
561            (Res::Local(_), _) | (_, Res::Local(_)) => false,
562            _ => self.eq_path_segments(left.segments, right.segments),
563        }
564    }
565
566    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
567        if left.parenthesized == right.parenthesized {
568            over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
569                && over(left.constraints, right.constraints, |l, r| self.eq_assoc_eq_constraint(l, r))
570        } else {
571            false
572        }
573    }
574
575    pub fn eq_path_segments<'tcx>(
576        &mut self,
577        mut left: &'tcx [PathSegment<'tcx>],
578        mut right: &'tcx [PathSegment<'tcx>],
579    ) -> bool {
580        if let PathCheck::Resolution = self.inner.path_check
581            && let Some(left_seg) = generic_path_segments(left)
582            && let Some(right_seg) = generic_path_segments(right)
583        {
584            // If we compare by resolution, then only check the last segments that could possibly have generic
585            // arguments
586            left = left_seg;
587            right = right_seg;
588        }
589
590        over(left, right, |l, r| self.eq_path_segment(l, r))
591    }
592
593    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
594        if !self.eq_path_parameters(left.args(), right.args()) {
595            return false;
596        }
597
598        if let PathCheck::Resolution = self.inner.path_check
599            && left.res != Res::Err
600            && right.res != Res::Err
601        {
602            left.res == right.res
603        } else {
604            // The == of idents doesn't work with different contexts,
605            // we have to be explicit about hygiene
606            left.ident.name == right.ident.name
607        }
608    }
609
610    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
611        match (&left.kind, &right.kind) {
612            (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
613            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_const_arg(ll, rl),
614            (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty),
615            (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => {
616                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
617            },
618            (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
619            (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
620            (&TyKind::Infer(()), &TyKind::Infer(())) => true,
621            _ => false,
622        }
623    }
624
625    /// Checks whether two constraints designate the same equality constraint (same name, and same
626    /// type or const).
627    fn eq_assoc_eq_constraint(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool {
628        // TODO: this could be extended to check for identical associated item bound constraints
629        left.ident.name == right.ident.name
630            && (both_some_and(left.ty(), right.ty(), |l, r| self.eq_ty(l, r))
631                || both_some_and(left.ct(), right.ct(), |l, r| self.eq_const_arg(l, r)))
632    }
633
634    fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
635        if self.left_ctxt == left && self.right_ctxt == right {
636            return true;
637        } else if self.left_ctxt == left || self.right_ctxt == right {
638            // Only one context has changed. This can only happen if the two nodes are written differently.
639            return false;
640        } else if left != SyntaxContext::root() {
641            let mut left_data = left.outer_expn_data();
642            let mut right_data = right.outer_expn_data();
643            loop {
644                use TokenKind::{BlockComment, LineComment, Whitespace};
645                if left_data.macro_def_id != right_data.macro_def_id
646                    || (matches!(
647                        left_data.kind,
648                        ExpnKind::Macro(MacroKind::Bang, name)
649                        if name == sym::cfg || name == sym::option_env
650                    ) && !eq_span_tokens(self.inner.cx, left_data.call_site, right_data.call_site, |t| {
651                        !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. })
652                    }))
653                {
654                    // Either a different chain of macro calls, or different arguments to the `cfg` macro.
655                    return false;
656                }
657                let left_ctxt = left_data.call_site.ctxt();
658                let right_ctxt = right_data.call_site.ctxt();
659                if left_ctxt == SyntaxContext::root() && right_ctxt == SyntaxContext::root() {
660                    break;
661                }
662                if left_ctxt == SyntaxContext::root() || right_ctxt == SyntaxContext::root() {
663                    // Different lengths for the expansion stack. This can only happen if nodes are written differently,
664                    // or shouldn't be compared to start with.
665                    return false;
666                }
667                left_data = left_ctxt.outer_expn_data();
668                right_data = right_ctxt.outer_expn_data();
669            }
670        }
671        self.left_ctxt = left;
672        self.right_ctxt = right;
673        true
674    }
675}
676
677/// Some simple reductions like `{ return }` => `return`
678fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
679    if let ExprKind::Block(block, _) = kind {
680        match (block.stmts, block.expr) {
681            // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
682            // block with an empty span.
683            ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
684            // `{}` => `()`
685            ([], None)
686                if block.span.check_source_text(cx, |src| {
687                    tokenize(src)
688                        .map(|t| t.kind)
689                        .filter(|t| {
690                            !matches!(
691                                t,
692                                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
693                            )
694                        })
695                        .eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied())
696                }) =>
697            {
698                &ExprKind::Tup(&[])
699            },
700            ([], Some(expr)) => match expr.kind {
701                // `{ return .. }` => `return ..`
702                ExprKind::Ret(..) => &expr.kind,
703                _ => kind,
704            },
705            ([stmt], None) => match stmt.kind {
706                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
707                    // `{ return ..; }` => `return ..`
708                    ExprKind::Ret(..) => &expr.kind,
709                    _ => kind,
710                },
711                _ => kind,
712            },
713            _ => kind,
714        }
715    } else {
716        kind
717    }
718}
719
720fn swap_binop<'a>(
721    binop: BinOpKind,
722    lhs: &'a Expr<'a>,
723    rhs: &'a Expr<'a>,
724) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
725    match binop {
726        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
727            Some((binop, rhs, lhs))
728        },
729        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
730        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
731        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
732        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
733        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
734        | BinOpKind::Shl
735        | BinOpKind::Shr
736        | BinOpKind::Rem
737        | BinOpKind::Sub
738        | BinOpKind::Div
739        | BinOpKind::And
740        | BinOpKind::Or => None,
741    }
742}
743
744/// Checks if the two `Option`s are both `None` or some equal values as per
745/// `eq_fn`.
746pub fn both<X>(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
747    l.as_ref()
748        .map_or_else(|| r.is_none(), |x| r.as_ref().is_some_and(|y| eq_fn(x, y)))
749}
750
751/// Checks if the two `Option`s are both `Some` and pass the predicate function.
752pub fn both_some_and<X, Y>(l: Option<X>, r: Option<Y>, mut pred: impl FnMut(X, Y) -> bool) -> bool {
753    l.is_some_and(|l| r.is_some_and(|r| pred(l, r)))
754}
755
756/// Checks if two slices are equal as per `eq_fn`.
757pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
758    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
759}
760
761/// Counts how many elements of the slices are equal as per `eq_fn`.
762pub fn count_eq<X: Sized>(
763    left: &mut dyn Iterator<Item = X>,
764    right: &mut dyn Iterator<Item = X>,
765    mut eq_fn: impl FnMut(&X, &X) -> bool,
766) -> usize {
767    left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
768}
769
770/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
771pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
772    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
773}
774
775/// Returns the segments of a path that might have generic parameters.
776/// Usually just the last segment for free items, except for when the path resolves to an associated
777/// item, in which case it is the last two
778fn generic_path_segments<'tcx>(segments: &'tcx [PathSegment<'tcx>]) -> Option<&'tcx [PathSegment<'tcx>]> {
779    match segments.last()?.res {
780        Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) => {
781            // <Ty as module::Trait<T>>::assoc::<U>
782            //        ^^^^^^^^^^^^^^^^   ^^^^^^^^^^ segments: [module, Trait<T>, assoc<U>]
783            Some(&segments[segments.len().checked_sub(2)?..])
784        },
785        Res::Err => None,
786        _ => Some(slice::from_ref(segments.last()?)),
787    }
788}
789
790/// Type used to hash an ast element. This is different from the `Hash` trait
791/// on ast types as this
792/// trait would consider IDs and spans.
793///
794/// All expressions kind are hashed, but some might have a weaker hash.
795pub struct SpanlessHash<'a, 'tcx> {
796    /// Context used to evaluate constant expressions.
797    cx: &'a LateContext<'tcx>,
798    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
799    s: FxHasher,
800    path_check: PathCheck,
801}
802
803impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
804    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
805        Self {
806            cx,
807            maybe_typeck_results: cx.maybe_typeck_results(),
808            s: FxHasher::default(),
809            path_check: PathCheck::default(),
810        }
811    }
812
813    /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more
814    /// details.
815    #[must_use]
816    pub fn paths_by_resolution(self) -> Self {
817        Self {
818            path_check: PathCheck::Resolution,
819            ..self
820        }
821    }
822
823    pub fn finish(self) -> u64 {
824        self.s.finish()
825    }
826
827    pub fn hash_block(&mut self, b: &Block<'_>) {
828        for s in b.stmts {
829            self.hash_stmt(s);
830        }
831
832        if let Some(e) = b.expr {
833            self.hash_expr(e);
834        }
835
836        std::mem::discriminant(&b.rules).hash(&mut self.s);
837    }
838
839    #[expect(clippy::too_many_lines)]
840    pub fn hash_expr(&mut self, e: &Expr<'_>) {
841        let simple_const = self.maybe_typeck_results.and_then(|typeck_results| {
842            ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e)
843        });
844
845        // const hashing may result in the same hash as some unrelated node, so add a sort of
846        // discriminant depending on which path we're choosing next
847        simple_const.hash(&mut self.s);
848        if simple_const.is_some() {
849            return;
850        }
851
852        std::mem::discriminant(&e.kind).hash(&mut self.s);
853
854        match e.kind {
855            ExprKind::AddrOf(kind, m, e) => {
856                std::mem::discriminant(&kind).hash(&mut self.s);
857                m.hash(&mut self.s);
858                self.hash_expr(e);
859            },
860            ExprKind::Continue(i) => {
861                if let Some(i) = i.label {
862                    self.hash_name(i.ident.name);
863                }
864            },
865            ExprKind::Array(v) => {
866                self.hash_exprs(v);
867            },
868            ExprKind::Assign(l, r, _) => {
869                self.hash_expr(l);
870                self.hash_expr(r);
871            },
872            ExprKind::AssignOp(ref o, l, r) => {
873                std::mem::discriminant(&o.node).hash(&mut self.s);
874                self.hash_expr(l);
875                self.hash_expr(r);
876            },
877            ExprKind::Become(f) => {
878                self.hash_expr(f);
879            },
880            ExprKind::Block(b, _) => {
881                self.hash_block(b);
882            },
883            ExprKind::Binary(op, l, r) => {
884                std::mem::discriminant(&op.node).hash(&mut self.s);
885                self.hash_expr(l);
886                self.hash_expr(r);
887            },
888            ExprKind::Break(i, ref j) => {
889                if let Some(i) = i.label {
890                    self.hash_name(i.ident.name);
891                }
892                if let Some(j) = *j {
893                    self.hash_expr(j);
894                }
895            },
896            ExprKind::Call(fun, args) => {
897                self.hash_expr(fun);
898                self.hash_exprs(args);
899            },
900            ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
901                self.hash_expr(e);
902                self.hash_ty(ty);
903            },
904            ExprKind::Closure(&Closure {
905                capture_clause, body, ..
906            }) => {
907                std::mem::discriminant(&capture_clause).hash(&mut self.s);
908                // closures inherit TypeckResults
909                self.hash_expr(self.cx.tcx.hir().body(body).value);
910            },
911            ExprKind::ConstBlock(ref l_id) => {
912                self.hash_body(l_id.body);
913            },
914            ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
915                self.hash_expr(e);
916            },
917            ExprKind::Field(e, ref f) => {
918                self.hash_expr(e);
919                self.hash_name(f.name);
920            },
921            ExprKind::Index(a, i, _) => {
922                self.hash_expr(a);
923                self.hash_expr(i);
924            },
925            ExprKind::InlineAsm(asm) => {
926                for piece in asm.template {
927                    match piece {
928                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
929                        InlineAsmTemplatePiece::Placeholder {
930                            operand_idx,
931                            modifier,
932                            span: _,
933                        } => {
934                            operand_idx.hash(&mut self.s);
935                            modifier.hash(&mut self.s);
936                        },
937                    }
938                }
939                asm.options.hash(&mut self.s);
940                for (op, _op_sp) in asm.operands {
941                    match op {
942                        InlineAsmOperand::In { reg, expr } => {
943                            reg.hash(&mut self.s);
944                            self.hash_expr(expr);
945                        },
946                        InlineAsmOperand::Out { reg, late, expr } => {
947                            reg.hash(&mut self.s);
948                            late.hash(&mut self.s);
949                            if let Some(expr) = expr {
950                                self.hash_expr(expr);
951                            }
952                        },
953                        InlineAsmOperand::InOut { reg, late, expr } => {
954                            reg.hash(&mut self.s);
955                            late.hash(&mut self.s);
956                            self.hash_expr(expr);
957                        },
958                        InlineAsmOperand::SplitInOut {
959                            reg,
960                            late,
961                            in_expr,
962                            out_expr,
963                        } => {
964                            reg.hash(&mut self.s);
965                            late.hash(&mut self.s);
966                            self.hash_expr(in_expr);
967                            if let Some(out_expr) = out_expr {
968                                self.hash_expr(out_expr);
969                            }
970                        },
971                        InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
972                            self.hash_body(anon_const.body);
973                        },
974                        InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
975                        InlineAsmOperand::Label { block } => self.hash_block(block),
976                    }
977                }
978            },
979            ExprKind::Let(LetExpr { pat, init, ty, .. }) => {
980                self.hash_expr(init);
981                if let Some(ty) = ty {
982                    self.hash_ty(ty);
983                }
984                self.hash_pat(pat);
985            },
986            ExprKind::Lit(l) => {
987                l.node.hash(&mut self.s);
988            },
989            ExprKind::Loop(b, ref i, ..) => {
990                self.hash_block(b);
991                if let Some(i) = *i {
992                    self.hash_name(i.ident.name);
993                }
994            },
995            ExprKind::If(cond, then, ref else_opt) => {
996                self.hash_expr(cond);
997                self.hash_expr(then);
998                if let Some(e) = *else_opt {
999                    self.hash_expr(e);
1000                }
1001            },
1002            ExprKind::Match(e, arms, ref s) => {
1003                self.hash_expr(e);
1004
1005                for arm in arms {
1006                    self.hash_pat(arm.pat);
1007                    if let Some(e) = arm.guard {
1008                        self.hash_expr(e);
1009                    }
1010                    self.hash_expr(arm.body);
1011                }
1012
1013                s.hash(&mut self.s);
1014            },
1015            ExprKind::MethodCall(path, receiver, args, ref _fn_span) => {
1016                self.hash_name(path.ident.name);
1017                self.hash_expr(receiver);
1018                self.hash_exprs(args);
1019            },
1020            ExprKind::OffsetOf(container, fields) => {
1021                self.hash_ty(container);
1022                for field in fields {
1023                    self.hash_name(field.name);
1024                }
1025            },
1026            ExprKind::Path(ref qpath) => {
1027                self.hash_qpath(qpath);
1028            },
1029            ExprKind::Repeat(e, len) => {
1030                self.hash_expr(e);
1031                self.hash_const_arg(len);
1032            },
1033            ExprKind::Ret(ref e) => {
1034                if let Some(e) = *e {
1035                    self.hash_expr(e);
1036                }
1037            },
1038            ExprKind::Struct(path, fields, ref expr) => {
1039                self.hash_qpath(path);
1040
1041                for f in fields {
1042                    self.hash_name(f.ident.name);
1043                    self.hash_expr(f.expr);
1044                }
1045
1046                if let StructTailExpr::Base(e) = *expr {
1047                    self.hash_expr(e);
1048                }
1049            },
1050            ExprKind::Tup(tup) => {
1051                self.hash_exprs(tup);
1052            },
1053            ExprKind::Unary(lop, le) => {
1054                std::mem::discriminant(&lop).hash(&mut self.s);
1055                self.hash_expr(le);
1056            },
1057            ExprKind::UnsafeBinderCast(kind, expr, ty) => {
1058                std::mem::discriminant(&kind).hash(&mut self.s);
1059                self.hash_expr(expr);
1060                if let Some(ty) = ty {
1061                    self.hash_ty(ty);
1062                }
1063            },
1064            ExprKind::Err(_) => {},
1065        }
1066    }
1067
1068    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
1069        for e in e {
1070            self.hash_expr(e);
1071        }
1072    }
1073
1074    pub fn hash_name(&mut self, n: Symbol) {
1075        n.hash(&mut self.s);
1076    }
1077
1078    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
1079        match *p {
1080            QPath::Resolved(_, path) => {
1081                self.hash_path(path);
1082            },
1083            QPath::TypeRelative(_, path) => {
1084                self.hash_name(path.ident.name);
1085            },
1086            QPath::LangItem(lang_item, ..) => {
1087                std::mem::discriminant(&lang_item).hash(&mut self.s);
1088            },
1089        }
1090        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
1091    }
1092
1093    pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) {
1094        std::mem::discriminant(&lit.kind).hash(&mut self.s);
1095        match &lit.kind {
1096            PatExprKind::Lit { lit, negated } => {
1097                lit.node.hash(&mut self.s);
1098                negated.hash(&mut self.s);
1099            },
1100            PatExprKind::ConstBlock(c) => self.hash_body(c.body),
1101            PatExprKind::Path(qpath) => self.hash_qpath(qpath),
1102        }
1103    }
1104
1105    pub fn hash_ty_pat(&mut self, pat: &TyPat<'_>) {
1106        std::mem::discriminant(&pat.kind).hash(&mut self.s);
1107        match pat.kind {
1108            TyPatKind::Range(s, e, i) => {
1109                if let Some(s) = s {
1110                    self.hash_const_arg(s);
1111                }
1112                if let Some(e) = e {
1113                    self.hash_const_arg(e);
1114                }
1115                std::mem::discriminant(&i).hash(&mut self.s);
1116            },
1117            TyPatKind::Err(_) => {},
1118        }
1119    }
1120
1121    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
1122        std::mem::discriminant(&pat.kind).hash(&mut self.s);
1123        match pat.kind {
1124            PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => {
1125                std::mem::discriminant(&by_ref).hash(&mut self.s);
1126                std::mem::discriminant(&mutability).hash(&mut self.s);
1127                if let Some(pat) = pat {
1128                    self.hash_pat(pat);
1129                }
1130            },
1131            PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat),
1132            PatKind::Expr(expr) => self.hash_pat_expr(expr),
1133            PatKind::Or(pats) => {
1134                for pat in pats {
1135                    self.hash_pat(pat);
1136                }
1137            },
1138            PatKind::Range(s, e, i) => {
1139                if let Some(s) = s {
1140                    self.hash_pat_expr(s);
1141                }
1142                if let Some(e) = e {
1143                    self.hash_pat_expr(e);
1144                }
1145                std::mem::discriminant(&i).hash(&mut self.s);
1146            },
1147            PatKind::Ref(pat, mu) => {
1148                self.hash_pat(pat);
1149                std::mem::discriminant(&mu).hash(&mut self.s);
1150            },
1151            PatKind::Guard(pat, guard) => {
1152                self.hash_pat(pat);
1153                self.hash_expr(guard);
1154            },
1155            PatKind::Slice(l, m, r) => {
1156                for pat in l {
1157                    self.hash_pat(pat);
1158                }
1159                if let Some(pat) = m {
1160                    self.hash_pat(pat);
1161                }
1162                for pat in r {
1163                    self.hash_pat(pat);
1164                }
1165            },
1166            PatKind::Struct(ref qpath, fields, e) => {
1167                self.hash_qpath(qpath);
1168                for f in fields {
1169                    self.hash_name(f.ident.name);
1170                    self.hash_pat(f.pat);
1171                }
1172                e.hash(&mut self.s);
1173            },
1174            PatKind::Tuple(pats, e) => {
1175                for pat in pats {
1176                    self.hash_pat(pat);
1177                }
1178                e.hash(&mut self.s);
1179            },
1180            PatKind::TupleStruct(ref qpath, pats, e) => {
1181                self.hash_qpath(qpath);
1182                for pat in pats {
1183                    self.hash_pat(pat);
1184                }
1185                e.hash(&mut self.s);
1186            },
1187            PatKind::Never | PatKind::Wild | PatKind::Err(_) => {},
1188        }
1189    }
1190
1191    pub fn hash_path(&mut self, path: &Path<'_>) {
1192        match path.res {
1193            // constant hash since equality is dependant on inter-expression context
1194            // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
1195            // even though the binding names are different and they have different `HirId`s.
1196            Res::Local(_) => 1_usize.hash(&mut self.s),
1197            _ => {
1198                if let PathCheck::Resolution = self.path_check
1199                    && let [.., last] = path.segments
1200                    && let Some(segments) = generic_path_segments(path.segments)
1201                {
1202                    for seg in segments {
1203                        self.hash_generic_args(seg.args().args);
1204                    }
1205                    last.res.hash(&mut self.s);
1206                } else {
1207                    for seg in path.segments {
1208                        self.hash_name(seg.ident.name);
1209                        self.hash_generic_args(seg.args().args);
1210                    }
1211                }
1212            },
1213        }
1214    }
1215
1216    pub fn hash_modifiers(&mut self, modifiers: TraitBoundModifiers) {
1217        let TraitBoundModifiers { constness, polarity } = modifiers;
1218        std::mem::discriminant(&polarity).hash(&mut self.s);
1219        std::mem::discriminant(&constness).hash(&mut self.s);
1220    }
1221
1222    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
1223        std::mem::discriminant(&b.kind).hash(&mut self.s);
1224
1225        match &b.kind {
1226            StmtKind::Let(local) => {
1227                self.hash_pat(local.pat);
1228                if let Some(init) = local.init {
1229                    self.hash_expr(init);
1230                }
1231                if let Some(els) = local.els {
1232                    self.hash_block(els);
1233                }
1234            },
1235            StmtKind::Item(..) => {},
1236            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
1237                self.hash_expr(expr);
1238            },
1239        }
1240    }
1241
1242    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
1243        lifetime.ident.name.hash(&mut self.s);
1244        std::mem::discriminant(&lifetime.res).hash(&mut self.s);
1245        if let LifetimeName::Param(param_id) = lifetime.res {
1246            param_id.hash(&mut self.s);
1247        }
1248    }
1249
1250    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
1251        std::mem::discriminant(&ty.kind).hash(&mut self.s);
1252        self.hash_tykind(&ty.kind);
1253    }
1254
1255    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
1256        match ty {
1257            TyKind::Slice(ty) => {
1258                self.hash_ty(ty);
1259            },
1260            &TyKind::Array(ty, len) => {
1261                self.hash_ty(ty);
1262                self.hash_const_arg(len);
1263            },
1264            TyKind::Pat(ty, pat) => {
1265                self.hash_ty(ty);
1266                self.hash_ty_pat(pat);
1267            },
1268            TyKind::Ptr(mut_ty) => {
1269                self.hash_ty(mut_ty.ty);
1270                mut_ty.mutbl.hash(&mut self.s);
1271            },
1272            TyKind::Ref(lifetime, mut_ty) => {
1273                self.hash_lifetime(lifetime);
1274                self.hash_ty(mut_ty.ty);
1275                mut_ty.mutbl.hash(&mut self.s);
1276            },
1277            TyKind::BareFn(bfn) => {
1278                bfn.safety.hash(&mut self.s);
1279                bfn.abi.hash(&mut self.s);
1280                for arg in bfn.decl.inputs {
1281                    self.hash_ty(arg);
1282                }
1283                std::mem::discriminant(&bfn.decl.output).hash(&mut self.s);
1284                match bfn.decl.output {
1285                    FnRetTy::DefaultReturn(_) => {},
1286                    FnRetTy::Return(ty) => {
1287                        self.hash_ty(ty);
1288                    },
1289                }
1290                bfn.decl.c_variadic.hash(&mut self.s);
1291            },
1292            TyKind::Tup(ty_list) => {
1293                for ty in *ty_list {
1294                    self.hash_ty(ty);
1295                }
1296            },
1297            TyKind::Path(qpath) => self.hash_qpath(qpath),
1298            TyKind::TraitObject(_, lifetime) => {
1299                self.hash_lifetime(lifetime);
1300            },
1301            TyKind::Typeof(anon_const) => {
1302                self.hash_body(anon_const.body);
1303            },
1304            TyKind::UnsafeBinder(binder) => {
1305                self.hash_ty(binder.inner_ty);
1306            },
1307            TyKind::Err(_)
1308            | TyKind::Infer(())
1309            | TyKind::Never
1310            | TyKind::InferDelegation(..)
1311            | TyKind::OpaqueDef(_)
1312            | TyKind::TraitAscription(_) => {},
1313        }
1314    }
1315
1316    pub fn hash_body(&mut self, body_id: BodyId) {
1317        // swap out TypeckResults when hashing a body
1318        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
1319        self.hash_expr(self.cx.tcx.hir().body(body_id).value);
1320        self.maybe_typeck_results = old_maybe_typeck_results;
1321    }
1322
1323    fn hash_const_arg(&mut self, const_arg: &ConstArg<'_>) {
1324        match &const_arg.kind {
1325            ConstArgKind::Path(path) => self.hash_qpath(path),
1326            ConstArgKind::Anon(anon) => self.hash_body(anon.body),
1327            ConstArgKind::Infer(..) => {},
1328        }
1329    }
1330
1331    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
1332        for arg in arg_list {
1333            match *arg {
1334                GenericArg::Lifetime(l) => self.hash_lifetime(l),
1335                GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()),
1336                GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()),
1337                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
1338            }
1339        }
1340    }
1341}
1342
1343pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 {
1344    let mut h = SpanlessHash::new(cx);
1345    h.hash_stmt(s);
1346    h.finish()
1347}
1348
1349pub fn is_bool(ty: &Ty<'_>) -> bool {
1350    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
1351        matches!(path.res, Res::PrimTy(PrimTy::Bool))
1352    } else {
1353        false
1354    }
1355}
1356
1357pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
1358    let mut h = SpanlessHash::new(cx);
1359    h.hash_expr(e);
1360    h.finish()
1361}
1362
1363fn eq_span_tokens(
1364    cx: &LateContext<'_>,
1365    left: impl SpanRange,
1366    right: impl SpanRange,
1367    pred: impl Fn(TokenKind) -> bool,
1368) -> bool {
1369    fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
1370        if let Some(lsrc) = left.get_source_range(cx)
1371            && let Some(lsrc) = lsrc.as_str()
1372            && let Some(rsrc) = right.get_source_range(cx)
1373            && let Some(rsrc) = rsrc.as_str()
1374        {
1375            let pred = |&(token, ..): &(TokenKind, _, _)| pred(token);
1376            let map = |(_, source, _)| source;
1377
1378            let ltok = tokenize_with_text(lsrc).filter(pred).map(map);
1379            let rtok = tokenize_with_text(rsrc).filter(pred).map(map);
1380            ltok.eq(rtok)
1381        } else {
1382            // Unable to access the source. Conservatively assume the blocks aren't equal.
1383            false
1384        }
1385    }
1386    f(cx, left.into_range(), right.into_range(), pred)
1387}