//! Utilities for manipulating and extracting information from `rustc_ast::ast`. //! //! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s. #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::{both, over}; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; use std::mem; pub mod ident_iter; pub use ident_iter::IdentIter; pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { use BinOpKind::*; matches!( kind, Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr ) } /// Checks if each element in the first slice is contained within the latter as per `eq_fn`. pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) } pub fn eq_id(l: Ident, r: Ident) -> bool { l.name == r.name } pub fn eq_pat(l: &Pat, r: &Pat) -> bool { use PatKind::*; match (&l.kind, &r.kind) { (Paren(l), _) => eq_pat(l, r), (_, Paren(r)) => eq_pat(l, r), (Wild, Wild) | (Rest, Rest) => true, (Lit(l), Lit(r)) => eq_expr(l, r), (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)), (Range(lf, lt, le), Range(rf, rt, re)) => { eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node) }, (Box(l), Box(r)) | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => { eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)) }, (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => { lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat) }, (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), _ => false, } } pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { match (l, r) { (RangeEnd::Excluded, RangeEnd::Excluded) => true, (RangeEnd::Included(l), RangeEnd::Included(r)) => { matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq) }, _ => false, } } pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool { l.is_placeholder == r.is_placeholder && eq_id(l.ident, r.ident) && eq_pat(&l.pat, &r.pat) && over(&l.attrs, &r.attrs, eq_attr) } pub fn eq_qself(l: &P, r: &P) -> bool { l.position == r.position && eq_ty(&l.ty, &r.ty) } pub fn eq_maybe_qself(l: &Option>, r: &Option>) -> bool { match (l, r) { (Some(l), Some(r)) => eq_qself(l, r), (None, None) => true, _ => false, } } pub fn eq_path(l: &Path, r: &Path) -> bool { over(&l.segments, &r.segments, eq_path_seg) } pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r)) } pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { match (l, r) { (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg), (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => { over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) }, _ => false, } } pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool { match (l, r) { (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r), (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r), _ => false, } } pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { match (l, r) { (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident), (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r), (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value), _ => false, } } pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { both(l, r, |l, r| eq_expr(l, r)) } pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { match (l, r) { (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true, _ => false, } } pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, eq_attr) { return false; } match (&l.kind, &r.kind) { (Paren(l), _) => eq_expr(l, r), (_, Paren(r)) => eq_expr(l, r), (Err, Err) => true, (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), ( MethodCall(box ast::MethodCall { seg: ls, receiver: lr, args: la, .. }), MethodCall(box ast::MethodCall { seg: rs, receiver: rr, args: ra, .. }), ) => eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)), (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) }, (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll, rl) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), (Continue(ll), Continue(rl)) => eq_label(ll, rl), (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2), (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), ( Closure(box ast::Closure { binder: lb, capture_clause: lc, asyncness: la, movability: lm, fn_decl: lf, body: le, .. }), Closure(box ast::Closure { binder: rb, capture_clause: rc, asyncness: ra, movability: rm, fn_decl: rf, body: re, .. }), ) => { eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re) }, (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb), (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lse), Struct(rse)) => { eq_maybe_qself(&lse.qself, &rse.qself) && eq_path(&lse.path, &rse.path) && eq_struct_rest(&lse.rest, &rse.rest) && unordered_over(&lse.fields, &rse.fields, eq_field) }, _ => false, } } pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { l.is_placeholder == r.is_placeholder && eq_id(l.ident, r.ident) && eq_expr(&l.expr, &r.expr) && over(&l.attrs, &r.attrs, eq_attr) } pub fn eq_arm(l: &Arm, r: &Arm) -> bool { l.is_placeholder == r.is_placeholder && eq_pat(&l.pat, &r.pat) && eq_expr(&l.body, &r.body) && eq_expr_opt(&l.guard, &r.guard) && over(&l.attrs, &r.attrs, eq_attr) } pub fn eq_label(l: &Option