Skip to main content

php_ast/owned/
mod.rs

1//! Owned (lifetime-free) versions of all AST types.
2//!
3//! Arena-allocated types (`Program<'arena, 'src>`, etc.) cannot be stored in a
4//! struct alongside the `bumpalo::Bump` arena that owns them — the borrow checker
5//! rejects the self-referential pattern. These owned mirrors solve that: they use
6//! `Box<str>` instead of `&str`, `Box<[T]>` instead of `ArenaVec<'arena, T>`, and
7//! `Box<T>` instead of `&'arena T`. The result is `'static` — no lifetime
8//! parameters, storeable in a `HashMap`, sendable across threads.
9//!
10//! Serialization is byte-for-byte identical to the arena types so existing JSON
11//! snapshots remain valid.
12//!
13//! # Entry points
14//!
15//! Call [`php_rs_parser::parse()`] — it returns a [`php_rs_parser::ParseResult`]
16//! whose `.program` field is an owned [`Program`]. To convert an
17//! arena-allocated [`Program`](crate::ast::Program) directly, use
18//! [`to_owned_program`].
19
20pub mod fold;
21pub mod visitor;
22
23pub use fold::{
24    fold_owned_arg, fold_owned_attribute, fold_owned_catch_clause, fold_owned_class_member,
25    fold_owned_closure_use_var, fold_owned_enum_member, fold_owned_expr, fold_owned_match_arm,
26    fold_owned_name, fold_owned_param, fold_owned_program, fold_owned_property_hook,
27    fold_owned_stmt, fold_owned_type_hint, FoldOwned,
28};
29pub use visitor::{
30    walk_owned_arg, walk_owned_attribute, walk_owned_catch_clause, walk_owned_class_member,
31    walk_owned_comments, walk_owned_enum_member, walk_owned_expr, walk_owned_match_arm,
32    walk_owned_name, walk_owned_param, walk_owned_program, walk_owned_property_hook,
33    walk_owned_stmt, walk_owned_trait_use, walk_owned_type_hint, OwnedScope, OwnedScopeVisitor,
34    OwnedScopeWalker, OwnedVisitor,
35};
36
37use serde::Serialize;
38
39use crate::ast as arena_ast;
40use crate::ast::{
41    AssignOp, BinaryOp, BuiltinType, CastKind, ClassModifiers, CommentKind, IncludeKind,
42    MagicConstKind, NameKind, PropertyHookKind, UnaryPostfixOp, UnaryPrefixOp, UseKind, Visibility,
43};
44use crate::Span;
45
46// ---------------------------------------------------------------------------
47// Serde helpers
48// ---------------------------------------------------------------------------
49
50fn is_false(b: &bool) -> bool {
51    !*b
52}
53
54fn slice_is_empty<T>(v: &[T]) -> bool {
55    v.is_empty()
56}
57
58// ---------------------------------------------------------------------------
59// Ident
60//
61// `Ident<'src>` serialises as `null` for the error state (empty string) and as
62// a JSON string for real names. `Option<Box<str>>` produces identical output:
63// `None` → null, `Some(s)` → "s". We use a type alias to keep field types
64// readable.
65// ---------------------------------------------------------------------------
66
67/// Owned equivalent of [`Ident`](crate::ast::Ident).
68/// `None` represents the error state (no identifier was parsed).
69pub type Ident = Option<Box<str>>;
70
71// ---------------------------------------------------------------------------
72// Name
73//
74// `Name<'arena, 'src>` has three variants (Simple, Complex, Error) and a
75// custom Serialize that always emits `{"parts":[...],"kind":"...","span":{…}}`.
76// The owned version flattens them into a single struct and derives Serialize —
77// the JSON output is identical.
78// ---------------------------------------------------------------------------
79
80#[derive(Debug, Clone, Serialize)]
81pub struct Name {
82    pub parts: Box<[Box<str>]>,
83    pub kind: NameKind,
84    pub span: Span,
85}
86
87// ---------------------------------------------------------------------------
88// Comment
89// ---------------------------------------------------------------------------
90
91#[derive(Debug, Clone, Serialize)]
92pub struct Comment {
93    pub kind: CommentKind,
94    pub text: Box<str>,
95    pub span: Span,
96}
97
98// ---------------------------------------------------------------------------
99// TypeHint / TypeHintKind
100//
101// `TypeHintKind::Keyword` has a custom Serialize that emits the same JSON as
102// `Named`. We replicate that here.
103// ---------------------------------------------------------------------------
104
105#[derive(Debug, Clone, Serialize)]
106pub struct TypeHint {
107    pub kind: TypeHintKind,
108    pub span: Span,
109}
110
111#[derive(Debug, Clone)]
112pub enum TypeHintKind {
113    Named(Name),
114    /// Serialises as `Named` — see [`TypeHintKind::Keyword`](crate::ast::TypeHintKind::Keyword).
115    Keyword(BuiltinType, Span),
116    Nullable(Box<TypeHint>),
117    Union(Box<[TypeHint]>),
118    Intersection(Box<[TypeHint]>),
119}
120
121impl Serialize for TypeHintKind {
122    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
123        match self {
124            Self::Named(name) => s.serialize_newtype_variant("TypeHintKind", 0, "Named", name),
125            Self::Nullable(inner) => {
126                s.serialize_newtype_variant("TypeHintKind", 2, "Nullable", inner)
127            }
128            Self::Union(types) => s.serialize_newtype_variant("TypeHintKind", 3, "Union", types),
129            Self::Intersection(types) => {
130                s.serialize_newtype_variant("TypeHintKind", 4, "Intersection", types)
131            }
132            Self::Keyword(builtin, span) => {
133                struct BuiltinNameRepr<'a>(&'a BuiltinType, &'a Span);
134                impl Serialize for BuiltinNameRepr<'_> {
135                    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
136                        use serde::ser::SerializeStruct;
137                        let mut st = s.serialize_struct("Name", 3)?;
138                        st.serialize_field("parts", &[self.0.as_str()])?;
139                        st.serialize_field("kind", &NameKind::Unqualified)?;
140                        st.serialize_field("span", self.1)?;
141                        st.end()
142                    }
143                }
144                s.serialize_newtype_variant(
145                    "TypeHintKind",
146                    0,
147                    "Named",
148                    &BuiltinNameRepr(builtin, span),
149                )
150            }
151        }
152    }
153}
154
155// ---------------------------------------------------------------------------
156// Arg / Attribute
157// ---------------------------------------------------------------------------
158
159#[derive(Debug, Clone, Serialize)]
160pub struct Arg {
161    pub name: Option<Name>,
162    pub value: Expr,
163    pub unpack: bool,
164    pub by_ref: bool,
165    pub span: Span,
166}
167
168#[derive(Debug, Clone, Serialize)]
169pub struct Attribute {
170    pub name: Name,
171    pub args: Box<[Arg]>,
172    pub span: Span,
173}
174
175// ---------------------------------------------------------------------------
176// Program
177// ---------------------------------------------------------------------------
178
179#[derive(Debug, Clone, Serialize)]
180pub struct Program {
181    pub stmts: Box<[Stmt]>,
182    pub span: Span,
183}
184
185// ---------------------------------------------------------------------------
186// Stmt / StmtKind
187// ---------------------------------------------------------------------------
188
189#[derive(Debug, Clone, Serialize)]
190pub struct Stmt {
191    pub kind: StmtKind,
192    pub span: Span,
193}
194
195#[derive(Debug, Clone, Serialize)]
196pub enum StmtKind {
197    Expression(Box<Expr>),
198    Echo(Box<[Expr]>),
199    Return(Option<Box<Expr>>),
200    Block(Box<[Stmt]>),
201    If(Box<IfStmt>),
202    While(Box<WhileStmt>),
203    For(Box<ForStmt>),
204    Foreach(Box<ForeachStmt>),
205    DoWhile(Box<DoWhileStmt>),
206    Function(Box<FunctionDecl>),
207    Break(Option<Box<Expr>>),
208    Continue(Option<Box<Expr>>),
209    Switch(Box<SwitchStmt>),
210    Goto(Ident),
211    Label(Box<str>),
212    Declare(Box<DeclareStmt>),
213    Unset(Box<[Expr]>),
214    Throw(Box<Expr>),
215    TryCatch(Box<TryCatchStmt>),
216    Global(Box<[Expr]>),
217    Class(Box<ClassDecl>),
218    Interface(Box<InterfaceDecl>),
219    Trait(Box<TraitDecl>),
220    Enum(Box<EnumDecl>),
221    Namespace(Box<NamespaceDecl>),
222    Use(Box<UseDecl>),
223    Const(Box<[ConstItem]>),
224    StaticVar(Box<[StaticVar]>),
225    HaltCompiler(Box<str>),
226    Nop,
227    InlineHtml(Box<str>),
228    Error,
229}
230
231#[derive(Debug, Clone, Serialize)]
232pub struct IfStmt {
233    pub condition: Expr,
234    pub then_branch: Box<Stmt>,
235    pub elseif_branches: Box<[ElseIfBranch]>,
236    pub else_branch: Option<Box<Stmt>>,
237    #[serde(default, skip_serializing_if = "is_false")]
238    pub uses_alternative: bool,
239}
240
241#[derive(Debug, Clone, Serialize)]
242pub struct ElseIfBranch {
243    pub condition: Expr,
244    pub body: Stmt,
245    pub span: Span,
246}
247
248#[derive(Debug, Clone, Serialize)]
249pub struct WhileStmt {
250    pub condition: Expr,
251    pub body: Box<Stmt>,
252    #[serde(default, skip_serializing_if = "is_false")]
253    pub uses_alternative: bool,
254}
255
256#[derive(Debug, Clone, Serialize)]
257pub struct ForStmt {
258    pub init: Box<[Expr]>,
259    pub condition: Box<[Expr]>,
260    pub update: Box<[Expr]>,
261    pub body: Box<Stmt>,
262    #[serde(default, skip_serializing_if = "is_false")]
263    pub uses_alternative: bool,
264}
265
266#[derive(Debug, Clone, Serialize)]
267pub struct ForeachStmt {
268    pub expr: Expr,
269    pub key: Option<Expr>,
270    pub value: Expr,
271    pub body: Box<Stmt>,
272    #[serde(default, skip_serializing_if = "is_false")]
273    pub uses_alternative: bool,
274}
275
276#[derive(Debug, Clone, Serialize)]
277pub struct DoWhileStmt {
278    pub body: Box<Stmt>,
279    pub condition: Expr,
280}
281
282#[derive(Debug, Clone, Serialize)]
283pub struct SwitchStmt {
284    pub expr: Expr,
285    pub cases: Box<[SwitchCase]>,
286    #[serde(default, skip_serializing_if = "is_false")]
287    pub uses_alternative: bool,
288}
289
290#[derive(Debug, Clone, Serialize)]
291pub struct SwitchCase {
292    pub value: Option<Expr>,
293    pub body: Box<[Stmt]>,
294    pub span: Span,
295}
296
297#[derive(Debug, Clone, Serialize)]
298pub struct TryCatchStmt {
299    pub body: Box<[Stmt]>,
300    pub catches: Box<[CatchClause]>,
301    pub finally: Option<Box<[Stmt]>>,
302}
303
304#[derive(Debug, Clone, Serialize)]
305pub struct CatchClause {
306    pub types: Box<[Name]>,
307    pub var: Option<Box<str>>,
308    pub body: Box<[Stmt]>,
309    pub span: Span,
310}
311
312#[derive(Debug, Clone, Serialize)]
313pub struct NamespaceDecl {
314    pub name: Option<Name>,
315    pub body: NamespaceBody,
316}
317
318#[derive(Debug, Clone, Serialize)]
319pub enum NamespaceBody {
320    Braced(Box<[Stmt]>),
321    Simple,
322}
323
324#[derive(Debug, Clone, Serialize)]
325pub struct DeclareStmt {
326    pub directives: Box<[(Box<str>, Expr)]>,
327    pub body: Option<Box<Stmt>>,
328    #[serde(default, skip_serializing_if = "is_false")]
329    pub uses_alternative: bool,
330}
331
332#[derive(Debug, Clone, Serialize)]
333pub struct UseDecl {
334    pub kind: UseKind,
335    pub uses: Box<[UseItem]>,
336}
337
338#[derive(Debug, Clone, Serialize)]
339pub struct UseItem {
340    pub name: Name,
341    pub alias: Option<Box<str>>,
342    #[serde(skip_serializing_if = "Option::is_none")]
343    pub kind: Option<UseKind>,
344    pub span: Span,
345}
346
347#[derive(Debug, Clone, Serialize)]
348pub struct ConstItem {
349    pub name: Ident,
350    pub value: Expr,
351    pub attributes: Box<[Attribute]>,
352    pub span: Span,
353    #[serde(skip_serializing_if = "Option::is_none")]
354    pub doc_comment: Option<Comment>,
355}
356
357#[derive(Debug, Clone, Serialize)]
358pub struct StaticVar {
359    pub name: Ident,
360    pub default: Option<Expr>,
361    pub span: Span,
362}
363
364// ---------------------------------------------------------------------------
365// Expr / ExprKind
366// ---------------------------------------------------------------------------
367
368#[derive(Debug, Clone, Serialize)]
369pub struct Expr {
370    pub kind: ExprKind,
371    pub span: Span,
372}
373
374#[derive(Debug, Clone, Serialize)]
375pub enum ExprKind {
376    Int(i64),
377    Float(f64),
378    String(Box<str>),
379    InterpolatedString(Box<[StringPart]>),
380    Heredoc {
381        label: Box<str>,
382        parts: Box<[StringPart]>,
383    },
384    Nowdoc {
385        label: Box<str>,
386        value: Box<str>,
387    },
388    ShellExec(Box<[StringPart]>),
389    Bool(bool),
390    Null,
391    Variable(Box<str>),
392    VariableVariable(Box<Expr>),
393    Identifier(Box<str>),
394    Assign(AssignExpr),
395    Binary(BinaryExpr),
396    UnaryPrefix(UnaryPrefixExpr),
397    UnaryPostfix(UnaryPostfixExpr),
398    Ternary(TernaryExpr),
399    NullCoalesce(NullCoalesceExpr),
400    FunctionCall(FunctionCallExpr),
401    Array(Box<[ArrayElement]>),
402    ArrayAccess(ArrayAccessExpr),
403    Print(Box<Expr>),
404    Parenthesized(Box<Expr>),
405    Cast(CastKind, Box<Expr>),
406    ErrorSuppress(Box<Expr>),
407    Isset(Box<[Expr]>),
408    Empty(Box<Expr>),
409    Include(IncludeKind, Box<Expr>),
410    Eval(Box<Expr>),
411    Exit(Option<Box<Expr>>),
412    MagicConst(MagicConstKind),
413    Clone(Box<Expr>),
414    CloneWith(Box<Expr>, Box<Expr>),
415    New(NewExpr),
416    PropertyAccess(PropertyAccessExpr),
417    NullsafePropertyAccess(PropertyAccessExpr),
418    MethodCall(Box<MethodCallExpr>),
419    NullsafeMethodCall(Box<MethodCallExpr>),
420    StaticPropertyAccess(StaticAccessExpr),
421    StaticMethodCall(Box<StaticMethodCallExpr>),
422    StaticDynMethodCall(Box<StaticDynMethodCallExpr>),
423    ClassConstAccess(StaticAccessExpr),
424    ClassConstAccessDynamic {
425        class: Box<Expr>,
426        member: Box<Expr>,
427    },
428    StaticPropertyAccessDynamic {
429        class: Box<Expr>,
430        member: Box<Expr>,
431    },
432    Closure(Box<ClosureExpr>),
433    ArrowFunction(Box<ArrowFunctionExpr>),
434    Match(MatchExpr),
435    ThrowExpr(Box<Expr>),
436    Yield(YieldExpr),
437    AnonymousClass(Box<ClassDecl>),
438    CallableCreate(CallableCreateExpr),
439    Omit,
440    Error,
441}
442
443#[derive(Debug, Clone, Serialize)]
444pub struct AssignExpr {
445    pub target: Box<Expr>,
446    pub op: AssignOp,
447    pub value: Box<Expr>,
448    #[serde(skip_serializing_if = "is_false")]
449    pub by_ref: bool,
450}
451
452#[derive(Debug, Clone, Serialize)]
453pub struct BinaryExpr {
454    pub left: Box<Expr>,
455    pub op: BinaryOp,
456    pub right: Box<Expr>,
457}
458
459#[derive(Debug, Clone, Serialize)]
460pub struct UnaryPrefixExpr {
461    pub op: UnaryPrefixOp,
462    pub operand: Box<Expr>,
463}
464
465#[derive(Debug, Clone, Serialize)]
466pub struct UnaryPostfixExpr {
467    pub operand: Box<Expr>,
468    pub op: UnaryPostfixOp,
469}
470
471#[derive(Debug, Clone, Serialize)]
472pub struct TernaryExpr {
473    pub condition: Box<Expr>,
474    pub then_expr: Option<Box<Expr>>,
475    pub else_expr: Box<Expr>,
476}
477
478#[derive(Debug, Clone, Serialize)]
479pub struct NullCoalesceExpr {
480    pub left: Box<Expr>,
481    pub right: Box<Expr>,
482}
483
484#[derive(Debug, Clone, Serialize)]
485pub struct FunctionCallExpr {
486    pub name: Box<Expr>,
487    pub args: Box<[Arg]>,
488}
489
490#[derive(Debug, Clone, Serialize)]
491pub struct ArrayElement {
492    pub key: Option<Expr>,
493    pub value: Expr,
494    pub unpack: bool,
495    #[serde(skip_serializing_if = "is_false")]
496    pub by_ref: bool,
497    pub span: Span,
498}
499
500#[derive(Debug, Clone, Serialize)]
501pub struct ArrayAccessExpr {
502    pub array: Box<Expr>,
503    pub index: Option<Box<Expr>>,
504}
505
506#[derive(Debug, Clone, Serialize)]
507pub struct NewExpr {
508    pub class: Box<Expr>,
509    pub args: Box<[Arg]>,
510}
511
512#[derive(Debug, Clone, Serialize)]
513pub struct PropertyAccessExpr {
514    pub object: Box<Expr>,
515    pub property: Box<Expr>,
516}
517
518#[derive(Debug, Clone, Serialize)]
519pub struct MethodCallExpr {
520    pub object: Box<Expr>,
521    pub method: Box<Expr>,
522    pub args: Box<[Arg]>,
523}
524
525#[derive(Debug, Clone, Serialize)]
526pub struct StaticAccessExpr {
527    pub class: Box<Expr>,
528    pub member: Box<Expr>,
529}
530
531#[derive(Debug, Clone, Serialize)]
532pub struct StaticMethodCallExpr {
533    pub class: Box<Expr>,
534    pub method: Box<Expr>,
535    pub args: Box<[Arg]>,
536}
537
538#[derive(Debug, Clone, Serialize)]
539pub struct StaticDynMethodCallExpr {
540    pub class: Box<Expr>,
541    pub method: Box<Expr>,
542    pub args: Box<[Arg]>,
543}
544
545#[derive(Debug, Clone, Serialize)]
546pub struct ClosureExpr {
547    pub is_static: bool,
548    pub by_ref: bool,
549    pub params: Box<[Param]>,
550    pub use_vars: Box<[ClosureUseVar]>,
551    pub return_type: Option<TypeHint>,
552    pub body: Box<[Stmt]>,
553    pub attributes: Box<[Attribute]>,
554}
555
556#[derive(Debug, Clone, Serialize)]
557pub struct ClosureUseVar {
558    pub name: Box<str>,
559    pub by_ref: bool,
560    pub span: Span,
561}
562
563#[derive(Debug, Clone, Serialize)]
564pub struct ArrowFunctionExpr {
565    pub is_static: bool,
566    pub by_ref: bool,
567    pub params: Box<[Param]>,
568    pub return_type: Option<TypeHint>,
569    pub body: Box<Expr>,
570    pub attributes: Box<[Attribute]>,
571}
572
573#[derive(Debug, Clone, Serialize)]
574pub struct MatchExpr {
575    pub subject: Box<Expr>,
576    pub arms: Box<[MatchArm]>,
577}
578
579#[derive(Debug, Clone, Serialize)]
580pub struct MatchArm {
581    pub conditions: Option<Box<[Expr]>>,
582    pub body: Expr,
583    pub span: Span,
584}
585
586#[derive(Debug, Clone, Serialize)]
587pub struct YieldExpr {
588    pub key: Option<Box<Expr>>,
589    pub value: Option<Box<Expr>>,
590    pub is_from: bool,
591}
592
593#[derive(Debug, Clone, Serialize)]
594pub struct CallableCreateExpr {
595    pub kind: CallableCreateKind,
596}
597
598#[derive(Debug, Clone, Serialize)]
599pub enum CallableCreateKind {
600    Function(Box<Expr>),
601    Method {
602        object: Box<Expr>,
603        method: Box<Expr>,
604    },
605    NullsafeMethod {
606        object: Box<Expr>,
607        method: Box<Expr>,
608    },
609    StaticMethod {
610        class: Box<Expr>,
611        method: Box<Expr>,
612    },
613}
614
615#[derive(Debug, Clone, Serialize)]
616pub enum StringPart {
617    Literal(Box<str>),
618    Expr(Expr),
619}
620
621// ---------------------------------------------------------------------------
622// Owned declaration types
623// ---------------------------------------------------------------------------
624
625#[derive(Debug, Clone, Serialize)]
626pub struct FunctionDecl {
627    pub name: Ident,
628    pub params: Box<[Param]>,
629    pub body: Box<[Stmt]>,
630    pub return_type: Option<TypeHint>,
631    pub by_ref: bool,
632    pub attributes: Box<[Attribute]>,
633    #[serde(skip_serializing_if = "Option::is_none")]
634    pub doc_comment: Option<Comment>,
635}
636
637#[derive(Debug, Clone, Serialize)]
638pub struct Param {
639    pub name: Ident,
640    pub type_hint: Option<TypeHint>,
641    pub default: Option<Expr>,
642    pub by_ref: bool,
643    pub variadic: bool,
644    pub is_readonly: bool,
645    pub is_final: bool,
646    pub visibility: Option<Visibility>,
647    pub set_visibility: Option<Visibility>,
648    pub attributes: Box<[Attribute]>,
649    #[serde(skip_serializing_if = "slice_is_empty")]
650    pub hooks: Box<[PropertyHook]>,
651    pub span: Span,
652}
653
654#[derive(Debug, Clone, Serialize)]
655pub struct ClassDecl {
656    pub name: Option<Ident>,
657    pub modifiers: ClassModifiers,
658    pub extends: Option<Name>,
659    pub implements: Box<[Name]>,
660    pub members: Box<[ClassMember]>,
661    pub attributes: Box<[Attribute]>,
662    #[serde(skip_serializing_if = "Option::is_none")]
663    pub doc_comment: Option<Comment>,
664}
665
666#[derive(Debug, Clone, Serialize)]
667pub struct ClassMember {
668    pub kind: ClassMemberKind,
669    pub span: Span,
670}
671
672#[derive(Debug, Clone, Serialize)]
673pub enum ClassMemberKind {
674    Property(PropertyDecl),
675    Method(MethodDecl),
676    ClassConst(ClassConstDecl),
677    TraitUse(TraitUseDecl),
678}
679
680#[derive(Debug, Clone, Serialize)]
681pub struct PropertyDecl {
682    pub name: Ident,
683    pub visibility: Option<Visibility>,
684    pub set_visibility: Option<Visibility>,
685    pub is_static: bool,
686    pub is_readonly: bool,
687    pub type_hint: Option<TypeHint>,
688    pub default: Option<Expr>,
689    pub attributes: Box<[Attribute]>,
690    #[serde(skip_serializing_if = "slice_is_empty")]
691    pub hooks: Box<[PropertyHook]>,
692    #[serde(skip_serializing_if = "Option::is_none")]
693    pub doc_comment: Option<Comment>,
694}
695
696#[derive(Debug, Clone, Serialize)]
697pub struct PropertyHook {
698    pub kind: PropertyHookKind,
699    pub body: PropertyHookBody,
700    pub is_final: bool,
701    pub by_ref: bool,
702    pub params: Box<[Param]>,
703    pub attributes: Box<[Attribute]>,
704    pub span: Span,
705}
706
707#[derive(Debug, Clone, Serialize)]
708pub enum PropertyHookBody {
709    Block(Box<[Stmt]>),
710    Expression(Expr),
711    Abstract,
712}
713
714#[derive(Debug, Clone, Serialize)]
715pub struct MethodDecl {
716    pub name: Ident,
717    pub visibility: Option<Visibility>,
718    pub is_static: bool,
719    pub is_abstract: bool,
720    pub is_final: bool,
721    pub by_ref: bool,
722    pub params: Box<[Param]>,
723    pub return_type: Option<TypeHint>,
724    pub body: Option<Box<[Stmt]>>,
725    pub attributes: Box<[Attribute]>,
726    #[serde(skip_serializing_if = "Option::is_none")]
727    pub doc_comment: Option<Comment>,
728}
729
730#[derive(Debug, Clone, Serialize)]
731pub struct ClassConstDecl {
732    pub name: Ident,
733    pub visibility: Option<Visibility>,
734    pub is_final: bool,
735    #[serde(skip_serializing_if = "Option::is_none")]
736    pub type_hint: Option<Box<TypeHint>>,
737    pub value: Expr,
738    pub attributes: Box<[Attribute]>,
739    #[serde(skip_serializing_if = "Option::is_none")]
740    pub doc_comment: Option<Comment>,
741}
742
743#[derive(Debug, Clone, Serialize)]
744pub struct TraitUseDecl {
745    pub traits: Box<[Name]>,
746    pub adaptations: Box<[TraitAdaptation]>,
747}
748
749#[derive(Debug, Clone, Serialize)]
750pub struct TraitAdaptation {
751    pub kind: TraitAdaptationKind,
752    pub span: Span,
753}
754
755#[derive(Debug, Clone, Serialize)]
756pub enum TraitAdaptationKind {
757    Precedence {
758        trait_name: Name,
759        method: Name,
760        insteadof: Box<[Name]>,
761    },
762    Alias {
763        trait_name: Option<Name>,
764        method: Name,
765        new_modifier: Option<Visibility>,
766        new_name: Option<Name>,
767    },
768}
769
770#[derive(Debug, Clone, Serialize)]
771pub struct InterfaceDecl {
772    pub name: Ident,
773    pub extends: Box<[Name]>,
774    pub members: Box<[ClassMember]>,
775    pub attributes: Box<[Attribute]>,
776    #[serde(skip_serializing_if = "Option::is_none")]
777    pub doc_comment: Option<Comment>,
778}
779
780#[derive(Debug, Clone, Serialize)]
781pub struct TraitDecl {
782    pub name: Ident,
783    pub members: Box<[ClassMember]>,
784    pub attributes: Box<[Attribute]>,
785    #[serde(skip_serializing_if = "Option::is_none")]
786    pub doc_comment: Option<Comment>,
787}
788
789#[derive(Debug, Clone, Serialize)]
790pub struct EnumDecl {
791    pub name: Ident,
792    pub scalar_type: Option<Name>,
793    pub implements: Box<[Name]>,
794    pub members: Box<[EnumMember]>,
795    pub attributes: Box<[Attribute]>,
796    #[serde(skip_serializing_if = "Option::is_none")]
797    pub doc_comment: Option<Comment>,
798}
799
800#[derive(Debug, Clone, Serialize)]
801pub struct EnumMember {
802    pub kind: EnumMemberKind,
803    pub span: Span,
804}
805
806#[derive(Debug, Clone, Serialize)]
807pub enum EnumMemberKind {
808    Case(EnumCase),
809    Method(MethodDecl),
810    ClassConst(ClassConstDecl),
811    TraitUse(TraitUseDecl),
812}
813
814#[derive(Debug, Clone, Serialize)]
815pub struct EnumCase {
816    pub name: Ident,
817    pub value: Option<Expr>,
818    pub attributes: Box<[Attribute]>,
819    #[serde(skip_serializing_if = "Option::is_none")]
820    pub doc_comment: Option<Comment>,
821}
822
823// ===========================================================================
824// Conversion functions
825// ===========================================================================
826
827fn owned_ident(ident: arena_ast::Ident<'_>) -> Ident {
828    ident.as_str().map(|s| s.into())
829}
830
831fn owned_name(name: &arena_ast::Name<'_, '_>) -> Name {
832    match name {
833        arena_ast::Name::Simple { value, span } => Name {
834            parts: vec![Box::from(*value)].into_boxed_slice(),
835            kind: NameKind::Unqualified,
836            span: *span,
837        },
838        arena_ast::Name::Complex { parts, kind, span } => Name {
839            parts: parts
840                .iter()
841                .map(|s| Box::from(*s))
842                .collect::<Vec<_>>()
843                .into_boxed_slice(),
844            kind: *kind,
845            span: *span,
846        },
847        arena_ast::Name::Error { span } => Name {
848            parts: Box::from([]),
849            kind: NameKind::Error,
850            span: *span,
851        },
852    }
853}
854
855fn owned_comment(c: &arena_ast::Comment<'_>) -> Comment {
856    Comment {
857        kind: c.kind,
858        text: c.text.into(),
859        span: c.span,
860    }
861}
862
863fn owned_opt_comment(c: &Option<arena_ast::Comment<'_>>) -> Option<Comment> {
864    c.as_ref().map(owned_comment)
865}
866
867fn owned_type_hint(th: &arena_ast::TypeHint<'_, '_>) -> TypeHint {
868    TypeHint {
869        kind: owned_type_hint_kind(&th.kind),
870        span: th.span,
871    }
872}
873
874fn owned_type_hint_kind(k: &arena_ast::TypeHintKind<'_, '_>) -> TypeHintKind {
875    match k {
876        arena_ast::TypeHintKind::Named(n) => TypeHintKind::Named(owned_name(n)),
877        arena_ast::TypeHintKind::Keyword(b, span) => TypeHintKind::Keyword(*b, *span),
878        arena_ast::TypeHintKind::Nullable(inner) => {
879            TypeHintKind::Nullable(Box::new(owned_type_hint(inner)))
880        }
881        arena_ast::TypeHintKind::Union(types) => TypeHintKind::Union(
882            types
883                .iter()
884                .map(owned_type_hint)
885                .collect::<Vec<_>>()
886                .into_boxed_slice(),
887        ),
888        arena_ast::TypeHintKind::Intersection(types) => TypeHintKind::Intersection(
889            types
890                .iter()
891                .map(owned_type_hint)
892                .collect::<Vec<_>>()
893                .into_boxed_slice(),
894        ),
895    }
896}
897
898fn owned_arg(arg: &arena_ast::Arg<'_, '_>) -> Arg {
899    Arg {
900        name: arg.name.as_ref().map(owned_name),
901        value: owned_expr(&arg.value),
902        unpack: arg.unpack,
903        by_ref: arg.by_ref,
904        span: arg.span,
905    }
906}
907
908fn owned_args(args: &[arena_ast::Arg<'_, '_>]) -> Box<[Arg]> {
909    args.iter()
910        .map(owned_arg)
911        .collect::<Vec<_>>()
912        .into_boxed_slice()
913}
914
915fn owned_attr(attr: &arena_ast::Attribute<'_, '_>) -> Attribute {
916    Attribute {
917        name: owned_name(&attr.name),
918        args: owned_args(&attr.args),
919        span: attr.span,
920    }
921}
922
923fn owned_attrs(attrs: &[arena_ast::Attribute<'_, '_>]) -> Box<[Attribute]> {
924    attrs
925        .iter()
926        .map(owned_attr)
927        .collect::<Vec<_>>()
928        .into_boxed_slice()
929}
930
931fn owned_string_parts(parts: &[arena_ast::StringPart<'_, '_>]) -> Box<[StringPart]> {
932    parts
933        .iter()
934        .map(|p| match p {
935            arena_ast::StringPart::Literal(s) => StringPart::Literal(Box::from(*s)),
936            arena_ast::StringPart::Expr(e) => StringPart::Expr(owned_expr(e)),
937        })
938        .collect::<Vec<_>>()
939        .into_boxed_slice()
940}
941
942/// Convert an arena-allocated [`Expr`](crate::ast::Expr) into an [`Expr`].
943pub fn to_owned_expr(expr: &arena_ast::Expr<'_, '_>) -> Expr {
944    owned_expr(expr)
945}
946
947fn owned_expr(expr: &arena_ast::Expr<'_, '_>) -> Expr {
948    Expr {
949        kind: owned_expr_kind(&expr.kind),
950        span: expr.span,
951    }
952}
953
954fn owned_exprs(exprs: &[arena_ast::Expr<'_, '_>]) -> Box<[Expr]> {
955    exprs
956        .iter()
957        .map(owned_expr)
958        .collect::<Vec<_>>()
959        .into_boxed_slice()
960}
961
962fn owned_expr_kind(k: &arena_ast::ExprKind<'_, '_>) -> ExprKind {
963    match k {
964        arena_ast::ExprKind::Int(v) => ExprKind::Int(*v),
965        arena_ast::ExprKind::Float(v) => ExprKind::Float(*v),
966        arena_ast::ExprKind::String(s) => ExprKind::String(Box::from(*s)),
967        arena_ast::ExprKind::InterpolatedString(parts) => {
968            ExprKind::InterpolatedString(owned_string_parts(parts))
969        }
970        arena_ast::ExprKind::Heredoc { label, parts } => ExprKind::Heredoc {
971            label: Box::from(*label),
972            parts: owned_string_parts(parts),
973        },
974        arena_ast::ExprKind::Nowdoc { label, value } => ExprKind::Nowdoc {
975            label: Box::from(*label),
976            value: Box::from(*value),
977        },
978        arena_ast::ExprKind::ShellExec(parts) => ExprKind::ShellExec(owned_string_parts(parts)),
979        arena_ast::ExprKind::Bool(v) => ExprKind::Bool(*v),
980        arena_ast::ExprKind::Null => ExprKind::Null,
981        arena_ast::ExprKind::Variable(s) => ExprKind::Variable(s.as_str().into()),
982        arena_ast::ExprKind::VariableVariable(inner) => {
983            ExprKind::VariableVariable(Box::new(owned_expr(inner)))
984        }
985        arena_ast::ExprKind::Identifier(s) => ExprKind::Identifier(s.as_str().into()),
986        arena_ast::ExprKind::Assign(a) => ExprKind::Assign(AssignExpr {
987            target: Box::new(owned_expr(a.target)),
988            op: a.op,
989            value: Box::new(owned_expr(a.value)),
990            by_ref: a.by_ref,
991        }),
992        arena_ast::ExprKind::Binary(b) => ExprKind::Binary(BinaryExpr {
993            left: Box::new(owned_expr(b.left)),
994            op: b.op,
995            right: Box::new(owned_expr(b.right)),
996        }),
997        arena_ast::ExprKind::UnaryPrefix(u) => ExprKind::UnaryPrefix(UnaryPrefixExpr {
998            op: u.op,
999            operand: Box::new(owned_expr(u.operand)),
1000        }),
1001        arena_ast::ExprKind::UnaryPostfix(u) => ExprKind::UnaryPostfix(UnaryPostfixExpr {
1002            operand: Box::new(owned_expr(u.operand)),
1003            op: u.op,
1004        }),
1005        arena_ast::ExprKind::Ternary(t) => ExprKind::Ternary(TernaryExpr {
1006            condition: Box::new(owned_expr(t.condition)),
1007            then_expr: t.then_expr.map(|e| Box::new(owned_expr(e))),
1008            else_expr: Box::new(owned_expr(t.else_expr)),
1009        }),
1010        arena_ast::ExprKind::NullCoalesce(n) => ExprKind::NullCoalesce(NullCoalesceExpr {
1011            left: Box::new(owned_expr(n.left)),
1012            right: Box::new(owned_expr(n.right)),
1013        }),
1014        arena_ast::ExprKind::FunctionCall(f) => ExprKind::FunctionCall(FunctionCallExpr {
1015            name: Box::new(owned_expr(f.name)),
1016            args: owned_args(&f.args),
1017        }),
1018        arena_ast::ExprKind::Array(elems) => ExprKind::Array(
1019            elems
1020                .iter()
1021                .map(|e| ArrayElement {
1022                    key: e.key.as_ref().map(owned_expr),
1023                    value: owned_expr(&e.value),
1024                    unpack: e.unpack,
1025                    by_ref: e.by_ref,
1026                    span: e.span,
1027                })
1028                .collect::<Vec<_>>()
1029                .into_boxed_slice(),
1030        ),
1031        arena_ast::ExprKind::ArrayAccess(a) => ExprKind::ArrayAccess(ArrayAccessExpr {
1032            array: Box::new(owned_expr(a.array)),
1033            index: a.index.map(|e| Box::new(owned_expr(e))),
1034        }),
1035        arena_ast::ExprKind::Print(e) => ExprKind::Print(Box::new(owned_expr(e))),
1036        arena_ast::ExprKind::Parenthesized(e) => ExprKind::Parenthesized(Box::new(owned_expr(e))),
1037        arena_ast::ExprKind::Cast(kind, e) => ExprKind::Cast(*kind, Box::new(owned_expr(e))),
1038        arena_ast::ExprKind::ErrorSuppress(e) => ExprKind::ErrorSuppress(Box::new(owned_expr(e))),
1039        arena_ast::ExprKind::Isset(exprs) => ExprKind::Isset(owned_exprs(exprs)),
1040        arena_ast::ExprKind::Empty(e) => ExprKind::Empty(Box::new(owned_expr(e))),
1041        arena_ast::ExprKind::Include(kind, e) => ExprKind::Include(*kind, Box::new(owned_expr(e))),
1042        arena_ast::ExprKind::Eval(e) => ExprKind::Eval(Box::new(owned_expr(e))),
1043        arena_ast::ExprKind::Exit(e) => ExprKind::Exit(e.map(|e| Box::new(owned_expr(e)))),
1044        arena_ast::ExprKind::MagicConst(m) => ExprKind::MagicConst(*m),
1045        arena_ast::ExprKind::Clone(e) => ExprKind::Clone(Box::new(owned_expr(e))),
1046        arena_ast::ExprKind::CloneWith(obj, props) => {
1047            ExprKind::CloneWith(Box::new(owned_expr(obj)), Box::new(owned_expr(props)))
1048        }
1049        arena_ast::ExprKind::New(n) => ExprKind::New(NewExpr {
1050            class: Box::new(owned_expr(n.class)),
1051            args: owned_args(&n.args),
1052        }),
1053        arena_ast::ExprKind::PropertyAccess(p) => ExprKind::PropertyAccess(PropertyAccessExpr {
1054            object: Box::new(owned_expr(p.object)),
1055            property: Box::new(owned_expr(p.property)),
1056        }),
1057        arena_ast::ExprKind::NullsafePropertyAccess(p) => {
1058            ExprKind::NullsafePropertyAccess(PropertyAccessExpr {
1059                object: Box::new(owned_expr(p.object)),
1060                property: Box::new(owned_expr(p.property)),
1061            })
1062        }
1063        arena_ast::ExprKind::MethodCall(m) => ExprKind::MethodCall(Box::new(MethodCallExpr {
1064            object: Box::new(owned_expr(m.object)),
1065            method: Box::new(owned_expr(m.method)),
1066            args: owned_args(&m.args),
1067        })),
1068        arena_ast::ExprKind::NullsafeMethodCall(m) => {
1069            ExprKind::NullsafeMethodCall(Box::new(MethodCallExpr {
1070                object: Box::new(owned_expr(m.object)),
1071                method: Box::new(owned_expr(m.method)),
1072                args: owned_args(&m.args),
1073            }))
1074        }
1075        arena_ast::ExprKind::StaticPropertyAccess(s) => {
1076            ExprKind::StaticPropertyAccess(StaticAccessExpr {
1077                class: Box::new(owned_expr(s.class)),
1078                member: Box::new(owned_expr(s.member)),
1079            })
1080        }
1081        arena_ast::ExprKind::StaticMethodCall(s) => {
1082            ExprKind::StaticMethodCall(Box::new(StaticMethodCallExpr {
1083                class: Box::new(owned_expr(s.class)),
1084                method: Box::new(owned_expr(s.method)),
1085                args: owned_args(&s.args),
1086            }))
1087        }
1088        arena_ast::ExprKind::StaticDynMethodCall(s) => {
1089            ExprKind::StaticDynMethodCall(Box::new(StaticDynMethodCallExpr {
1090                class: Box::new(owned_expr(s.class)),
1091                method: Box::new(owned_expr(s.method)),
1092                args: owned_args(&s.args),
1093            }))
1094        }
1095        arena_ast::ExprKind::ClassConstAccess(s) => ExprKind::ClassConstAccess(StaticAccessExpr {
1096            class: Box::new(owned_expr(s.class)),
1097            member: Box::new(owned_expr(s.member)),
1098        }),
1099        arena_ast::ExprKind::ClassConstAccessDynamic { class, member } => {
1100            ExprKind::ClassConstAccessDynamic {
1101                class: Box::new(owned_expr(class)),
1102                member: Box::new(owned_expr(member)),
1103            }
1104        }
1105        arena_ast::ExprKind::StaticPropertyAccessDynamic { class, member } => {
1106            ExprKind::StaticPropertyAccessDynamic {
1107                class: Box::new(owned_expr(class)),
1108                member: Box::new(owned_expr(member)),
1109            }
1110        }
1111        arena_ast::ExprKind::Closure(c) => ExprKind::Closure(Box::new(ClosureExpr {
1112            is_static: c.is_static,
1113            by_ref: c.by_ref,
1114            params: owned_params(&c.params),
1115            use_vars: c
1116                .use_vars
1117                .iter()
1118                .map(|v| ClosureUseVar {
1119                    name: v.name.into(),
1120                    by_ref: v.by_ref,
1121                    span: v.span,
1122                })
1123                .collect::<Vec<_>>()
1124                .into_boxed_slice(),
1125            return_type: c.return_type.as_ref().map(owned_type_hint),
1126            body: owned_stmts(&c.body),
1127            attributes: owned_attrs(&c.attributes),
1128        })),
1129        arena_ast::ExprKind::ArrowFunction(f) => {
1130            ExprKind::ArrowFunction(Box::new(ArrowFunctionExpr {
1131                is_static: f.is_static,
1132                by_ref: f.by_ref,
1133                params: owned_params(&f.params),
1134                return_type: f.return_type.as_ref().map(owned_type_hint),
1135                body: Box::new(owned_expr(f.body)),
1136                attributes: owned_attrs(&f.attributes),
1137            }))
1138        }
1139        arena_ast::ExprKind::Match(m) => ExprKind::Match(MatchExpr {
1140            subject: Box::new(owned_expr(m.subject)),
1141            arms: m
1142                .arms
1143                .iter()
1144                .map(|arm| MatchArm {
1145                    conditions: arm.conditions.as_ref().map(|conds| owned_exprs(conds)),
1146                    body: owned_expr(&arm.body),
1147                    span: arm.span,
1148                })
1149                .collect::<Vec<_>>()
1150                .into_boxed_slice(),
1151        }),
1152        arena_ast::ExprKind::ThrowExpr(e) => ExprKind::ThrowExpr(Box::new(owned_expr(e))),
1153        arena_ast::ExprKind::Yield(y) => ExprKind::Yield(YieldExpr {
1154            key: y.key.map(|e| Box::new(owned_expr(e))),
1155            value: y.value.map(|e| Box::new(owned_expr(e))),
1156            is_from: y.is_from,
1157        }),
1158        arena_ast::ExprKind::AnonymousClass(cls) => {
1159            ExprKind::AnonymousClass(Box::new(owned_class_decl(cls)))
1160        }
1161        arena_ast::ExprKind::CallableCreate(c) => ExprKind::CallableCreate(CallableCreateExpr {
1162            kind: match &c.kind {
1163                arena_ast::CallableCreateKind::Function(e) => {
1164                    CallableCreateKind::Function(Box::new(owned_expr(e)))
1165                }
1166                arena_ast::CallableCreateKind::Method { object, method } => {
1167                    CallableCreateKind::Method {
1168                        object: Box::new(owned_expr(object)),
1169                        method: Box::new(owned_expr(method)),
1170                    }
1171                }
1172                arena_ast::CallableCreateKind::NullsafeMethod { object, method } => {
1173                    CallableCreateKind::NullsafeMethod {
1174                        object: Box::new(owned_expr(object)),
1175                        method: Box::new(owned_expr(method)),
1176                    }
1177                }
1178                arena_ast::CallableCreateKind::StaticMethod { class, method } => {
1179                    CallableCreateKind::StaticMethod {
1180                        class: Box::new(owned_expr(class)),
1181                        method: Box::new(owned_expr(method)),
1182                    }
1183                }
1184            },
1185        }),
1186        arena_ast::ExprKind::Omit => ExprKind::Omit,
1187        arena_ast::ExprKind::Error => ExprKind::Error,
1188    }
1189}
1190
1191fn owned_param(p: &arena_ast::Param<'_, '_>) -> Param {
1192    Param {
1193        name: owned_ident(p.name),
1194        type_hint: p.type_hint.as_ref().map(owned_type_hint),
1195        default: p.default.as_ref().map(owned_expr),
1196        by_ref: p.by_ref,
1197        variadic: p.variadic,
1198        is_readonly: p.is_readonly,
1199        is_final: p.is_final,
1200        visibility: p.visibility,
1201        set_visibility: p.set_visibility,
1202        attributes: owned_attrs(&p.attributes),
1203        hooks: owned_hooks(&p.hooks),
1204        span: p.span,
1205    }
1206}
1207
1208fn owned_params(params: &[arena_ast::Param<'_, '_>]) -> Box<[Param]> {
1209    params
1210        .iter()
1211        .map(owned_param)
1212        .collect::<Vec<_>>()
1213        .into_boxed_slice()
1214}
1215
1216fn owned_hook(h: &arena_ast::PropertyHook<'_, '_>) -> PropertyHook {
1217    PropertyHook {
1218        kind: h.kind,
1219        body: match &h.body {
1220            arena_ast::PropertyHookBody::Block(stmts) => {
1221                PropertyHookBody::Block(owned_stmts(stmts))
1222            }
1223            arena_ast::PropertyHookBody::Expression(e) => {
1224                PropertyHookBody::Expression(owned_expr(e))
1225            }
1226            arena_ast::PropertyHookBody::Abstract => PropertyHookBody::Abstract,
1227        },
1228        is_final: h.is_final,
1229        by_ref: h.by_ref,
1230        params: owned_params(&h.params),
1231        attributes: owned_attrs(&h.attributes),
1232        span: h.span,
1233    }
1234}
1235
1236fn owned_hooks(hooks: &[arena_ast::PropertyHook<'_, '_>]) -> Box<[PropertyHook]> {
1237    hooks
1238        .iter()
1239        .map(owned_hook)
1240        .collect::<Vec<_>>()
1241        .into_boxed_slice()
1242}
1243
1244fn owned_stmts(stmts: &[arena_ast::Stmt<'_, '_>]) -> Box<[Stmt]> {
1245    stmts
1246        .iter()
1247        .map(owned_stmt)
1248        .collect::<Vec<_>>()
1249        .into_boxed_slice()
1250}
1251
1252/// Convert an arena-allocated [`Stmt`](crate::ast::Stmt) into a [`Stmt`].
1253pub fn to_owned_stmt(stmt: &arena_ast::Stmt<'_, '_>) -> Stmt {
1254    owned_stmt(stmt)
1255}
1256
1257fn owned_stmt(stmt: &arena_ast::Stmt<'_, '_>) -> Stmt {
1258    Stmt {
1259        kind: owned_stmt_kind(&stmt.kind),
1260        span: stmt.span,
1261    }
1262}
1263
1264fn owned_stmt_kind(k: &arena_ast::StmtKind<'_, '_>) -> StmtKind {
1265    match k {
1266        arena_ast::StmtKind::Expression(e) => StmtKind::Expression(Box::new(owned_expr(e))),
1267        arena_ast::StmtKind::Echo(exprs) => StmtKind::Echo(owned_exprs(exprs)),
1268        arena_ast::StmtKind::Return(e) => StmtKind::Return(e.map(|e| Box::new(owned_expr(e)))),
1269        arena_ast::StmtKind::Block(stmts) => StmtKind::Block(owned_stmts(stmts)),
1270        arena_ast::StmtKind::If(s) => StmtKind::If(Box::new(IfStmt {
1271            condition: owned_expr(&s.condition),
1272            then_branch: Box::new(owned_stmt(s.then_branch)),
1273            elseif_branches: s
1274                .elseif_branches
1275                .iter()
1276                .map(|b| ElseIfBranch {
1277                    condition: owned_expr(&b.condition),
1278                    body: owned_stmt(&b.body),
1279                    span: b.span,
1280                })
1281                .collect::<Vec<_>>()
1282                .into_boxed_slice(),
1283            else_branch: s.else_branch.map(|b| Box::new(owned_stmt(b))),
1284            uses_alternative: s.uses_alternative,
1285        })),
1286        arena_ast::StmtKind::While(s) => StmtKind::While(Box::new(WhileStmt {
1287            condition: owned_expr(&s.condition),
1288            body: Box::new(owned_stmt(s.body)),
1289            uses_alternative: s.uses_alternative,
1290        })),
1291        arena_ast::StmtKind::For(s) => StmtKind::For(Box::new(ForStmt {
1292            init: owned_exprs(&s.init),
1293            condition: owned_exprs(&s.condition),
1294            update: owned_exprs(&s.update),
1295            body: Box::new(owned_stmt(s.body)),
1296            uses_alternative: s.uses_alternative,
1297        })),
1298        arena_ast::StmtKind::Foreach(s) => StmtKind::Foreach(Box::new(ForeachStmt {
1299            expr: owned_expr(&s.expr),
1300            key: s.key.as_ref().map(owned_expr),
1301            value: owned_expr(&s.value),
1302            body: Box::new(owned_stmt(s.body)),
1303            uses_alternative: s.uses_alternative,
1304        })),
1305        arena_ast::StmtKind::DoWhile(s) => StmtKind::DoWhile(Box::new(DoWhileStmt {
1306            body: Box::new(owned_stmt(s.body)),
1307            condition: owned_expr(&s.condition),
1308        })),
1309        arena_ast::StmtKind::Function(f) => StmtKind::Function(Box::new(owned_function_decl(f))),
1310        arena_ast::StmtKind::Break(e) => StmtKind::Break(e.map(|e| Box::new(owned_expr(e)))),
1311        arena_ast::StmtKind::Continue(e) => StmtKind::Continue(e.map(|e| Box::new(owned_expr(e)))),
1312        arena_ast::StmtKind::Switch(s) => StmtKind::Switch(Box::new(SwitchStmt {
1313            expr: owned_expr(&s.expr),
1314            cases: s
1315                .cases
1316                .iter()
1317                .map(|c| SwitchCase {
1318                    value: c.value.as_ref().map(owned_expr),
1319                    body: owned_stmts(&c.body),
1320                    span: c.span,
1321                })
1322                .collect::<Vec<_>>()
1323                .into_boxed_slice(),
1324            uses_alternative: s.uses_alternative,
1325        })),
1326        arena_ast::StmtKind::Goto(ident) => StmtKind::Goto(owned_ident(*ident)),
1327        arena_ast::StmtKind::Label(s) => StmtKind::Label(Box::from(*s)),
1328        arena_ast::StmtKind::Declare(d) => StmtKind::Declare(Box::new(DeclareStmt {
1329            directives: d
1330                .directives
1331                .iter()
1332                .map(|(k, v)| (Box::from(*k), owned_expr(v)))
1333                .collect::<Vec<_>>()
1334                .into_boxed_slice(),
1335            body: d.body.map(|b| Box::new(owned_stmt(b))),
1336            uses_alternative: d.uses_alternative,
1337        })),
1338        arena_ast::StmtKind::Unset(exprs) => StmtKind::Unset(owned_exprs(exprs)),
1339        arena_ast::StmtKind::Throw(e) => StmtKind::Throw(Box::new(owned_expr(e))),
1340        arena_ast::StmtKind::TryCatch(t) => StmtKind::TryCatch(Box::new(TryCatchStmt {
1341            body: owned_stmts(&t.body),
1342            catches: t
1343                .catches
1344                .iter()
1345                .map(|c| CatchClause {
1346                    types: c
1347                        .types
1348                        .iter()
1349                        .map(owned_name)
1350                        .collect::<Vec<_>>()
1351                        .into_boxed_slice(),
1352                    var: c.var.map(Box::from),
1353                    body: owned_stmts(&c.body),
1354                    span: c.span,
1355                })
1356                .collect::<Vec<_>>()
1357                .into_boxed_slice(),
1358            finally: t.finally.as_ref().map(|stmts| owned_stmts(stmts)),
1359        })),
1360        arena_ast::StmtKind::Global(exprs) => StmtKind::Global(owned_exprs(exprs)),
1361        arena_ast::StmtKind::Class(cls) => StmtKind::Class(Box::new(owned_class_decl(cls))),
1362        arena_ast::StmtKind::Interface(iface) => {
1363            StmtKind::Interface(Box::new(owned_interface_decl(iface)))
1364        }
1365        arena_ast::StmtKind::Trait(tr) => StmtKind::Trait(Box::new(owned_trait_decl(tr))),
1366        arena_ast::StmtKind::Enum(en) => StmtKind::Enum(Box::new(owned_enum_decl(en))),
1367        arena_ast::StmtKind::Namespace(ns) => StmtKind::Namespace(Box::new(NamespaceDecl {
1368            name: ns.name.as_ref().map(owned_name),
1369            body: match &ns.body {
1370                arena_ast::NamespaceBody::Braced(stmts) => {
1371                    NamespaceBody::Braced(owned_stmts(stmts))
1372                }
1373                arena_ast::NamespaceBody::Simple => NamespaceBody::Simple,
1374            },
1375        })),
1376        arena_ast::StmtKind::Use(u) => StmtKind::Use(Box::new(UseDecl {
1377            kind: u.kind,
1378            uses: u
1379                .uses
1380                .iter()
1381                .map(|item| UseItem {
1382                    name: owned_name(&item.name),
1383                    alias: item.alias.map(Box::from),
1384                    kind: item.kind,
1385                    span: item.span,
1386                })
1387                .collect::<Vec<_>>()
1388                .into_boxed_slice(),
1389        })),
1390        arena_ast::StmtKind::Const(items) => StmtKind::Const(
1391            items
1392                .iter()
1393                .map(|item| ConstItem {
1394                    name: owned_ident(item.name),
1395                    value: owned_expr(&item.value),
1396                    attributes: owned_attrs(&item.attributes),
1397                    span: item.span,
1398                    doc_comment: owned_opt_comment(&item.doc_comment),
1399                })
1400                .collect::<Vec<_>>()
1401                .into_boxed_slice(),
1402        ),
1403        arena_ast::StmtKind::StaticVar(vars) => StmtKind::StaticVar(
1404            vars.iter()
1405                .map(|v| StaticVar {
1406                    name: owned_ident(v.name),
1407                    default: v.default.as_ref().map(owned_expr),
1408                    span: v.span,
1409                })
1410                .collect::<Vec<_>>()
1411                .into_boxed_slice(),
1412        ),
1413        arena_ast::StmtKind::HaltCompiler(s) => StmtKind::HaltCompiler(Box::from(*s)),
1414        arena_ast::StmtKind::Nop => StmtKind::Nop,
1415        arena_ast::StmtKind::InlineHtml(s) => StmtKind::InlineHtml(Box::from(*s)),
1416        arena_ast::StmtKind::Error => StmtKind::Error,
1417    }
1418}
1419
1420fn owned_function_decl(f: &arena_ast::FunctionDecl<'_, '_>) -> FunctionDecl {
1421    FunctionDecl {
1422        name: owned_ident(f.name),
1423        params: owned_params(&f.params),
1424        body: owned_stmts(&f.body),
1425        return_type: f.return_type.as_ref().map(owned_type_hint),
1426        by_ref: f.by_ref,
1427        attributes: owned_attrs(&f.attributes),
1428        doc_comment: owned_opt_comment(&f.doc_comment),
1429    }
1430}
1431
1432fn owned_class_member(m: &arena_ast::ClassMember<'_, '_>) -> ClassMember {
1433    ClassMember {
1434        kind: match &m.kind {
1435            arena_ast::ClassMemberKind::Property(p) => ClassMemberKind::Property(PropertyDecl {
1436                name: owned_ident(p.name),
1437                visibility: p.visibility,
1438                set_visibility: p.set_visibility,
1439                is_static: p.is_static,
1440                is_readonly: p.is_readonly,
1441                type_hint: p.type_hint.as_ref().map(owned_type_hint),
1442                default: p.default.as_ref().map(owned_expr),
1443                attributes: owned_attrs(&p.attributes),
1444                hooks: owned_hooks(&p.hooks),
1445                doc_comment: owned_opt_comment(&p.doc_comment),
1446            }),
1447            arena_ast::ClassMemberKind::Method(m) => ClassMemberKind::Method(MethodDecl {
1448                name: owned_ident(m.name),
1449                visibility: m.visibility,
1450                is_static: m.is_static,
1451                is_abstract: m.is_abstract,
1452                is_final: m.is_final,
1453                by_ref: m.by_ref,
1454                params: owned_params(&m.params),
1455                return_type: m.return_type.as_ref().map(owned_type_hint),
1456                body: m.body.as_ref().map(|stmts| owned_stmts(stmts)),
1457                attributes: owned_attrs(&m.attributes),
1458                doc_comment: owned_opt_comment(&m.doc_comment),
1459            }),
1460            arena_ast::ClassMemberKind::ClassConst(c) => {
1461                ClassMemberKind::ClassConst(owned_class_const(c))
1462            }
1463            arena_ast::ClassMemberKind::TraitUse(t) => {
1464                ClassMemberKind::TraitUse(owned_trait_use(t))
1465            }
1466        },
1467        span: m.span,
1468    }
1469}
1470
1471fn owned_class_members(members: &[arena_ast::ClassMember<'_, '_>]) -> Box<[ClassMember]> {
1472    members
1473        .iter()
1474        .map(owned_class_member)
1475        .collect::<Vec<_>>()
1476        .into_boxed_slice()
1477}
1478
1479fn owned_class_const(c: &arena_ast::ClassConstDecl<'_, '_>) -> ClassConstDecl {
1480    ClassConstDecl {
1481        name: owned_ident(c.name),
1482        visibility: c.visibility,
1483        is_final: c.is_final,
1484        type_hint: c.type_hint.map(|th| Box::new(owned_type_hint(th))),
1485        value: owned_expr(&c.value),
1486        attributes: owned_attrs(&c.attributes),
1487        doc_comment: owned_opt_comment(&c.doc_comment),
1488    }
1489}
1490
1491fn owned_trait_use(t: &arena_ast::TraitUseDecl<'_, '_>) -> TraitUseDecl {
1492    TraitUseDecl {
1493        traits: t
1494            .traits
1495            .iter()
1496            .map(owned_name)
1497            .collect::<Vec<_>>()
1498            .into_boxed_slice(),
1499        adaptations: t
1500            .adaptations
1501            .iter()
1502            .map(|a| TraitAdaptation {
1503                kind: match &a.kind {
1504                    arena_ast::TraitAdaptationKind::Precedence {
1505                        trait_name,
1506                        method,
1507                        insteadof,
1508                    } => TraitAdaptationKind::Precedence {
1509                        trait_name: owned_name(trait_name),
1510                        method: owned_name(method),
1511                        insteadof: insteadof
1512                            .iter()
1513                            .map(owned_name)
1514                            .collect::<Vec<_>>()
1515                            .into_boxed_slice(),
1516                    },
1517                    arena_ast::TraitAdaptationKind::Alias {
1518                        trait_name,
1519                        method,
1520                        new_modifier,
1521                        new_name,
1522                    } => TraitAdaptationKind::Alias {
1523                        trait_name: trait_name.as_ref().map(owned_name),
1524                        method: owned_name(method),
1525                        new_modifier: *new_modifier,
1526                        new_name: new_name.as_ref().map(owned_name),
1527                    },
1528                },
1529                span: a.span,
1530            })
1531            .collect::<Vec<_>>()
1532            .into_boxed_slice(),
1533    }
1534}
1535
1536fn owned_class_decl(cls: &arena_ast::ClassDecl<'_, '_>) -> ClassDecl {
1537    ClassDecl {
1538        name: cls
1539            .name
1540            .map(|ident| Some(owned_ident(ident)))
1541            .unwrap_or(None),
1542        modifiers: cls.modifiers.clone(),
1543        extends: cls.extends.as_ref().map(owned_name),
1544        implements: cls
1545            .implements
1546            .iter()
1547            .map(owned_name)
1548            .collect::<Vec<_>>()
1549            .into_boxed_slice(),
1550        members: owned_class_members(&cls.members),
1551        attributes: owned_attrs(&cls.attributes),
1552        doc_comment: owned_opt_comment(&cls.doc_comment),
1553    }
1554}
1555
1556fn owned_interface_decl(iface: &arena_ast::InterfaceDecl<'_, '_>) -> InterfaceDecl {
1557    InterfaceDecl {
1558        name: owned_ident(iface.name),
1559        extends: iface
1560            .extends
1561            .iter()
1562            .map(owned_name)
1563            .collect::<Vec<_>>()
1564            .into_boxed_slice(),
1565        members: owned_class_members(&iface.members),
1566        attributes: owned_attrs(&iface.attributes),
1567        doc_comment: owned_opt_comment(&iface.doc_comment),
1568    }
1569}
1570
1571fn owned_trait_decl(tr: &arena_ast::TraitDecl<'_, '_>) -> TraitDecl {
1572    TraitDecl {
1573        name: owned_ident(tr.name),
1574        members: owned_class_members(&tr.members),
1575        attributes: owned_attrs(&tr.attributes),
1576        doc_comment: owned_opt_comment(&tr.doc_comment),
1577    }
1578}
1579
1580fn owned_enum_decl(en: &arena_ast::EnumDecl<'_, '_>) -> EnumDecl {
1581    EnumDecl {
1582        name: owned_ident(en.name),
1583        scalar_type: en.scalar_type.as_ref().map(owned_name),
1584        implements: en
1585            .implements
1586            .iter()
1587            .map(owned_name)
1588            .collect::<Vec<_>>()
1589            .into_boxed_slice(),
1590        members: en
1591            .members
1592            .iter()
1593            .map(|m| EnumMember {
1594                kind: match &m.kind {
1595                    arena_ast::EnumMemberKind::Case(c) => EnumMemberKind::Case(EnumCase {
1596                        name: owned_ident(c.name),
1597                        value: c.value.as_ref().map(owned_expr),
1598                        attributes: owned_attrs(&c.attributes),
1599                        doc_comment: owned_opt_comment(&c.doc_comment),
1600                    }),
1601                    arena_ast::EnumMemberKind::Method(m) => EnumMemberKind::Method(MethodDecl {
1602                        name: owned_ident(m.name),
1603                        visibility: m.visibility,
1604                        is_static: m.is_static,
1605                        is_abstract: m.is_abstract,
1606                        is_final: m.is_final,
1607                        by_ref: m.by_ref,
1608                        params: owned_params(&m.params),
1609                        return_type: m.return_type.as_ref().map(owned_type_hint),
1610                        body: m.body.as_ref().map(|stmts| owned_stmts(stmts)),
1611                        attributes: owned_attrs(&m.attributes),
1612                        doc_comment: owned_opt_comment(&m.doc_comment),
1613                    }),
1614                    arena_ast::EnumMemberKind::ClassConst(c) => {
1615                        EnumMemberKind::ClassConst(owned_class_const(c))
1616                    }
1617                    arena_ast::EnumMemberKind::TraitUse(t) => {
1618                        EnumMemberKind::TraitUse(owned_trait_use(t))
1619                    }
1620                },
1621                span: m.span,
1622            })
1623            .collect::<Vec<_>>()
1624            .into_boxed_slice(),
1625        attributes: owned_attrs(&en.attributes),
1626        doc_comment: owned_opt_comment(&en.doc_comment),
1627    }
1628}
1629
1630/// Convert an arena-allocated [`Program`](crate::ast::Program) into a [`Program`].
1631pub fn to_owned_program(program: &arena_ast::Program<'_, '_>) -> Program {
1632    Program {
1633        stmts: owned_stmts(&program.stmts),
1634        span: program.span,
1635    }
1636}
1637
1638// ===========================================================================
1639// Reverse conversion: owned → arena
1640// ===========================================================================
1641
1642/// Convert an owned [`Program`] into an arena-allocated
1643/// [`Program`](crate::ast::Program).
1644///
1645/// All strings are copied into `arena`. Both lifetime parameters of the
1646/// returned type are `'arena` because every string originates in the arena
1647/// rather than a source buffer.
1648///
1649/// Used internally by the pretty printer so callers can pass an owned program
1650/// without round-tripping through `parse_arena()`.
1651pub fn from_owned_program<'arena>(
1652    arena: &'arena bumpalo::Bump,
1653    program: &Program,
1654) -> arena_ast::Program<'arena, 'arena> {
1655    arena_ast::Program {
1656        stmts: av_stmts(arena, &program.stmts),
1657        span: program.span,
1658    }
1659}
1660
1661// Helper: allocate a string in the arena.
1662#[inline]
1663fn av_str<'a>(arena: &'a bumpalo::Bump, s: &str) -> &'a str {
1664    arena.alloc_str(s)
1665}
1666
1667fn av_ident<'a>(arena: &'a bumpalo::Bump, ident: &Ident) -> arena_ast::Ident<'a> {
1668    match ident {
1669        Some(s) => arena_ast::Ident::name(arena.alloc_str(s)),
1670        None => arena_ast::Ident::ERROR,
1671    }
1672}
1673
1674fn av_name<'a>(arena: &'a bumpalo::Bump, name: &Name) -> arena_ast::Name<'a, 'a> {
1675    if name.kind == NameKind::Error {
1676        return arena_ast::Name::Error { span: name.span };
1677    }
1678    if name.parts.len() == 1 && name.kind == NameKind::Unqualified {
1679        return arena_ast::Name::Simple {
1680            value: arena.alloc_str(&name.parts[0]),
1681            span: name.span,
1682        };
1683    }
1684    let mut parts = arena_ast::ArenaVec::with_capacity_in(name.parts.len(), arena);
1685    for p in name.parts.iter() {
1686        parts.push(arena.alloc_str(p) as &str);
1687    }
1688    arena_ast::Name::Complex {
1689        parts,
1690        kind: name.kind,
1691        span: name.span,
1692    }
1693}
1694
1695fn av_type_hint<'a>(arena: &'a bumpalo::Bump, th: &TypeHint) -> arena_ast::TypeHint<'a, 'a> {
1696    arena_ast::TypeHint {
1697        kind: av_type_hint_kind(arena, &th.kind),
1698        span: th.span,
1699    }
1700}
1701
1702fn av_type_hint_kind<'a>(
1703    arena: &'a bumpalo::Bump,
1704    k: &TypeHintKind,
1705) -> arena_ast::TypeHintKind<'a, 'a> {
1706    match k {
1707        TypeHintKind::Named(n) => arena_ast::TypeHintKind::Named(av_name(arena, n)),
1708        TypeHintKind::Keyword(b, span) => arena_ast::TypeHintKind::Keyword(*b, *span),
1709        TypeHintKind::Nullable(inner) => {
1710            arena_ast::TypeHintKind::Nullable(arena.alloc(av_type_hint(arena, inner)))
1711        }
1712        TypeHintKind::Union(types) => {
1713            let mut v = arena_ast::ArenaVec::with_capacity_in(types.len(), arena);
1714            for t in types.iter() {
1715                v.push(av_type_hint(arena, t));
1716            }
1717            arena_ast::TypeHintKind::Union(v)
1718        }
1719        TypeHintKind::Intersection(types) => {
1720            let mut v = arena_ast::ArenaVec::with_capacity_in(types.len(), arena);
1721            for t in types.iter() {
1722                v.push(av_type_hint(arena, t));
1723            }
1724            arena_ast::TypeHintKind::Intersection(v)
1725        }
1726    }
1727}
1728
1729fn av_arg<'a>(arena: &'a bumpalo::Bump, arg: &Arg) -> arena_ast::Arg<'a, 'a> {
1730    arena_ast::Arg {
1731        name: arg.name.as_ref().map(|n| av_name(arena, n)),
1732        value: av_expr(arena, &arg.value),
1733        unpack: arg.unpack,
1734        by_ref: arg.by_ref,
1735        span: arg.span,
1736    }
1737}
1738
1739fn av_args<'a>(
1740    arena: &'a bumpalo::Bump,
1741    args: &[Arg],
1742) -> arena_ast::ArenaVec<'a, arena_ast::Arg<'a, 'a>> {
1743    let mut v = arena_ast::ArenaVec::with_capacity_in(args.len(), arena);
1744    for a in args.iter() {
1745        v.push(av_arg(arena, a));
1746    }
1747    v
1748}
1749
1750fn av_attr<'a>(arena: &'a bumpalo::Bump, attr: &Attribute) -> arena_ast::Attribute<'a, 'a> {
1751    arena_ast::Attribute {
1752        name: av_name(arena, &attr.name),
1753        args: av_args(arena, &attr.args),
1754        span: attr.span,
1755    }
1756}
1757
1758fn av_attrs<'a>(
1759    arena: &'a bumpalo::Bump,
1760    attrs: &[Attribute],
1761) -> arena_ast::ArenaVec<'a, arena_ast::Attribute<'a, 'a>> {
1762    let mut v = arena_ast::ArenaVec::with_capacity_in(attrs.len(), arena);
1763    for a in attrs.iter() {
1764        v.push(av_attr(arena, a));
1765    }
1766    v
1767}
1768
1769fn av_comment<'a>(arena: &'a bumpalo::Bump, c: &Comment) -> arena_ast::Comment<'a> {
1770    arena_ast::Comment {
1771        kind: c.kind,
1772        text: arena.alloc_str(&c.text),
1773        span: c.span,
1774    }
1775}
1776
1777fn av_opt_comment<'a>(
1778    arena: &'a bumpalo::Bump,
1779    c: &Option<Comment>,
1780) -> Option<arena_ast::Comment<'a>> {
1781    c.as_ref().map(|c| av_comment(arena, c))
1782}
1783
1784fn av_string_parts<'a>(
1785    arena: &'a bumpalo::Bump,
1786    parts: &[StringPart],
1787) -> arena_ast::ArenaVec<'a, arena_ast::StringPart<'a, 'a>> {
1788    let mut v = arena_ast::ArenaVec::with_capacity_in(parts.len(), arena);
1789    for p in parts.iter() {
1790        v.push(match p {
1791            StringPart::Literal(s) => arena_ast::StringPart::Literal(arena.alloc_str(s)),
1792            StringPart::Expr(e) => arena_ast::StringPart::Expr(av_expr(arena, e)),
1793        });
1794    }
1795    v
1796}
1797
1798fn av_expr<'a>(arena: &'a bumpalo::Bump, expr: &Expr) -> arena_ast::Expr<'a, 'a> {
1799    arena_ast::Expr {
1800        kind: av_expr_kind(arena, &expr.kind),
1801        span: expr.span,
1802    }
1803}
1804
1805fn av_exprs<'a>(
1806    arena: &'a bumpalo::Bump,
1807    exprs: &[Expr],
1808) -> arena_ast::ArenaVec<'a, arena_ast::Expr<'a, 'a>> {
1809    let mut v = arena_ast::ArenaVec::with_capacity_in(exprs.len(), arena);
1810    for e in exprs.iter() {
1811        v.push(av_expr(arena, e));
1812    }
1813    v
1814}
1815
1816fn av_namestr<'a>(arena: &'a bumpalo::Bump, s: &str) -> arena_ast::NameStr<'a, 'a> {
1817    arena_ast::NameStr::__arena(arena.alloc_str(s))
1818}
1819
1820fn av_expr_kind<'a>(arena: &'a bumpalo::Bump, k: &ExprKind) -> arena_ast::ExprKind<'a, 'a> {
1821    match k {
1822        ExprKind::Int(v) => arena_ast::ExprKind::Int(*v),
1823        ExprKind::Float(v) => arena_ast::ExprKind::Float(*v),
1824        ExprKind::String(s) => arena_ast::ExprKind::String(arena.alloc_str(s)),
1825        ExprKind::InterpolatedString(parts) => {
1826            arena_ast::ExprKind::InterpolatedString(av_string_parts(arena, parts))
1827        }
1828        ExprKind::Heredoc { label, parts } => arena_ast::ExprKind::Heredoc {
1829            label: arena.alloc_str(label),
1830            parts: av_string_parts(arena, parts),
1831        },
1832        ExprKind::Nowdoc { label, value } => arena_ast::ExprKind::Nowdoc {
1833            label: arena.alloc_str(label),
1834            value: arena.alloc_str(value),
1835        },
1836        ExprKind::ShellExec(parts) => arena_ast::ExprKind::ShellExec(av_string_parts(arena, parts)),
1837        ExprKind::Bool(v) => arena_ast::ExprKind::Bool(*v),
1838        ExprKind::Null => arena_ast::ExprKind::Null,
1839        ExprKind::Variable(s) => arena_ast::ExprKind::Variable(av_namestr(arena, s)),
1840        ExprKind::VariableVariable(inner) => {
1841            arena_ast::ExprKind::VariableVariable(arena.alloc(av_expr(arena, inner)))
1842        }
1843        ExprKind::Identifier(s) => arena_ast::ExprKind::Identifier(av_namestr(arena, s)),
1844        ExprKind::Assign(a) => arena_ast::ExprKind::Assign(arena_ast::AssignExpr {
1845            target: arena.alloc(av_expr(arena, &a.target)),
1846            op: a.op,
1847            value: arena.alloc(av_expr(arena, &a.value)),
1848            by_ref: a.by_ref,
1849        }),
1850        ExprKind::Binary(b) => arena_ast::ExprKind::Binary(arena_ast::BinaryExpr {
1851            left: arena.alloc(av_expr(arena, &b.left)),
1852            op: b.op,
1853            right: arena.alloc(av_expr(arena, &b.right)),
1854        }),
1855        ExprKind::UnaryPrefix(u) => arena_ast::ExprKind::UnaryPrefix(arena_ast::UnaryPrefixExpr {
1856            op: u.op,
1857            operand: arena.alloc(av_expr(arena, &u.operand)),
1858        }),
1859        ExprKind::UnaryPostfix(u) => {
1860            arena_ast::ExprKind::UnaryPostfix(arena_ast::UnaryPostfixExpr {
1861                operand: arena.alloc(av_expr(arena, &u.operand)),
1862                op: u.op,
1863            })
1864        }
1865        ExprKind::Ternary(t) => arena_ast::ExprKind::Ternary(arena_ast::TernaryExpr {
1866            condition: arena.alloc(av_expr(arena, &t.condition)),
1867            then_expr: t
1868                .then_expr
1869                .as_ref()
1870                .map(|e| &*arena.alloc(av_expr(arena, e))),
1871            else_expr: arena.alloc(av_expr(arena, &t.else_expr)),
1872        }),
1873        ExprKind::NullCoalesce(n) => {
1874            arena_ast::ExprKind::NullCoalesce(arena_ast::NullCoalesceExpr {
1875                left: arena.alloc(av_expr(arena, &n.left)),
1876                right: arena.alloc(av_expr(arena, &n.right)),
1877            })
1878        }
1879        ExprKind::FunctionCall(f) => {
1880            arena_ast::ExprKind::FunctionCall(arena_ast::FunctionCallExpr {
1881                name: arena.alloc(av_expr(arena, &f.name)),
1882                args: av_args(arena, &f.args),
1883            })
1884        }
1885        ExprKind::Array(elems) => {
1886            let mut v = arena_ast::ArenaVec::with_capacity_in(elems.len(), arena);
1887            for e in elems.iter() {
1888                v.push(arena_ast::ArrayElement {
1889                    key: e.key.as_ref().map(|k| av_expr(arena, k)),
1890                    value: av_expr(arena, &e.value),
1891                    unpack: e.unpack,
1892                    by_ref: e.by_ref,
1893                    span: e.span,
1894                });
1895            }
1896            arena_ast::ExprKind::Array(v)
1897        }
1898        ExprKind::ArrayAccess(a) => arena_ast::ExprKind::ArrayAccess(arena_ast::ArrayAccessExpr {
1899            array: arena.alloc(av_expr(arena, &a.array)),
1900            index: a.index.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
1901        }),
1902        ExprKind::Print(e) => arena_ast::ExprKind::Print(arena.alloc(av_expr(arena, e))),
1903        ExprKind::Parenthesized(e) => {
1904            arena_ast::ExprKind::Parenthesized(arena.alloc(av_expr(arena, e)))
1905        }
1906        ExprKind::Cast(kind, e) => arena_ast::ExprKind::Cast(*kind, arena.alloc(av_expr(arena, e))),
1907        ExprKind::ErrorSuppress(e) => {
1908            arena_ast::ExprKind::ErrorSuppress(arena.alloc(av_expr(arena, e)))
1909        }
1910        ExprKind::Isset(exprs) => arena_ast::ExprKind::Isset(av_exprs(arena, exprs)),
1911        ExprKind::Empty(e) => arena_ast::ExprKind::Empty(arena.alloc(av_expr(arena, e))),
1912        ExprKind::Include(kind, e) => {
1913            arena_ast::ExprKind::Include(*kind, arena.alloc(av_expr(arena, e)))
1914        }
1915        ExprKind::Eval(e) => arena_ast::ExprKind::Eval(arena.alloc(av_expr(arena, e))),
1916        ExprKind::Exit(e) => {
1917            arena_ast::ExprKind::Exit(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
1918        }
1919        ExprKind::MagicConst(m) => arena_ast::ExprKind::MagicConst(*m),
1920        ExprKind::Clone(e) => arena_ast::ExprKind::Clone(arena.alloc(av_expr(arena, e))),
1921        ExprKind::CloneWith(obj, props) => arena_ast::ExprKind::CloneWith(
1922            arena.alloc(av_expr(arena, obj)),
1923            arena.alloc(av_expr(arena, props)),
1924        ),
1925        ExprKind::New(n) => arena_ast::ExprKind::New(arena_ast::NewExpr {
1926            class: arena.alloc(av_expr(arena, &n.class)),
1927            args: av_args(arena, &n.args),
1928        }),
1929        ExprKind::PropertyAccess(p) => {
1930            arena_ast::ExprKind::PropertyAccess(arena_ast::PropertyAccessExpr {
1931                object: arena.alloc(av_expr(arena, &p.object)),
1932                property: arena.alloc(av_expr(arena, &p.property)),
1933            })
1934        }
1935        ExprKind::NullsafePropertyAccess(p) => {
1936            arena_ast::ExprKind::NullsafePropertyAccess(arena_ast::PropertyAccessExpr {
1937                object: arena.alloc(av_expr(arena, &p.object)),
1938                property: arena.alloc(av_expr(arena, &p.property)),
1939            })
1940        }
1941        ExprKind::MethodCall(m) => {
1942            arena_ast::ExprKind::MethodCall(arena.alloc(arena_ast::MethodCallExpr {
1943                object: arena.alloc(av_expr(arena, &m.object)),
1944                method: arena.alloc(av_expr(arena, &m.method)),
1945                args: av_args(arena, &m.args),
1946            }))
1947        }
1948        ExprKind::NullsafeMethodCall(m) => {
1949            arena_ast::ExprKind::NullsafeMethodCall(arena.alloc(arena_ast::MethodCallExpr {
1950                object: arena.alloc(av_expr(arena, &m.object)),
1951                method: arena.alloc(av_expr(arena, &m.method)),
1952                args: av_args(arena, &m.args),
1953            }))
1954        }
1955        ExprKind::StaticPropertyAccess(s) => {
1956            arena_ast::ExprKind::StaticPropertyAccess(arena_ast::StaticAccessExpr {
1957                class: arena.alloc(av_expr(arena, &s.class)),
1958                member: arena.alloc(av_expr(arena, &s.member)),
1959            })
1960        }
1961        ExprKind::StaticMethodCall(s) => {
1962            arena_ast::ExprKind::StaticMethodCall(arena.alloc(arena_ast::StaticMethodCallExpr {
1963                class: arena.alloc(av_expr(arena, &s.class)),
1964                method: arena.alloc(av_expr(arena, &s.method)),
1965                args: av_args(arena, &s.args),
1966            }))
1967        }
1968        ExprKind::StaticDynMethodCall(s) => arena_ast::ExprKind::StaticDynMethodCall(arena.alloc(
1969            arena_ast::StaticDynMethodCallExpr {
1970                class: arena.alloc(av_expr(arena, &s.class)),
1971                method: arena.alloc(av_expr(arena, &s.method)),
1972                args: av_args(arena, &s.args),
1973            },
1974        )),
1975        ExprKind::ClassConstAccess(s) => {
1976            arena_ast::ExprKind::ClassConstAccess(arena_ast::StaticAccessExpr {
1977                class: arena.alloc(av_expr(arena, &s.class)),
1978                member: arena.alloc(av_expr(arena, &s.member)),
1979            })
1980        }
1981        ExprKind::ClassConstAccessDynamic { class, member } => {
1982            arena_ast::ExprKind::ClassConstAccessDynamic {
1983                class: arena.alloc(av_expr(arena, class)),
1984                member: arena.alloc(av_expr(arena, member)),
1985            }
1986        }
1987        ExprKind::StaticPropertyAccessDynamic { class, member } => {
1988            arena_ast::ExprKind::StaticPropertyAccessDynamic {
1989                class: arena.alloc(av_expr(arena, class)),
1990                member: arena.alloc(av_expr(arena, member)),
1991            }
1992        }
1993        ExprKind::Closure(c) => arena_ast::ExprKind::Closure(arena.alloc(arena_ast::ClosureExpr {
1994            is_static: c.is_static,
1995            by_ref: c.by_ref,
1996            params: av_params(arena, &c.params),
1997            use_vars: {
1998                let mut v = arena_ast::ArenaVec::with_capacity_in(c.use_vars.len(), arena);
1999                for uv in c.use_vars.iter() {
2000                    v.push(arena_ast::ClosureUseVar {
2001                        name: arena.alloc_str(&uv.name),
2002                        by_ref: uv.by_ref,
2003                        span: uv.span,
2004                    });
2005                }
2006                v
2007            },
2008            return_type: c.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2009            body: av_stmts(arena, &c.body),
2010            attributes: av_attrs(arena, &c.attributes),
2011        })),
2012        ExprKind::ArrowFunction(f) => {
2013            arena_ast::ExprKind::ArrowFunction(arena.alloc(arena_ast::ArrowFunctionExpr {
2014                is_static: f.is_static,
2015                by_ref: f.by_ref,
2016                params: av_params(arena, &f.params),
2017                return_type: f.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2018                body: arena.alloc(av_expr(arena, &f.body)),
2019                attributes: av_attrs(arena, &f.attributes),
2020            }))
2021        }
2022        ExprKind::Match(m) => arena_ast::ExprKind::Match(arena_ast::MatchExpr {
2023            subject: arena.alloc(av_expr(arena, &m.subject)),
2024            arms: {
2025                let mut v = arena_ast::ArenaVec::with_capacity_in(m.arms.len(), arena);
2026                for arm in m.arms.iter() {
2027                    v.push(arena_ast::MatchArm {
2028                        conditions: arm.conditions.as_ref().map(|conds| av_exprs(arena, conds)),
2029                        body: av_expr(arena, &arm.body),
2030                        span: arm.span,
2031                    });
2032                }
2033                v
2034            },
2035        }),
2036        ExprKind::ThrowExpr(e) => arena_ast::ExprKind::ThrowExpr(arena.alloc(av_expr(arena, e))),
2037        ExprKind::Yield(y) => arena_ast::ExprKind::Yield(arena_ast::YieldExpr {
2038            key: y.key.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
2039            value: y.value.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
2040            is_from: y.is_from,
2041        }),
2042        ExprKind::AnonymousClass(cls) => {
2043            arena_ast::ExprKind::AnonymousClass(arena.alloc(av_class_decl(arena, cls)))
2044        }
2045        ExprKind::CallableCreate(c) => {
2046            arena_ast::ExprKind::CallableCreate(arena_ast::CallableCreateExpr {
2047                kind: match &c.kind {
2048                    CallableCreateKind::Function(e) => {
2049                        arena_ast::CallableCreateKind::Function(arena.alloc(av_expr(arena, e)))
2050                    }
2051                    CallableCreateKind::Method { object, method } => {
2052                        arena_ast::CallableCreateKind::Method {
2053                            object: arena.alloc(av_expr(arena, object)),
2054                            method: arena.alloc(av_expr(arena, method)),
2055                        }
2056                    }
2057                    CallableCreateKind::NullsafeMethod { object, method } => {
2058                        arena_ast::CallableCreateKind::NullsafeMethod {
2059                            object: arena.alloc(av_expr(arena, object)),
2060                            method: arena.alloc(av_expr(arena, method)),
2061                        }
2062                    }
2063                    CallableCreateKind::StaticMethod { class, method } => {
2064                        arena_ast::CallableCreateKind::StaticMethod {
2065                            class: arena.alloc(av_expr(arena, class)),
2066                            method: arena.alloc(av_expr(arena, method)),
2067                        }
2068                    }
2069                },
2070            })
2071        }
2072        ExprKind::Omit => arena_ast::ExprKind::Omit,
2073        ExprKind::Error => arena_ast::ExprKind::Error,
2074    }
2075}
2076
2077fn av_param<'a>(arena: &'a bumpalo::Bump, p: &Param) -> arena_ast::Param<'a, 'a> {
2078    arena_ast::Param {
2079        name: av_ident(arena, &p.name),
2080        type_hint: p.type_hint.as_ref().map(|t| av_type_hint(arena, t)),
2081        default: p.default.as_ref().map(|e| av_expr(arena, e)),
2082        by_ref: p.by_ref,
2083        variadic: p.variadic,
2084        is_readonly: p.is_readonly,
2085        is_final: p.is_final,
2086        visibility: p.visibility,
2087        set_visibility: p.set_visibility,
2088        attributes: av_attrs(arena, &p.attributes),
2089        hooks: av_hooks(arena, &p.hooks),
2090        span: p.span,
2091    }
2092}
2093
2094fn av_params<'a>(
2095    arena: &'a bumpalo::Bump,
2096    params: &[Param],
2097) -> arena_ast::ArenaVec<'a, arena_ast::Param<'a, 'a>> {
2098    let mut v = arena_ast::ArenaVec::with_capacity_in(params.len(), arena);
2099    for p in params.iter() {
2100        v.push(av_param(arena, p));
2101    }
2102    v
2103}
2104
2105fn av_hook<'a>(arena: &'a bumpalo::Bump, h: &PropertyHook) -> arena_ast::PropertyHook<'a, 'a> {
2106    arena_ast::PropertyHook {
2107        kind: h.kind,
2108        body: match &h.body {
2109            PropertyHookBody::Block(stmts) => {
2110                arena_ast::PropertyHookBody::Block(av_stmts(arena, stmts))
2111            }
2112            PropertyHookBody::Expression(e) => {
2113                arena_ast::PropertyHookBody::Expression(av_expr(arena, e))
2114            }
2115            PropertyHookBody::Abstract => arena_ast::PropertyHookBody::Abstract,
2116        },
2117        is_final: h.is_final,
2118        by_ref: h.by_ref,
2119        params: av_params(arena, &h.params),
2120        attributes: av_attrs(arena, &h.attributes),
2121        span: h.span,
2122    }
2123}
2124
2125fn av_hooks<'a>(
2126    arena: &'a bumpalo::Bump,
2127    hooks: &[PropertyHook],
2128) -> arena_ast::ArenaVec<'a, arena_ast::PropertyHook<'a, 'a>> {
2129    let mut v = arena_ast::ArenaVec::with_capacity_in(hooks.len(), arena);
2130    for h in hooks.iter() {
2131        v.push(av_hook(arena, h));
2132    }
2133    v
2134}
2135
2136fn av_stmt<'a>(arena: &'a bumpalo::Bump, stmt: &Stmt) -> arena_ast::Stmt<'a, 'a> {
2137    arena_ast::Stmt {
2138        kind: av_stmt_kind(arena, &stmt.kind),
2139        span: stmt.span,
2140    }
2141}
2142
2143fn av_stmts<'a>(
2144    arena: &'a bumpalo::Bump,
2145    stmts: &[Stmt],
2146) -> arena_ast::ArenaVec<'a, arena_ast::Stmt<'a, 'a>> {
2147    let mut v = arena_ast::ArenaVec::with_capacity_in(stmts.len(), arena);
2148    for s in stmts.iter() {
2149        v.push(av_stmt(arena, s));
2150    }
2151    v
2152}
2153
2154fn av_stmt_kind<'a>(arena: &'a bumpalo::Bump, k: &StmtKind) -> arena_ast::StmtKind<'a, 'a> {
2155    match k {
2156        StmtKind::Expression(e) => arena_ast::StmtKind::Expression(arena.alloc(av_expr(arena, e))),
2157        StmtKind::Echo(exprs) => arena_ast::StmtKind::Echo(av_exprs(arena, exprs)),
2158        StmtKind::Return(e) => {
2159            arena_ast::StmtKind::Return(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2160        }
2161        StmtKind::Block(stmts) => arena_ast::StmtKind::Block(av_stmts(arena, stmts)),
2162        StmtKind::If(s) => arena_ast::StmtKind::If(
2163            arena.alloc(arena_ast::IfStmt {
2164                condition: av_expr(arena, &s.condition),
2165                then_branch: arena.alloc(av_stmt(arena, &s.then_branch)),
2166                elseif_branches: {
2167                    let mut v =
2168                        arena_ast::ArenaVec::with_capacity_in(s.elseif_branches.len(), arena);
2169                    for b in s.elseif_branches.iter() {
2170                        v.push(arena_ast::ElseIfBranch {
2171                            condition: av_expr(arena, &b.condition),
2172                            body: av_stmt(arena, &b.body),
2173                            span: b.span,
2174                        });
2175                    }
2176                    v
2177                },
2178                else_branch: s
2179                    .else_branch
2180                    .as_ref()
2181                    .map(|b| &*arena.alloc(av_stmt(arena, b))),
2182                uses_alternative: s.uses_alternative,
2183            }),
2184        ),
2185        StmtKind::While(s) => arena_ast::StmtKind::While(arena.alloc(arena_ast::WhileStmt {
2186            condition: av_expr(arena, &s.condition),
2187            body: arena.alloc(av_stmt(arena, &s.body)),
2188            uses_alternative: s.uses_alternative,
2189        })),
2190        StmtKind::For(s) => arena_ast::StmtKind::For(arena.alloc(arena_ast::ForStmt {
2191            init: av_exprs(arena, &s.init),
2192            condition: av_exprs(arena, &s.condition),
2193            update: av_exprs(arena, &s.update),
2194            body: arena.alloc(av_stmt(arena, &s.body)),
2195            uses_alternative: s.uses_alternative,
2196        })),
2197        StmtKind::Foreach(s) => arena_ast::StmtKind::Foreach(arena.alloc(arena_ast::ForeachStmt {
2198            expr: av_expr(arena, &s.expr),
2199            key: s.key.as_ref().map(|e| av_expr(arena, e)),
2200            value: av_expr(arena, &s.value),
2201            body: arena.alloc(av_stmt(arena, &s.body)),
2202            uses_alternative: s.uses_alternative,
2203        })),
2204        StmtKind::DoWhile(s) => arena_ast::StmtKind::DoWhile(arena.alloc(arena_ast::DoWhileStmt {
2205            body: arena.alloc(av_stmt(arena, &s.body)),
2206            condition: av_expr(arena, &s.condition),
2207        })),
2208        StmtKind::Function(f) => {
2209            arena_ast::StmtKind::Function(arena.alloc(av_function_decl(arena, f)))
2210        }
2211        StmtKind::Break(e) => {
2212            arena_ast::StmtKind::Break(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2213        }
2214        StmtKind::Continue(e) => {
2215            arena_ast::StmtKind::Continue(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2216        }
2217        StmtKind::Switch(s) => arena_ast::StmtKind::Switch(arena.alloc(arena_ast::SwitchStmt {
2218            expr: av_expr(arena, &s.expr),
2219            cases: {
2220                let mut v = arena_ast::ArenaVec::with_capacity_in(s.cases.len(), arena);
2221                for c in s.cases.iter() {
2222                    v.push(arena_ast::SwitchCase {
2223                        value: c.value.as_ref().map(|vl| av_expr(arena, vl)),
2224                        body: av_stmts(arena, &c.body),
2225                        span: c.span,
2226                    });
2227                }
2228                v
2229            },
2230            uses_alternative: s.uses_alternative,
2231        })),
2232        StmtKind::Goto(ident) => arena_ast::StmtKind::Goto(av_ident(arena, ident)),
2233        StmtKind::Label(s) => arena_ast::StmtKind::Label(av_str(arena, s)),
2234        StmtKind::Declare(d) => arena_ast::StmtKind::Declare(arena.alloc(arena_ast::DeclareStmt {
2235            directives: {
2236                let mut v = arena_ast::ArenaVec::with_capacity_in(d.directives.len(), arena);
2237                for (k, val) in d.directives.iter() {
2238                    v.push((arena.alloc_str(k) as &str, av_expr(arena, val)));
2239                }
2240                v
2241            },
2242            body: d.body.as_ref().map(|b| &*arena.alloc(av_stmt(arena, b))),
2243            uses_alternative: d.uses_alternative,
2244        })),
2245        StmtKind::Unset(exprs) => arena_ast::StmtKind::Unset(av_exprs(arena, exprs)),
2246        StmtKind::Throw(e) => arena_ast::StmtKind::Throw(arena.alloc(av_expr(arena, e))),
2247        StmtKind::TryCatch(t) => {
2248            arena_ast::StmtKind::TryCatch(arena.alloc(arena_ast::TryCatchStmt {
2249                body: av_stmts(arena, &t.body),
2250                catches: {
2251                    let mut v = arena_ast::ArenaVec::with_capacity_in(t.catches.len(), arena);
2252                    for c in t.catches.iter() {
2253                        let mut types = arena_ast::ArenaVec::with_capacity_in(c.types.len(), arena);
2254                        for n in c.types.iter() {
2255                            types.push(av_name(arena, n));
2256                        }
2257                        v.push(arena_ast::CatchClause {
2258                            types,
2259                            var: c.var.as_deref().map(|s| av_str(arena, s)),
2260                            body: av_stmts(arena, &c.body),
2261                            span: c.span,
2262                        });
2263                    }
2264                    v
2265                },
2266                finally: t.finally.as_ref().map(|stmts| av_stmts(arena, stmts)),
2267            }))
2268        }
2269        StmtKind::Global(exprs) => arena_ast::StmtKind::Global(av_exprs(arena, exprs)),
2270        StmtKind::Class(cls) => arena_ast::StmtKind::Class(arena.alloc(av_class_decl(arena, cls))),
2271        StmtKind::Interface(iface) => {
2272            arena_ast::StmtKind::Interface(arena.alloc(av_interface_decl(arena, iface)))
2273        }
2274        StmtKind::Trait(tr) => arena_ast::StmtKind::Trait(arena.alloc(av_trait_decl(arena, tr))),
2275        StmtKind::Enum(en) => arena_ast::StmtKind::Enum(arena.alloc(av_enum_decl(arena, en))),
2276        StmtKind::Namespace(ns) => {
2277            arena_ast::StmtKind::Namespace(arena.alloc(arena_ast::NamespaceDecl {
2278                name: ns.name.as_ref().map(|n| av_name(arena, n)),
2279                body: match &ns.body {
2280                    NamespaceBody::Braced(stmts) => {
2281                        arena_ast::NamespaceBody::Braced(av_stmts(arena, stmts))
2282                    }
2283                    NamespaceBody::Simple => arena_ast::NamespaceBody::Simple,
2284                },
2285            }))
2286        }
2287        StmtKind::Use(u) => arena_ast::StmtKind::Use(arena.alloc(arena_ast::UseDecl {
2288            kind: u.kind,
2289            uses: {
2290                let mut v = arena_ast::ArenaVec::with_capacity_in(u.uses.len(), arena);
2291                for item in u.uses.iter() {
2292                    v.push(arena_ast::UseItem {
2293                        name: av_name(arena, &item.name),
2294                        alias: item.alias.as_deref().map(|s| av_str(arena, s)),
2295                        kind: item.kind,
2296                        span: item.span,
2297                    });
2298                }
2299                v
2300            },
2301        })),
2302        StmtKind::Const(items) => {
2303            let mut v = arena_ast::ArenaVec::with_capacity_in(items.len(), arena);
2304            for item in items.iter() {
2305                v.push(arena_ast::ConstItem {
2306                    name: av_ident(arena, &item.name),
2307                    value: av_expr(arena, &item.value),
2308                    attributes: av_attrs(arena, &item.attributes),
2309                    span: item.span,
2310                    doc_comment: av_opt_comment(arena, &item.doc_comment),
2311                });
2312            }
2313            arena_ast::StmtKind::Const(v)
2314        }
2315        StmtKind::StaticVar(vars) => {
2316            let mut v = arena_ast::ArenaVec::with_capacity_in(vars.len(), arena);
2317            for var in vars.iter() {
2318                v.push(arena_ast::StaticVar {
2319                    name: av_ident(arena, &var.name),
2320                    default: var.default.as_ref().map(|e| av_expr(arena, e)),
2321                    span: var.span,
2322                });
2323            }
2324            arena_ast::StmtKind::StaticVar(v)
2325        }
2326        StmtKind::HaltCompiler(s) => arena_ast::StmtKind::HaltCompiler(av_str(arena, s)),
2327        StmtKind::Nop => arena_ast::StmtKind::Nop,
2328        StmtKind::InlineHtml(s) => arena_ast::StmtKind::InlineHtml(av_str(arena, s)),
2329        StmtKind::Error => arena_ast::StmtKind::Error,
2330    }
2331}
2332
2333fn av_function_decl<'a>(
2334    arena: &'a bumpalo::Bump,
2335    f: &FunctionDecl,
2336) -> arena_ast::FunctionDecl<'a, 'a> {
2337    arena_ast::FunctionDecl {
2338        name: av_ident(arena, &f.name),
2339        params: av_params(arena, &f.params),
2340        body: av_stmts(arena, &f.body),
2341        return_type: f.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2342        by_ref: f.by_ref,
2343        attributes: av_attrs(arena, &f.attributes),
2344        doc_comment: av_opt_comment(arena, &f.doc_comment),
2345    }
2346}
2347
2348fn av_class_member<'a>(
2349    arena: &'a bumpalo::Bump,
2350    m: &ClassMember,
2351) -> arena_ast::ClassMember<'a, 'a> {
2352    arena_ast::ClassMember {
2353        kind: match &m.kind {
2354            ClassMemberKind::Property(p) => {
2355                arena_ast::ClassMemberKind::Property(arena_ast::PropertyDecl {
2356                    name: av_ident(arena, &p.name),
2357                    visibility: p.visibility,
2358                    set_visibility: p.set_visibility,
2359                    is_static: p.is_static,
2360                    is_readonly: p.is_readonly,
2361                    type_hint: p.type_hint.as_ref().map(|t| av_type_hint(arena, t)),
2362                    default: p.default.as_ref().map(|e| av_expr(arena, e)),
2363                    attributes: av_attrs(arena, &p.attributes),
2364                    hooks: av_hooks(arena, &p.hooks),
2365                    doc_comment: av_opt_comment(arena, &p.doc_comment),
2366                })
2367            }
2368            ClassMemberKind::Method(m) => {
2369                arena_ast::ClassMemberKind::Method(arena_ast::MethodDecl {
2370                    name: av_ident(arena, &m.name),
2371                    visibility: m.visibility,
2372                    is_static: m.is_static,
2373                    is_abstract: m.is_abstract,
2374                    is_final: m.is_final,
2375                    by_ref: m.by_ref,
2376                    params: av_params(arena, &m.params),
2377                    return_type: m.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2378                    body: m.body.as_ref().map(|stmts| av_stmts(arena, stmts)),
2379                    attributes: av_attrs(arena, &m.attributes),
2380                    doc_comment: av_opt_comment(arena, &m.doc_comment),
2381                })
2382            }
2383            ClassMemberKind::ClassConst(c) => {
2384                arena_ast::ClassMemberKind::ClassConst(av_class_const(arena, c))
2385            }
2386            ClassMemberKind::TraitUse(t) => {
2387                arena_ast::ClassMemberKind::TraitUse(av_trait_use(arena, t))
2388            }
2389        },
2390        span: m.span,
2391    }
2392}
2393
2394fn av_class_members<'a>(
2395    arena: &'a bumpalo::Bump,
2396    members: &[ClassMember],
2397) -> arena_ast::ArenaVec<'a, arena_ast::ClassMember<'a, 'a>> {
2398    let mut v = arena_ast::ArenaVec::with_capacity_in(members.len(), arena);
2399    for m in members.iter() {
2400        v.push(av_class_member(arena, m));
2401    }
2402    v
2403}
2404
2405fn av_class_const<'a>(
2406    arena: &'a bumpalo::Bump,
2407    c: &ClassConstDecl,
2408) -> arena_ast::ClassConstDecl<'a, 'a> {
2409    arena_ast::ClassConstDecl {
2410        name: av_ident(arena, &c.name),
2411        visibility: c.visibility,
2412        is_final: c.is_final,
2413        type_hint: c
2414            .type_hint
2415            .as_ref()
2416            .map(|th| arena.alloc(av_type_hint(arena, th)) as &_),
2417        value: av_expr(arena, &c.value),
2418        attributes: av_attrs(arena, &c.attributes),
2419        doc_comment: av_opt_comment(arena, &c.doc_comment),
2420    }
2421}
2422
2423fn av_trait_use<'a>(arena: &'a bumpalo::Bump, t: &TraitUseDecl) -> arena_ast::TraitUseDecl<'a, 'a> {
2424    let mut traits = arena_ast::ArenaVec::with_capacity_in(t.traits.len(), arena);
2425    for n in t.traits.iter() {
2426        traits.push(av_name(arena, n));
2427    }
2428    let mut adaptations = arena_ast::ArenaVec::with_capacity_in(t.adaptations.len(), arena);
2429    for a in t.adaptations.iter() {
2430        adaptations.push(arena_ast::TraitAdaptation {
2431            kind: match &a.kind {
2432                TraitAdaptationKind::Precedence {
2433                    trait_name,
2434                    method,
2435                    insteadof,
2436                } => {
2437                    let mut instead = arena_ast::ArenaVec::with_capacity_in(insteadof.len(), arena);
2438                    for n in insteadof.iter() {
2439                        instead.push(av_name(arena, n));
2440                    }
2441                    arena_ast::TraitAdaptationKind::Precedence {
2442                        trait_name: av_name(arena, trait_name),
2443                        method: av_name(arena, method),
2444                        insteadof: instead,
2445                    }
2446                }
2447                TraitAdaptationKind::Alias {
2448                    trait_name,
2449                    method,
2450                    new_modifier,
2451                    new_name,
2452                } => arena_ast::TraitAdaptationKind::Alias {
2453                    trait_name: trait_name.as_ref().map(|n| av_name(arena, n)),
2454                    method: av_name(arena, method),
2455                    new_modifier: *new_modifier,
2456                    new_name: new_name.as_ref().map(|n| av_name(arena, n)),
2457                },
2458            },
2459            span: a.span,
2460        });
2461    }
2462    arena_ast::TraitUseDecl {
2463        traits,
2464        adaptations,
2465    }
2466}
2467
2468fn av_class_decl<'a>(arena: &'a bumpalo::Bump, cls: &ClassDecl) -> arena_ast::ClassDecl<'a, 'a> {
2469    let mut implements = arena_ast::ArenaVec::with_capacity_in(cls.implements.len(), arena);
2470    for n in cls.implements.iter() {
2471        implements.push(av_name(arena, n));
2472    }
2473    arena_ast::ClassDecl {
2474        name: cls.name.as_ref().map(|ident| av_ident(arena, ident)),
2475        modifiers: cls.modifiers.clone(),
2476        extends: cls.extends.as_ref().map(|n| av_name(arena, n)),
2477        implements,
2478        members: av_class_members(arena, &cls.members),
2479        attributes: av_attrs(arena, &cls.attributes),
2480        doc_comment: av_opt_comment(arena, &cls.doc_comment),
2481    }
2482}
2483
2484fn av_interface_decl<'a>(
2485    arena: &'a bumpalo::Bump,
2486    iface: &InterfaceDecl,
2487) -> arena_ast::InterfaceDecl<'a, 'a> {
2488    let mut extends = arena_ast::ArenaVec::with_capacity_in(iface.extends.len(), arena);
2489    for n in iface.extends.iter() {
2490        extends.push(av_name(arena, n));
2491    }
2492    arena_ast::InterfaceDecl {
2493        name: av_ident(arena, &iface.name),
2494        extends,
2495        members: av_class_members(arena, &iface.members),
2496        attributes: av_attrs(arena, &iface.attributes),
2497        doc_comment: av_opt_comment(arena, &iface.doc_comment),
2498    }
2499}
2500
2501fn av_trait_decl<'a>(arena: &'a bumpalo::Bump, tr: &TraitDecl) -> arena_ast::TraitDecl<'a, 'a> {
2502    arena_ast::TraitDecl {
2503        name: av_ident(arena, &tr.name),
2504        members: av_class_members(arena, &tr.members),
2505        attributes: av_attrs(arena, &tr.attributes),
2506        doc_comment: av_opt_comment(arena, &tr.doc_comment),
2507    }
2508}
2509
2510fn av_enum_decl<'a>(arena: &'a bumpalo::Bump, en: &EnumDecl) -> arena_ast::EnumDecl<'a, 'a> {
2511    let mut implements = arena_ast::ArenaVec::with_capacity_in(en.implements.len(), arena);
2512    for n in en.implements.iter() {
2513        implements.push(av_name(arena, n));
2514    }
2515    let mut members = arena_ast::ArenaVec::with_capacity_in(en.members.len(), arena);
2516    for m in en.members.iter() {
2517        members.push(arena_ast::EnumMember {
2518            kind: match &m.kind {
2519                EnumMemberKind::Case(c) => arena_ast::EnumMemberKind::Case(arena_ast::EnumCase {
2520                    name: av_ident(arena, &c.name),
2521                    value: c.value.as_ref().map(|e| av_expr(arena, e)),
2522                    attributes: av_attrs(arena, &c.attributes),
2523                    doc_comment: av_opt_comment(arena, &c.doc_comment),
2524                }),
2525                EnumMemberKind::Method(m) => {
2526                    arena_ast::EnumMemberKind::Method(arena_ast::MethodDecl {
2527                        name: av_ident(arena, &m.name),
2528                        visibility: m.visibility,
2529                        is_static: m.is_static,
2530                        is_abstract: m.is_abstract,
2531                        is_final: m.is_final,
2532                        by_ref: m.by_ref,
2533                        params: av_params(arena, &m.params),
2534                        return_type: m.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2535                        body: m.body.as_ref().map(|stmts| av_stmts(arena, stmts)),
2536                        attributes: av_attrs(arena, &m.attributes),
2537                        doc_comment: av_opt_comment(arena, &m.doc_comment),
2538                    })
2539                }
2540                EnumMemberKind::ClassConst(c) => {
2541                    arena_ast::EnumMemberKind::ClassConst(av_class_const(arena, c))
2542                }
2543                EnumMemberKind::TraitUse(t) => {
2544                    arena_ast::EnumMemberKind::TraitUse(av_trait_use(arena, t))
2545                }
2546            },
2547            span: m.span,
2548        });
2549    }
2550    arena_ast::EnumDecl {
2551        name: av_ident(arena, &en.name),
2552        scalar_type: en.scalar_type.as_ref().map(|n| av_name(arena, n)),
2553        implements,
2554        members,
2555        attributes: av_attrs(arena, &en.attributes),
2556        doc_comment: av_opt_comment(arena, &en.doc_comment),
2557    }
2558}