Skip to main content

clippy_utils/ast_utils/
mod.rs

1//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
2//!
3//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
4
5#![allow(clippy::enum_glob_use, clippy::wildcard_imports)]
6
7use crate::{both, over};
8use rustc_ast::attr::data_structures::CfgEntry;
9use rustc_ast::{self as ast, HasAttrs, *};
10use rustc_span::sym;
11use rustc_span::symbol::Ident;
12use std::mem;
13
14pub mod ident_iter;
15pub use ident_iter::IdentIter;
16
17pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
18    use BinOpKind::*;
19    matches!(
20        kind,
21        Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
22    )
23}
24
25/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
26pub fn unordered_over<X, Y>(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool {
27    left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
28}
29
30pub fn eq_id(l: Ident, r: Ident) -> bool {
31    l.name == r.name
32}
33
34pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
35    use PatKind::*;
36    match (&l.kind, &r.kind) {
37        (Missing, _) | (_, Missing) => unreachable!(),
38        (Paren(l), _) => eq_pat(l, r),
39        (_, Paren(r)) => eq_pat(l, r),
40        (Wild, Wild) | (Rest, Rest) => true,
41        (Expr(l), Expr(r)) => eq_expr(l, r),
42        (Ident(b1, i1, s1), Ident(b2, i2, s2)) => {
43            b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat)
44        },
45        (Range(lf, lt, le), Range(rf, rt, re)) => {
46            eq_expr_opt(lf.as_deref(), rf.as_deref())
47                && eq_expr_opt(lt.as_deref(), rt.as_deref())
48                && eq_range_end(le.node, re.node)
49        },
50        (Box(l), Box(r)) => eq_pat(l, r),
51        (Ref(l, l_pin, l_mut), Ref(r, r_pin, r_mut)) => l_pin == r_pin && l_mut == r_mut && eq_pat(l, r),
52        (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, eq_pat),
53        (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
54        (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
55            eq_maybe_qself(lqself.as_deref(), rqself.as_deref()) && eq_path(lp, rp) && over(lfs, rfs, eq_pat)
56        },
57        (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
58            lr == rr
59                && eq_maybe_qself(lqself.as_deref(), rqself.as_deref())
60                && eq_path(lp, rp)
61                && unordered_over(lfs, rfs, eq_field_pat)
62        },
63        (Or(ls), Or(rs)) => unordered_over(ls, rs, eq_pat),
64        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
65        _ => false,
66    }
67}
68
69fn eq_range_end(l: RangeEnd, r: RangeEnd) -> bool {
70    match (l, r) {
71        (RangeEnd::Excluded, RangeEnd::Excluded) => true,
72        (RangeEnd::Included(l), RangeEnd::Included(r)) => {
73            matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
74        },
75        _ => false,
76    }
77}
78
79pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
80    l.is_placeholder == r.is_placeholder
81        && eq_id(l.ident, r.ident)
82        && eq_pat(&l.pat, &r.pat)
83        && over(&l.attrs, &r.attrs, eq_attr)
84}
85
86fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
87    l.position == r.position && eq_ty(&l.ty, &r.ty)
88}
89
90pub fn eq_maybe_qself(l: Option<&QSelf>, r: Option<&QSelf>) -> bool {
91    match (l, r) {
92        (Some(l), Some(r)) => eq_qself(l, r),
93        (None, None) => true,
94        _ => false,
95    }
96}
97
98pub fn eq_path(l: &Path, r: &Path) -> bool {
99    over(&l.segments, &r.segments, eq_path_seg)
100}
101
102fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
103    eq_id(l.ident, r.ident) && both(l.args.as_ref(), r.args.as_ref(), |l, r| eq_generic_args(l, r))
104}
105
106fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
107    match (l, r) {
108        (AngleBracketed(l), AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg),
109        (Parenthesized(l), Parenthesized(r)) => {
110            over(&l.inputs, &r.inputs, eq_param) && eq_fn_ret_ty(&l.output, &r.output)
111        },
112        _ => false,
113    }
114}
115
116fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
117    match (l, r) {
118        (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
119        (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_item_constraint(l, r),
120        _ => false,
121    }
122}
123
124fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
125    match (l, r) {
126        (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
127        (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
128        (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
129        _ => false,
130    }
131}
132
133fn eq_expr_opt(l: Option<&Expr>, r: Option<&Expr>) -> bool {
134    both(l, r, eq_expr)
135}
136
137fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
138    match (l, r) {
139        (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb),
140        (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true,
141        _ => false,
142    }
143}
144
145#[expect(clippy::too_many_lines, reason = "big match statement")]
146fn eq_expr(l: &Expr, r: &Expr) -> bool {
147    use ExprKind::*;
148    if !over(&l.attrs, &r.attrs, eq_attr) {
149        return false;
150    }
151    match (&l.kind, &r.kind) {
152        (Paren(l), _) => eq_expr(l, r),
153        (_, Paren(r)) => eq_expr(l, r),
154        (Err(_), Err(_)) => true,
155        (Dummy, _) | (_, Dummy) => unreachable!("comparing `ExprKind::Dummy`"),
156        (Try(l), Try(r)) | (Await(l, _), Await(r, _)) => eq_expr(l, r),
157        (Array(l), Array(r)) => over(l, r, |l, r| eq_expr(l, r)),
158        (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
159        (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
160        (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
161        (
162            MethodCall(box ast::MethodCall {
163                seg: ls,
164                receiver: lr,
165                args: la,
166                ..
167            }),
168            MethodCall(box ast::MethodCall {
169                seg: rs,
170                receiver: rr,
171                args: ra,
172                ..
173            }),
174        ) => eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)),
175        (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
176        (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
177        (Lit(l), Lit(r)) => l == r,
178        (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
179        (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re),
180        (If(lc, lt, le), If(rc, rt, re)) => {
181            eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref())
182        },
183        (While(lc, lt, ll), While(rc, rt, rl)) => {
184            eq_label(ll.as_ref(), rl.as_ref()) && eq_expr(lc, rc) && eq_block(lt, rt)
185        },
186        (ForLoop(lf), ForLoop(rf)) => {
187            eq_label(lf.label.as_ref(), rf.label.as_ref())
188                && eq_pat(&lf.pat, &rf.pat)
189                && eq_expr(&lf.iter, &rf.iter)
190                && eq_block(&lf.body, &rf.body)
191                && lf.kind == rf.kind
192        },
193        (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt),
194        (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb),
195        (TryBlock(lb, lt), TryBlock(rb, rt)) => eq_block(lb, rb) && both(lt.as_deref(), rt.as_deref(), eq_ty),
196        (Yield(l), Yield(r)) => eq_expr_opt(l.expr().map(Box::as_ref), r.expr().map(Box::as_ref)) && l.same_kind(r),
197        (Ret(l), Ret(r)) => eq_expr_opt(l.as_deref(), r.as_deref()),
198        (Break(ll, le), Break(rl, re)) => {
199            eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_deref(), re.as_deref())
200        },
201        (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()),
202        (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => {
203            eq_expr(l1, r1) && eq_expr(l2, r2)
204        },
205        (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
206        (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
207        (Match(ls, la, lkind), Match(rs, ra, rkind)) => (lkind == rkind) && eq_expr(ls, rs) && over(la, ra, eq_arm),
208        (
209            Closure(box ast::Closure {
210                binder: lb,
211                capture_clause: lc,
212                coroutine_marker: lcm,
213                movability: lm,
214                fn_decl: lf,
215                body: le,
216                ..
217            }),
218            Closure(box ast::Closure {
219                binder: rb,
220                capture_clause: rc,
221                coroutine_marker: rcm,
222                movability: rm,
223                fn_decl: rf,
224                body: re,
225                ..
226            }),
227        ) => {
228            eq_closure_binder(lb, rb)
229                && lc == rc
230                && eq_opt_coroutine_marker(*lcm, *rcm)
231                && lm == rm
232                && eq_fn_decl(lf, rf)
233                && eq_expr(le, re)
234        },
235        (Gen(lc, lb, lk, _), Gen(rc, rb, rk, _)) => lc == rc && eq_block(lb, rb) && lk == rk,
236        (Range(lf, lt, ll), Range(rf, rt, rl)) => {
237            ll == rl && eq_expr_opt(lf.as_deref(), rf.as_deref()) && eq_expr_opt(lt.as_deref(), rt.as_deref())
238        },
239        (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
240        (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
241        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
242        (Struct(lse), Struct(rse)) => {
243            eq_maybe_qself(lse.qself.as_deref(), rse.qself.as_deref())
244                && eq_path(&lse.path, &rse.path)
245                && eq_struct_rest(&lse.rest, &rse.rest)
246                && unordered_over(&lse.fields, &rse.fields, eq_field)
247        },
248        _ => false,
249    }
250}
251
252fn eq_field(l: &ExprField, r: &ExprField) -> bool {
253    l.is_placeholder == r.is_placeholder
254        && eq_id(l.ident, r.ident)
255        && eq_expr(&l.expr, &r.expr)
256        && over(&l.attrs, &r.attrs, eq_attr)
257}
258
259fn eq_arm(l: &Arm, r: &Arm) -> bool {
260    l.is_placeholder == r.is_placeholder
261        && eq_pat(&l.pat, &r.pat)
262        && eq_expr_opt(l.body.as_deref(), r.body.as_deref())
263        && eq_expr_opt(l.guard.as_deref().map(|g| &g.cond), r.guard.as_deref().map(|g| &g.cond))
264        && over(&l.attrs, &r.attrs, eq_attr)
265}
266
267fn eq_label(l: Option<&Label>, r: Option<&Label>) -> bool {
268    both(l, r, |l, r| eq_id(l.ident, r.ident))
269}
270
271fn eq_block(l: &Block, r: &Block) -> bool {
272    l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt)
273}
274
275fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
276    use StmtKind::*;
277    match (&l.kind, &r.kind) {
278        (Let(l), Let(r)) => {
279            eq_pat(&l.pat, &r.pat)
280                && both(l.ty.as_ref(), r.ty.as_ref(), |l, r| eq_ty(l, r))
281                && eq_local_kind(&l.kind, &r.kind)
282                && over(&l.attrs, &r.attrs, eq_attr)
283        },
284        (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
285        (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
286        (Empty, Empty) => true,
287        (MacCall(l), MacCall(r)) => {
288            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr)
289        },
290        _ => false,
291    }
292}
293
294fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
295    use LocalKind::*;
296    match (l, r) {
297        (Decl, Decl) => true,
298        (Init(l), Init(r)) => eq_expr(l, r),
299        (InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re),
300        _ => false,
301    }
302}
303
304fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
305    over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
306}
307
308#[expect(clippy::too_many_lines, reason = "big match statement")]
309fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
310    use ItemKind::*;
311    match (l, r) {
312        (ExternCrate(ls, li), ExternCrate(rs, ri)) => ls == rs && eq_id(*li, *ri),
313        (Use(l), Use(r)) => eq_use_tree(l, r),
314        (
315            Static(box StaticItem {
316                ident: li,
317                ty: lt,
318                mutability: lm,
319                expr: le,
320                safety: ls,
321                define_opaque: _,
322                eii_impl: _,
323            }),
324            Static(box StaticItem {
325                ident: ri,
326                ty: rt,
327                mutability: rm,
328                expr: re,
329                safety: rs,
330                define_opaque: _,
331                eii_impl: _,
332            }),
333        ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()),
334        (
335            Const(box ConstItem {
336                defaultness: ld,
337                ident: li,
338                generics: lg,
339                ty: lt,
340                body: lb,
341                kind: lk,
342                define_opaque: _,
343            }),
344            Const(box ConstItem {
345                defaultness: rd,
346                ident: ri,
347                generics: rg,
348                ty: rt,
349
350                body: rb,
351                kind: rk,
352                define_opaque: _,
353            }),
354        ) => {
355            eq_defaultness(*ld, *rd)
356                && eq_id(*li, *ri)
357                && eq_generics(lg, rg)
358                && eq_ty(lt, rt)
359                && lk == rk
360                && both(lb.as_deref(), rb.as_deref(), eq_expr)
361        },
362        (
363            Fn(box ast::Fn {
364                defaultness: ld,
365                sig: lf,
366                ident: li,
367                generics: lg,
368                contract: lc,
369                body: lb,
370                define_opaque: _,
371                eii_impl: _,
372            }),
373            Fn(box ast::Fn {
374                defaultness: rd,
375                sig: rf,
376                ident: ri,
377                generics: rg,
378                contract: rc,
379                body: rb,
380                define_opaque: _,
381                eii_impl: _,
382            }),
383        ) => {
384            eq_defaultness(*ld, *rd)
385                && eq_fn_sig(lf, rf)
386                && eq_id(*li, *ri)
387                && eq_generics(lg, rg)
388                && eq_opt_fn_contract(lc, rc)
389                && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r))
390        },
391        (Mod(ls, li, lmk), Mod(rs, ri, rmk)) => {
392            ls == rs
393                && eq_id(*li, *ri)
394                && match (lmk, rmk) {
395                    (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
396                        linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
397                    },
398                    (ModKind::Unloaded, ModKind::Unloaded) => true,
399                    _ => false,
400                }
401        },
402        (ForeignMod(l), ForeignMod(r)) => {
403            both(l.abi.as_ref(), r.abi.as_ref(), eq_str_lit)
404                && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
405        },
406        (
407            TyAlias(box ast::TyAlias {
408                defaultness: ld,
409                generics: lg,
410                bounds: lb,
411                ty: lt,
412                ..
413            }),
414            TyAlias(box ast::TyAlias {
415                defaultness: rd,
416                generics: rg,
417                bounds: rb,
418                ty: rt,
419                ..
420            }),
421        ) => {
422            eq_defaultness(*ld, *rd)
423                && eq_generics(lg, rg)
424                && over(lb, rb, eq_generic_bound)
425                && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r))
426        },
427        (Enum(li, lg, le), Enum(ri, rg, re)) => {
428            eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant)
429        },
430        (Struct(li, lg, lv), Struct(ri, rg, rv)) | (Union(li, lg, lv), Union(ri, rg, rv)) => {
431            eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv)
432        },
433        (
434            Trait(box ast::Trait {
435                impl_restriction: liprt,
436                constness: lc,
437                is_auto: la,
438                safety: lu,
439                ident: li,
440                generics: lg,
441                bounds: lb,
442                items: lis,
443            }),
444            Trait(box ast::Trait {
445                impl_restriction: riprt,
446                constness: rc,
447                is_auto: ra,
448                safety: ru,
449                ident: ri,
450                generics: rg,
451                bounds: rb,
452                items: ris,
453            }),
454        ) => {
455            eq_impl_restriction(liprt, riprt)
456                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
457                && la == ra
458                && matches!(lu, Safety::Default) == matches!(ru, Safety::Default)
459                && eq_id(*li, *ri)
460                && eq_generics(lg, rg)
461                && over(lb, rb, eq_generic_bound)
462                && over(lis, ris, |l, r| eq_item(l, r, eq_assoc_item_kind))
463        },
464        (
465            TraitAlias(box ast::TraitAlias {
466                ident: li,
467                generics: lg,
468                bounds: lb,
469                constness: lc,
470            }),
471            TraitAlias(box ast::TraitAlias {
472                ident: ri,
473                generics: rg,
474                bounds: rb,
475                constness: rc,
476            }),
477        ) => {
478            matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
479                && eq_id(*li, *ri)
480                && eq_generics(lg, rg)
481                && over(lb, rb, eq_generic_bound)
482        },
483        (
484            Impl(ast::Impl {
485                generics: lg,
486                of_trait: lot,
487                self_ty: lst,
488                items: li,
489                constness: lc,
490            }),
491            Impl(ast::Impl {
492                generics: rg,
493                of_trait: rot,
494                self_ty: rst,
495                items: ri,
496                constness: rc,
497            }),
498        ) => {
499            eq_generics(lg, rg)
500                && both(lot.as_deref(), rot.as_deref(), |l, r| {
501                    matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default)
502                        && matches!(l.polarity, ImplPolarity::Positive) == matches!(r.polarity, ImplPolarity::Positive)
503                        && eq_defaultness(l.defaultness, r.defaultness)
504                        && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
505                        && eq_path(&l.trait_ref.path, &r.trait_ref.path)
506                })
507                && eq_ty(lst, rst)
508                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
509        },
510        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
511        (MacroDef(li, ld), MacroDef(ri, rd)) => {
512            eq_id(*li, *ri) && ld.macro_rules == rd.macro_rules && eq_delim_args(&ld.body, &rd.body)
513        },
514        _ => false,
515    }
516}
517
518fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
519    use ForeignItemKind::*;
520    match (l, r) {
521        (
522            Static(box StaticItem {
523                ident: li,
524                ty: lt,
525                mutability: lm,
526                expr: le,
527                safety: ls,
528                define_opaque: _,
529                eii_impl: _,
530            }),
531            Static(box StaticItem {
532                ident: ri,
533                ty: rt,
534                mutability: rm,
535                expr: re,
536                safety: rs,
537                define_opaque: _,
538                eii_impl: _,
539            }),
540        ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_deref(), re.as_deref()) && ls == rs,
541        (
542            Fn(box ast::Fn {
543                defaultness: ld,
544                sig: lf,
545                ident: li,
546                generics: lg,
547                contract: lc,
548                body: lb,
549                define_opaque: _,
550                eii_impl: _,
551            }),
552            Fn(box ast::Fn {
553                defaultness: rd,
554                sig: rf,
555                ident: ri,
556                generics: rg,
557                contract: rc,
558                body: rb,
559                define_opaque: _,
560                eii_impl: _,
561            }),
562        ) => {
563            eq_defaultness(*ld, *rd)
564                && eq_fn_sig(lf, rf)
565                && eq_id(*li, *ri)
566                && eq_generics(lg, rg)
567                && eq_opt_fn_contract(lc, rc)
568                && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r))
569        },
570        (
571            TyAlias(box ast::TyAlias {
572                defaultness: ld,
573                ident: li,
574                generics: lg,
575                after_where_clause: lw,
576                bounds: lb,
577                ty: lt,
578            }),
579            TyAlias(box ast::TyAlias {
580                defaultness: rd,
581                ident: ri,
582                generics: rg,
583                after_where_clause: rw,
584                bounds: rb,
585                ty: rt,
586            }),
587        ) => {
588            eq_defaultness(*ld, *rd)
589                && eq_id(*li, *ri)
590                && eq_generics(lg, rg)
591                && over(&lw.predicates, &rw.predicates, eq_where_predicate)
592                && over(lb, rb, eq_generic_bound)
593                && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r))
594        },
595        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
596        _ => false,
597    }
598}
599
600fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
601    use AssocItemKind::*;
602    match (l, r) {
603        (
604            Const(box ConstItem {
605                defaultness: ld,
606                ident: li,
607                generics: lg,
608                ty: lt,
609                body: lb,
610                kind: lk,
611                define_opaque: _,
612            }),
613            Const(box ConstItem {
614                defaultness: rd,
615                ident: ri,
616                generics: rg,
617                ty: rt,
618                body: rb,
619                kind: rk,
620                define_opaque: _,
621            }),
622        ) => {
623            eq_defaultness(*ld, *rd)
624                && eq_id(*li, *ri)
625                && eq_generics(lg, rg)
626                && eq_ty(lt, rt)
627                && lk == rk
628                && both(lb.as_deref(), rb.as_deref(), eq_expr)
629        },
630        (
631            Fn(box ast::Fn {
632                defaultness: ld,
633                sig: lf,
634                ident: li,
635                generics: lg,
636                contract: lc,
637                body: lb,
638                define_opaque: _,
639                eii_impl: _,
640            }),
641            Fn(box ast::Fn {
642                defaultness: rd,
643                sig: rf,
644                ident: ri,
645                generics: rg,
646                contract: rc,
647                body: rb,
648                define_opaque: _,
649                eii_impl: _,
650            }),
651        ) => {
652            eq_defaultness(*ld, *rd)
653                && eq_fn_sig(lf, rf)
654                && eq_id(*li, *ri)
655                && eq_generics(lg, rg)
656                && eq_opt_fn_contract(lc, rc)
657                && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r))
658        },
659        (
660            Type(box TyAlias {
661                defaultness: ld,
662                ident: li,
663                generics: lg,
664                after_where_clause: lw,
665                bounds: lb,
666                ty: lt,
667            }),
668            Type(box TyAlias {
669                defaultness: rd,
670                ident: ri,
671                generics: rg,
672                after_where_clause: rw,
673                bounds: rb,
674                ty: rt,
675            }),
676        ) => {
677            eq_defaultness(*ld, *rd)
678                && eq_id(*li, *ri)
679                && eq_generics(lg, rg)
680                && over(&lw.predicates, &rw.predicates, eq_where_predicate)
681                && over(lb, rb, eq_generic_bound)
682                && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r))
683        },
684        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
685        _ => false,
686    }
687}
688
689fn eq_variant(l: &Variant, r: &Variant) -> bool {
690    l.is_placeholder == r.is_placeholder
691        && over(&l.attrs, &r.attrs, eq_attr)
692        && eq_vis(&l.vis, &r.vis)
693        && eq_id(l.ident, r.ident)
694        && eq_variant_data(&l.data, &r.data)
695        && both(l.disr_expr.as_ref(), r.disr_expr.as_ref(), |l, r| {
696            eq_expr(&l.value, &r.value)
697        })
698}
699
700fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
701    use VariantData::*;
702    match (l, r) {
703        (Unit(_), Unit(_)) => true,
704        (Struct { fields: l, .. }, Struct { fields: r, .. }) | (Tuple(l, _), Tuple(r, _)) => {
705            over(l, r, eq_struct_field)
706        },
707        _ => false,
708    }
709}
710
711fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
712    l.is_placeholder == r.is_placeholder
713        && over(&l.attrs, &r.attrs, eq_attr)
714        && eq_vis(&l.vis, &r.vis)
715        && eq_mut_restriction(l.mut_restriction(), r.mut_restriction())
716        && both(l.ident.as_ref(), r.ident.as_ref(), |l, r| eq_id(*l, *r))
717        && eq_ty(&l.ty, &r.ty)
718}
719
720fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
721    eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
722}
723
724fn eq_opt_coroutine_marker(l: Option<CoroutineMarker>, r: Option<CoroutineMarker>) -> bool {
725    match (l, r) {
726        (Some(lcm), Some(rcm)) => lcm.kind == rcm.kind,
727        (None, None) => true,
728        (Some(_), None) | (None, Some(_)) => false,
729    }
730}
731
732fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
733    matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default)
734        && eq_opt_coroutine_marker(l.coroutine_marker, r.coroutine_marker)
735        && matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
736        && eq_ext(&l.ext, &r.ext)
737}
738
739#[expect(clippy::ref_option, reason = "This is the type how it is stored in the AST")]
740fn eq_opt_fn_contract(l: &Option<Box<FnContract>>, r: &Option<Box<FnContract>>) -> bool {
741    match (l, r) {
742        (Some(l), Some(r)) => {
743            eq_expr_opt(l.requires.as_deref(), r.requires.as_deref())
744                && eq_expr_opt(l.ensures.as_deref(), r.ensures.as_deref())
745        },
746        (None, None) => true,
747        (Some(_), None) | (None, Some(_)) => false,
748    }
749}
750
751fn eq_generics(l: &Generics, r: &Generics) -> bool {
752    over(&l.params, &r.params, eq_generic_param)
753        && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
754            eq_where_predicate(l, r)
755        })
756}
757
758fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
759    use WherePredicateKind::*;
760    over(&l.attrs, &r.attrs, eq_attr)
761        && match (&l.kind, &r.kind) {
762            (BoundPredicate(l), BoundPredicate(r)) => {
763                over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
764                    eq_generic_param(l, r)
765                }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
766                    && over(&l.bounds, &r.bounds, eq_generic_bound)
767            },
768            (RegionPredicate(l), RegionPredicate(r)) => {
769                eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound)
770            },
771            _ => false,
772        }
773}
774
775fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
776    eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
777}
778
779fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
780    eq_expr(&l.value, &r.value)
781}
782
783fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
784    use UseTreeKind::*;
785    match (l, r) {
786        (Glob(_), Glob(_)) => true,
787        (Simple(l), Simple(r)) => both(l.as_ref(), r.as_ref(), |l, r| eq_id(*l, *r)),
788        (Nested { items: l, .. }, Nested { items: r, .. }) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
789        _ => false,
790    }
791}
792
793fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
794    matches!(
795        (l, r),
796        (Defaultness::Implicit, Defaultness::Implicit)
797            | (Defaultness::Default(_), Defaultness::Default(_))
798            | (Defaultness::Final(_), Defaultness::Final(_))
799    )
800}
801
802fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
803    use VisibilityKind::*;
804    match (&l.kind, &r.kind) {
805        (Public, Public) | (Inherited, Inherited) => true,
806        (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
807        _ => false,
808    }
809}
810
811fn eq_impl_restriction(l: &ImplRestriction, r: &ImplRestriction) -> bool {
812    eq_restriction_kind(&l.kind, &r.kind)
813}
814
815pub fn eq_mut_restriction(l: &MutRestriction, r: &MutRestriction) -> bool {
816    eq_restriction_kind(&l.kind, &r.kind)
817}
818
819fn eq_restriction_kind(l: &RestrictionKind, r: &RestrictionKind) -> bool {
820    match (l, r) {
821        (RestrictionKind::Unrestricted, RestrictionKind::Unrestricted) => true,
822        (
823            RestrictionKind::Restricted {
824                path: l_path,
825                shorthand: l_short,
826                id: _,
827            },
828            RestrictionKind::Restricted {
829                path: r_path,
830                shorthand: r_short,
831                id: _,
832            },
833        ) => l_short == r_short && eq_path(l_path, r_path),
834        _ => false,
835    }
836}
837
838fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
839    eq_fn_ret_ty(&l.output, &r.output) && over(&l.inputs, &r.inputs, eq_param)
840}
841
842fn eq_param(l: &Param, r: &Param) -> bool {
843    l.is_placeholder == r.is_placeholder
844        && eq_pat(&l.pat, &r.pat)
845        && eq_ty(&l.ty, &r.ty)
846        && over(&l.attrs, &r.attrs, eq_attr)
847}
848
849fn eq_closure_binder(l: &ClosureBinder, r: &ClosureBinder) -> bool {
850    match (l, r) {
851        (ClosureBinder::NotPresent, ClosureBinder::NotPresent) => true,
852        (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => {
853            lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r))
854        },
855        _ => false,
856    }
857}
858
859fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
860    match (l, r) {
861        (FnRetTy::Default(_), FnRetTy::Default(_)) => true,
862        (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
863        _ => false,
864    }
865}
866
867fn eq_ty(l: &Ty, r: &Ty) -> bool {
868    use TyKind::*;
869    match (&l.kind, &r.kind) {
870        (Paren(l), _) => eq_ty(l, r),
871        (_, Paren(r)) => eq_ty(l, r),
872        (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err(_), Err(_)) | (CVarArgs, CVarArgs) => {
873            true
874        },
875        (Slice(l), Slice(r)) => eq_ty(l, r),
876        (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
877        (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
878        (Ref(ll, l), Ref(rl, r)) => {
879            both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
880        },
881        (PinnedRef(ll, l), PinnedRef(rl, r)) => {
882            both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
883        },
884        (FnPtr(l), FnPtr(r)) => {
885            l.safety == r.safety
886                && eq_ext(&l.ext, &r.ext)
887                && over(&l.generic_params, &r.generic_params, eq_generic_param)
888                && eq_fn_decl(&l.decl, &r.decl)
889        },
890        (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
891        (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
892        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
893        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
894        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
895        _ => false,
896    }
897}
898
899fn eq_ext(l: &Extern, r: &Extern) -> bool {
900    use Extern::*;
901    match (l, r) {
902        (None, None) | (Implicit(_), Implicit(_)) => true,
903        (Explicit(l, _), Explicit(r, _)) => eq_str_lit(l, r),
904        _ => false,
905    }
906}
907
908fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
909    l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
910}
911
912fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
913    l.modifiers == r.modifiers
914        && eq_path(&l.trait_ref.path, &r.trait_ref.path)
915        && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
916            eq_generic_param(l, r)
917        })
918}
919
920fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
921    use GenericParamKind::*;
922    l.is_placeholder == r.is_placeholder
923        && eq_id(l.ident, r.ident)
924        && over(&l.bounds, &r.bounds, eq_generic_bound)
925        && match (&l.kind, &r.kind) {
926            (Lifetime, Lifetime) => true,
927            (Type { default: l }, Type { default: r }) => both(l.as_ref(), r.as_ref(), |l, r| eq_ty(l, r)),
928            (
929                Const {
930                    ty: lt,
931                    default: ld,
932                    span: _,
933                },
934                Const {
935                    ty: rt,
936                    default: rd,
937                    span: _,
938                },
939            ) => eq_ty(lt, rt) && both(ld.as_ref(), rd.as_ref(), eq_anon_const),
940            _ => false,
941        }
942        && over(&l.attrs, &r.attrs, eq_attr)
943}
944
945fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
946    use GenericBound::*;
947    match (l, r) {
948        (Trait(ptr1), Trait(ptr2)) => eq_poly_ref_trait(ptr1, ptr2),
949        (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
950        _ => false,
951    }
952}
953
954fn eq_term(l: &Term, r: &Term) -> bool {
955    match (l, r) {
956        (Term::Ty(l), Term::Ty(r)) => eq_ty(l, r),
957        (Term::Const(l), Term::Const(r)) => eq_anon_const(l, r),
958        _ => false,
959    }
960}
961
962fn eq_assoc_item_constraint(l: &AssocItemConstraint, r: &AssocItemConstraint) -> bool {
963    use AssocItemConstraintKind::*;
964    eq_id(l.ident, r.ident)
965        && match (&l.kind, &r.kind) {
966            (Equality { term: l }, Equality { term: r }) => eq_term(l, r),
967            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound),
968            _ => false,
969        }
970}
971
972fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
973    eq_path(&l.path, &r.path) && eq_delim_args(&l.args, &r.args)
974}
975
976fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
977    use AttrKind::*;
978    l.style == r.style
979        && match (&l.kind, &r.kind) {
980            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
981            (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args),
982            (Synthetic(..), _) | (_, Synthetic(..)) => unreachable!(),
983            _ => false,
984        }
985}
986
987fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool {
988    use AttrArgs::*;
989    match (l, r) {
990        (Empty, Empty) => true,
991        (Delimited(la), Delimited(ra)) => eq_delim_args(la, ra),
992        (Eq { eq_span: _, expr: le }, Eq { eq_span: _, expr: re }) => eq_expr(le, re),
993        _ => false,
994    }
995}
996
997fn eq_delim_args(l: &DelimArgs, r: &DelimArgs) -> bool {
998    l.delim == r.delim
999        && l.tokens.len() == r.tokens.len()
1000        && l.tokens.iter().zip(r.tokens.iter()).all(|(a, b)| a.eq_unspanned(b))
1001}
1002
1003/// Checks whether `item` is gated on `#[cfg(test)]`.
1004pub fn is_cfg_test(item: &impl HasAttrs) -> bool {
1005    item.attrs().iter().any(|attr| {
1006        if attr.has_name(sym::cfg)
1007            && let Some(item_list) = attr.meta_item_list()
1008            && item_list.iter().any(|item| item.has_name(sym::test))
1009        {
1010            true
1011        } else if let AttrKind::Synthetic(synthetic) = &attr.kind
1012            && let SyntheticAttr::CfgTrace(cfg) = &**synthetic
1013        {
1014            requires_test_cfg(cfg)
1015        } else {
1016            false
1017        }
1018    })
1019}
1020
1021fn requires_test_cfg(cfg: &CfgEntry) -> bool {
1022    match cfg {
1023        CfgEntry::NameValue { name: sym::test, .. } => true,
1024        CfgEntry::All(subs, _) => subs.iter().any(requires_test_cfg),
1025        _ => false,
1026    }
1027}