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
947/// Convert an arena-allocated [`Name`](crate::ast::Name) into a [`Name`].
948pub fn to_owned_name(name: &arena_ast::Name<'_, '_>) -> Name {
949    owned_name(name)
950}
951
952/// Convert an arena-allocated [`TypeHint`](crate::ast::TypeHint) into a [`TypeHint`].
953pub fn to_owned_type_hint(hint: &arena_ast::TypeHint<'_, '_>) -> TypeHint {
954    owned_type_hint(hint)
955}
956
957fn owned_expr(expr: &arena_ast::Expr<'_, '_>) -> Expr {
958    Expr {
959        kind: owned_expr_kind(&expr.kind),
960        span: expr.span,
961    }
962}
963
964fn owned_exprs(exprs: &[arena_ast::Expr<'_, '_>]) -> Box<[Expr]> {
965    exprs
966        .iter()
967        .map(owned_expr)
968        .collect::<Vec<_>>()
969        .into_boxed_slice()
970}
971
972fn owned_expr_kind(k: &arena_ast::ExprKind<'_, '_>) -> ExprKind {
973    match k {
974        arena_ast::ExprKind::Int(v) => ExprKind::Int(*v),
975        arena_ast::ExprKind::Float(v) => ExprKind::Float(*v),
976        arena_ast::ExprKind::String(s) => ExprKind::String(Box::from(*s)),
977        arena_ast::ExprKind::InterpolatedString(parts) => {
978            ExprKind::InterpolatedString(owned_string_parts(parts))
979        }
980        arena_ast::ExprKind::Heredoc { label, parts } => ExprKind::Heredoc {
981            label: Box::from(*label),
982            parts: owned_string_parts(parts),
983        },
984        arena_ast::ExprKind::Nowdoc { label, value } => ExprKind::Nowdoc {
985            label: Box::from(*label),
986            value: Box::from(*value),
987        },
988        arena_ast::ExprKind::ShellExec(parts) => ExprKind::ShellExec(owned_string_parts(parts)),
989        arena_ast::ExprKind::Bool(v) => ExprKind::Bool(*v),
990        arena_ast::ExprKind::Null => ExprKind::Null,
991        arena_ast::ExprKind::Variable(s) => ExprKind::Variable(s.as_str().into()),
992        arena_ast::ExprKind::VariableVariable(inner) => {
993            ExprKind::VariableVariable(Box::new(owned_expr(inner)))
994        }
995        arena_ast::ExprKind::Identifier(s) => ExprKind::Identifier(s.as_str().into()),
996        arena_ast::ExprKind::Assign(a) => ExprKind::Assign(AssignExpr {
997            target: Box::new(owned_expr(a.target)),
998            op: a.op,
999            value: Box::new(owned_expr(a.value)),
1000            by_ref: a.by_ref,
1001        }),
1002        arena_ast::ExprKind::Binary(b) => ExprKind::Binary(BinaryExpr {
1003            left: Box::new(owned_expr(b.left)),
1004            op: b.op,
1005            right: Box::new(owned_expr(b.right)),
1006        }),
1007        arena_ast::ExprKind::UnaryPrefix(u) => ExprKind::UnaryPrefix(UnaryPrefixExpr {
1008            op: u.op,
1009            operand: Box::new(owned_expr(u.operand)),
1010        }),
1011        arena_ast::ExprKind::UnaryPostfix(u) => ExprKind::UnaryPostfix(UnaryPostfixExpr {
1012            operand: Box::new(owned_expr(u.operand)),
1013            op: u.op,
1014        }),
1015        arena_ast::ExprKind::Ternary(t) => ExprKind::Ternary(TernaryExpr {
1016            condition: Box::new(owned_expr(t.condition)),
1017            then_expr: t.then_expr.map(|e| Box::new(owned_expr(e))),
1018            else_expr: Box::new(owned_expr(t.else_expr)),
1019        }),
1020        arena_ast::ExprKind::NullCoalesce(n) => ExprKind::NullCoalesce(NullCoalesceExpr {
1021            left: Box::new(owned_expr(n.left)),
1022            right: Box::new(owned_expr(n.right)),
1023        }),
1024        arena_ast::ExprKind::FunctionCall(f) => ExprKind::FunctionCall(FunctionCallExpr {
1025            name: Box::new(owned_expr(f.name)),
1026            args: owned_args(&f.args),
1027        }),
1028        arena_ast::ExprKind::Array(elems) => ExprKind::Array(
1029            elems
1030                .iter()
1031                .map(|e| ArrayElement {
1032                    key: e.key.as_ref().map(owned_expr),
1033                    value: owned_expr(&e.value),
1034                    unpack: e.unpack,
1035                    by_ref: e.by_ref,
1036                    span: e.span,
1037                })
1038                .collect::<Vec<_>>()
1039                .into_boxed_slice(),
1040        ),
1041        arena_ast::ExprKind::ArrayAccess(a) => ExprKind::ArrayAccess(ArrayAccessExpr {
1042            array: Box::new(owned_expr(a.array)),
1043            index: a.index.map(|e| Box::new(owned_expr(e))),
1044        }),
1045        arena_ast::ExprKind::Print(e) => ExprKind::Print(Box::new(owned_expr(e))),
1046        arena_ast::ExprKind::Parenthesized(e) => ExprKind::Parenthesized(Box::new(owned_expr(e))),
1047        arena_ast::ExprKind::Cast(kind, e) => ExprKind::Cast(*kind, Box::new(owned_expr(e))),
1048        arena_ast::ExprKind::ErrorSuppress(e) => ExprKind::ErrorSuppress(Box::new(owned_expr(e))),
1049        arena_ast::ExprKind::Isset(exprs) => ExprKind::Isset(owned_exprs(exprs)),
1050        arena_ast::ExprKind::Empty(e) => ExprKind::Empty(Box::new(owned_expr(e))),
1051        arena_ast::ExprKind::Include(kind, e) => ExprKind::Include(*kind, Box::new(owned_expr(e))),
1052        arena_ast::ExprKind::Eval(e) => ExprKind::Eval(Box::new(owned_expr(e))),
1053        arena_ast::ExprKind::Exit(e) => ExprKind::Exit(e.map(|e| Box::new(owned_expr(e)))),
1054        arena_ast::ExprKind::MagicConst(m) => ExprKind::MagicConst(*m),
1055        arena_ast::ExprKind::Clone(e) => ExprKind::Clone(Box::new(owned_expr(e))),
1056        arena_ast::ExprKind::CloneWith(obj, props) => {
1057            ExprKind::CloneWith(Box::new(owned_expr(obj)), Box::new(owned_expr(props)))
1058        }
1059        arena_ast::ExprKind::New(n) => ExprKind::New(NewExpr {
1060            class: Box::new(owned_expr(n.class)),
1061            args: owned_args(&n.args),
1062        }),
1063        arena_ast::ExprKind::PropertyAccess(p) => ExprKind::PropertyAccess(PropertyAccessExpr {
1064            object: Box::new(owned_expr(p.object)),
1065            property: Box::new(owned_expr(p.property)),
1066        }),
1067        arena_ast::ExprKind::NullsafePropertyAccess(p) => {
1068            ExprKind::NullsafePropertyAccess(PropertyAccessExpr {
1069                object: Box::new(owned_expr(p.object)),
1070                property: Box::new(owned_expr(p.property)),
1071            })
1072        }
1073        arena_ast::ExprKind::MethodCall(m) => ExprKind::MethodCall(Box::new(MethodCallExpr {
1074            object: Box::new(owned_expr(m.object)),
1075            method: Box::new(owned_expr(m.method)),
1076            args: owned_args(&m.args),
1077        })),
1078        arena_ast::ExprKind::NullsafeMethodCall(m) => {
1079            ExprKind::NullsafeMethodCall(Box::new(MethodCallExpr {
1080                object: Box::new(owned_expr(m.object)),
1081                method: Box::new(owned_expr(m.method)),
1082                args: owned_args(&m.args),
1083            }))
1084        }
1085        arena_ast::ExprKind::StaticPropertyAccess(s) => {
1086            ExprKind::StaticPropertyAccess(StaticAccessExpr {
1087                class: Box::new(owned_expr(s.class)),
1088                member: Box::new(owned_expr(s.member)),
1089            })
1090        }
1091        arena_ast::ExprKind::StaticMethodCall(s) => {
1092            ExprKind::StaticMethodCall(Box::new(StaticMethodCallExpr {
1093                class: Box::new(owned_expr(s.class)),
1094                method: Box::new(owned_expr(s.method)),
1095                args: owned_args(&s.args),
1096            }))
1097        }
1098        arena_ast::ExprKind::StaticDynMethodCall(s) => {
1099            ExprKind::StaticDynMethodCall(Box::new(StaticDynMethodCallExpr {
1100                class: Box::new(owned_expr(s.class)),
1101                method: Box::new(owned_expr(s.method)),
1102                args: owned_args(&s.args),
1103            }))
1104        }
1105        arena_ast::ExprKind::ClassConstAccess(s) => ExprKind::ClassConstAccess(StaticAccessExpr {
1106            class: Box::new(owned_expr(s.class)),
1107            member: Box::new(owned_expr(s.member)),
1108        }),
1109        arena_ast::ExprKind::ClassConstAccessDynamic { class, member } => {
1110            ExprKind::ClassConstAccessDynamic {
1111                class: Box::new(owned_expr(class)),
1112                member: Box::new(owned_expr(member)),
1113            }
1114        }
1115        arena_ast::ExprKind::StaticPropertyAccessDynamic { class, member } => {
1116            ExprKind::StaticPropertyAccessDynamic {
1117                class: Box::new(owned_expr(class)),
1118                member: Box::new(owned_expr(member)),
1119            }
1120        }
1121        arena_ast::ExprKind::Closure(c) => ExprKind::Closure(Box::new(ClosureExpr {
1122            is_static: c.is_static,
1123            by_ref: c.by_ref,
1124            params: owned_params(&c.params),
1125            use_vars: c
1126                .use_vars
1127                .iter()
1128                .map(|v| ClosureUseVar {
1129                    name: v.name.into(),
1130                    by_ref: v.by_ref,
1131                    span: v.span,
1132                })
1133                .collect::<Vec<_>>()
1134                .into_boxed_slice(),
1135            return_type: c.return_type.as_ref().map(owned_type_hint),
1136            body: owned_stmts(&c.body),
1137            attributes: owned_attrs(&c.attributes),
1138        })),
1139        arena_ast::ExprKind::ArrowFunction(f) => {
1140            ExprKind::ArrowFunction(Box::new(ArrowFunctionExpr {
1141                is_static: f.is_static,
1142                by_ref: f.by_ref,
1143                params: owned_params(&f.params),
1144                return_type: f.return_type.as_ref().map(owned_type_hint),
1145                body: Box::new(owned_expr(f.body)),
1146                attributes: owned_attrs(&f.attributes),
1147            }))
1148        }
1149        arena_ast::ExprKind::Match(m) => ExprKind::Match(MatchExpr {
1150            subject: Box::new(owned_expr(m.subject)),
1151            arms: m
1152                .arms
1153                .iter()
1154                .map(|arm| MatchArm {
1155                    conditions: arm.conditions.as_ref().map(|conds| owned_exprs(conds)),
1156                    body: owned_expr(&arm.body),
1157                    span: arm.span,
1158                })
1159                .collect::<Vec<_>>()
1160                .into_boxed_slice(),
1161        }),
1162        arena_ast::ExprKind::ThrowExpr(e) => ExprKind::ThrowExpr(Box::new(owned_expr(e))),
1163        arena_ast::ExprKind::Yield(y) => ExprKind::Yield(YieldExpr {
1164            key: y.key.map(|e| Box::new(owned_expr(e))),
1165            value: y.value.map(|e| Box::new(owned_expr(e))),
1166            is_from: y.is_from,
1167        }),
1168        arena_ast::ExprKind::AnonymousClass(cls) => {
1169            ExprKind::AnonymousClass(Box::new(owned_class_decl(cls)))
1170        }
1171        arena_ast::ExprKind::CallableCreate(c) => ExprKind::CallableCreate(CallableCreateExpr {
1172            kind: match &c.kind {
1173                arena_ast::CallableCreateKind::Function(e) => {
1174                    CallableCreateKind::Function(Box::new(owned_expr(e)))
1175                }
1176                arena_ast::CallableCreateKind::Method { object, method } => {
1177                    CallableCreateKind::Method {
1178                        object: Box::new(owned_expr(object)),
1179                        method: Box::new(owned_expr(method)),
1180                    }
1181                }
1182                arena_ast::CallableCreateKind::NullsafeMethod { object, method } => {
1183                    CallableCreateKind::NullsafeMethod {
1184                        object: Box::new(owned_expr(object)),
1185                        method: Box::new(owned_expr(method)),
1186                    }
1187                }
1188                arena_ast::CallableCreateKind::StaticMethod { class, method } => {
1189                    CallableCreateKind::StaticMethod {
1190                        class: Box::new(owned_expr(class)),
1191                        method: Box::new(owned_expr(method)),
1192                    }
1193                }
1194            },
1195        }),
1196        arena_ast::ExprKind::Omit => ExprKind::Omit,
1197        arena_ast::ExprKind::Error => ExprKind::Error,
1198    }
1199}
1200
1201fn owned_param(p: &arena_ast::Param<'_, '_>) -> Param {
1202    Param {
1203        name: owned_ident(p.name),
1204        type_hint: p.type_hint.as_ref().map(owned_type_hint),
1205        default: p.default.as_ref().map(owned_expr),
1206        by_ref: p.by_ref,
1207        variadic: p.variadic,
1208        is_readonly: p.is_readonly,
1209        is_final: p.is_final,
1210        visibility: p.visibility,
1211        set_visibility: p.set_visibility,
1212        attributes: owned_attrs(&p.attributes),
1213        hooks: owned_hooks(&p.hooks),
1214        span: p.span,
1215    }
1216}
1217
1218fn owned_params(params: &[arena_ast::Param<'_, '_>]) -> Box<[Param]> {
1219    params
1220        .iter()
1221        .map(owned_param)
1222        .collect::<Vec<_>>()
1223        .into_boxed_slice()
1224}
1225
1226fn owned_hook(h: &arena_ast::PropertyHook<'_, '_>) -> PropertyHook {
1227    PropertyHook {
1228        kind: h.kind,
1229        body: match &h.body {
1230            arena_ast::PropertyHookBody::Block(stmts) => {
1231                PropertyHookBody::Block(owned_stmts(stmts))
1232            }
1233            arena_ast::PropertyHookBody::Expression(e) => {
1234                PropertyHookBody::Expression(owned_expr(e))
1235            }
1236            arena_ast::PropertyHookBody::Abstract => PropertyHookBody::Abstract,
1237        },
1238        is_final: h.is_final,
1239        by_ref: h.by_ref,
1240        params: owned_params(&h.params),
1241        attributes: owned_attrs(&h.attributes),
1242        span: h.span,
1243    }
1244}
1245
1246fn owned_hooks(hooks: &[arena_ast::PropertyHook<'_, '_>]) -> Box<[PropertyHook]> {
1247    hooks
1248        .iter()
1249        .map(owned_hook)
1250        .collect::<Vec<_>>()
1251        .into_boxed_slice()
1252}
1253
1254fn owned_stmts(stmts: &[arena_ast::Stmt<'_, '_>]) -> Box<[Stmt]> {
1255    stmts
1256        .iter()
1257        .map(owned_stmt)
1258        .collect::<Vec<_>>()
1259        .into_boxed_slice()
1260}
1261
1262/// Convert an arena-allocated [`Stmt`](crate::ast::Stmt) into a [`Stmt`].
1263pub fn to_owned_stmt(stmt: &arena_ast::Stmt<'_, '_>) -> Stmt {
1264    owned_stmt(stmt)
1265}
1266
1267fn owned_stmt(stmt: &arena_ast::Stmt<'_, '_>) -> Stmt {
1268    Stmt {
1269        kind: owned_stmt_kind(&stmt.kind),
1270        span: stmt.span,
1271    }
1272}
1273
1274fn owned_stmt_kind(k: &arena_ast::StmtKind<'_, '_>) -> StmtKind {
1275    match k {
1276        arena_ast::StmtKind::Expression(e) => StmtKind::Expression(Box::new(owned_expr(e))),
1277        arena_ast::StmtKind::Echo(exprs) => StmtKind::Echo(owned_exprs(exprs)),
1278        arena_ast::StmtKind::Return(e) => StmtKind::Return(e.map(|e| Box::new(owned_expr(e)))),
1279        arena_ast::StmtKind::Block(stmts) => StmtKind::Block(owned_stmts(stmts)),
1280        arena_ast::StmtKind::If(s) => StmtKind::If(Box::new(IfStmt {
1281            condition: owned_expr(&s.condition),
1282            then_branch: Box::new(owned_stmt(s.then_branch)),
1283            elseif_branches: s
1284                .elseif_branches
1285                .iter()
1286                .map(|b| ElseIfBranch {
1287                    condition: owned_expr(&b.condition),
1288                    body: owned_stmt(&b.body),
1289                    span: b.span,
1290                })
1291                .collect::<Vec<_>>()
1292                .into_boxed_slice(),
1293            else_branch: s.else_branch.map(|b| Box::new(owned_stmt(b))),
1294            uses_alternative: s.uses_alternative,
1295        })),
1296        arena_ast::StmtKind::While(s) => StmtKind::While(Box::new(WhileStmt {
1297            condition: owned_expr(&s.condition),
1298            body: Box::new(owned_stmt(s.body)),
1299            uses_alternative: s.uses_alternative,
1300        })),
1301        arena_ast::StmtKind::For(s) => StmtKind::For(Box::new(ForStmt {
1302            init: owned_exprs(&s.init),
1303            condition: owned_exprs(&s.condition),
1304            update: owned_exprs(&s.update),
1305            body: Box::new(owned_stmt(s.body)),
1306            uses_alternative: s.uses_alternative,
1307        })),
1308        arena_ast::StmtKind::Foreach(s) => StmtKind::Foreach(Box::new(ForeachStmt {
1309            expr: owned_expr(&s.expr),
1310            key: s.key.as_ref().map(owned_expr),
1311            value: owned_expr(&s.value),
1312            body: Box::new(owned_stmt(s.body)),
1313            uses_alternative: s.uses_alternative,
1314        })),
1315        arena_ast::StmtKind::DoWhile(s) => StmtKind::DoWhile(Box::new(DoWhileStmt {
1316            body: Box::new(owned_stmt(s.body)),
1317            condition: owned_expr(&s.condition),
1318        })),
1319        arena_ast::StmtKind::Function(f) => StmtKind::Function(Box::new(owned_function_decl(f))),
1320        arena_ast::StmtKind::Break(e) => StmtKind::Break(e.map(|e| Box::new(owned_expr(e)))),
1321        arena_ast::StmtKind::Continue(e) => StmtKind::Continue(e.map(|e| Box::new(owned_expr(e)))),
1322        arena_ast::StmtKind::Switch(s) => StmtKind::Switch(Box::new(SwitchStmt {
1323            expr: owned_expr(&s.expr),
1324            cases: s
1325                .cases
1326                .iter()
1327                .map(|c| SwitchCase {
1328                    value: c.value.as_ref().map(owned_expr),
1329                    body: owned_stmts(&c.body),
1330                    span: c.span,
1331                })
1332                .collect::<Vec<_>>()
1333                .into_boxed_slice(),
1334            uses_alternative: s.uses_alternative,
1335        })),
1336        arena_ast::StmtKind::Goto(ident) => StmtKind::Goto(owned_ident(*ident)),
1337        arena_ast::StmtKind::Label(s) => StmtKind::Label(Box::from(*s)),
1338        arena_ast::StmtKind::Declare(d) => StmtKind::Declare(Box::new(DeclareStmt {
1339            directives: d
1340                .directives
1341                .iter()
1342                .map(|(k, v)| (Box::from(*k), owned_expr(v)))
1343                .collect::<Vec<_>>()
1344                .into_boxed_slice(),
1345            body: d.body.map(|b| Box::new(owned_stmt(b))),
1346            uses_alternative: d.uses_alternative,
1347        })),
1348        arena_ast::StmtKind::Unset(exprs) => StmtKind::Unset(owned_exprs(exprs)),
1349        arena_ast::StmtKind::Throw(e) => StmtKind::Throw(Box::new(owned_expr(e))),
1350        arena_ast::StmtKind::TryCatch(t) => StmtKind::TryCatch(Box::new(TryCatchStmt {
1351            body: owned_stmts(&t.body),
1352            catches: t
1353                .catches
1354                .iter()
1355                .map(|c| CatchClause {
1356                    types: c
1357                        .types
1358                        .iter()
1359                        .map(owned_name)
1360                        .collect::<Vec<_>>()
1361                        .into_boxed_slice(),
1362                    var: c.var.map(Box::from),
1363                    body: owned_stmts(&c.body),
1364                    span: c.span,
1365                })
1366                .collect::<Vec<_>>()
1367                .into_boxed_slice(),
1368            finally: t.finally.as_ref().map(|stmts| owned_stmts(stmts)),
1369        })),
1370        arena_ast::StmtKind::Global(exprs) => StmtKind::Global(owned_exprs(exprs)),
1371        arena_ast::StmtKind::Class(cls) => StmtKind::Class(Box::new(owned_class_decl(cls))),
1372        arena_ast::StmtKind::Interface(iface) => {
1373            StmtKind::Interface(Box::new(owned_interface_decl(iface)))
1374        }
1375        arena_ast::StmtKind::Trait(tr) => StmtKind::Trait(Box::new(owned_trait_decl(tr))),
1376        arena_ast::StmtKind::Enum(en) => StmtKind::Enum(Box::new(owned_enum_decl(en))),
1377        arena_ast::StmtKind::Namespace(ns) => StmtKind::Namespace(Box::new(NamespaceDecl {
1378            name: ns.name.as_ref().map(owned_name),
1379            body: match &ns.body {
1380                arena_ast::NamespaceBody::Braced(stmts) => {
1381                    NamespaceBody::Braced(owned_stmts(stmts))
1382                }
1383                arena_ast::NamespaceBody::Simple => NamespaceBody::Simple,
1384            },
1385        })),
1386        arena_ast::StmtKind::Use(u) => StmtKind::Use(Box::new(UseDecl {
1387            kind: u.kind,
1388            uses: u
1389                .uses
1390                .iter()
1391                .map(|item| UseItem {
1392                    name: owned_name(&item.name),
1393                    alias: item.alias.map(Box::from),
1394                    kind: item.kind,
1395                    span: item.span,
1396                })
1397                .collect::<Vec<_>>()
1398                .into_boxed_slice(),
1399        })),
1400        arena_ast::StmtKind::Const(items) => StmtKind::Const(
1401            items
1402                .iter()
1403                .map(|item| ConstItem {
1404                    name: owned_ident(item.name),
1405                    value: owned_expr(&item.value),
1406                    attributes: owned_attrs(&item.attributes),
1407                    span: item.span,
1408                    doc_comment: owned_opt_comment(&item.doc_comment),
1409                })
1410                .collect::<Vec<_>>()
1411                .into_boxed_slice(),
1412        ),
1413        arena_ast::StmtKind::StaticVar(vars) => StmtKind::StaticVar(
1414            vars.iter()
1415                .map(|v| StaticVar {
1416                    name: owned_ident(v.name),
1417                    default: v.default.as_ref().map(owned_expr),
1418                    span: v.span,
1419                })
1420                .collect::<Vec<_>>()
1421                .into_boxed_slice(),
1422        ),
1423        arena_ast::StmtKind::HaltCompiler(s) => StmtKind::HaltCompiler(Box::from(*s)),
1424        arena_ast::StmtKind::Nop => StmtKind::Nop,
1425        arena_ast::StmtKind::InlineHtml(s) => StmtKind::InlineHtml(Box::from(*s)),
1426        arena_ast::StmtKind::Error => StmtKind::Error,
1427    }
1428}
1429
1430fn owned_function_decl(f: &arena_ast::FunctionDecl<'_, '_>) -> FunctionDecl {
1431    FunctionDecl {
1432        name: owned_ident(f.name),
1433        params: owned_params(&f.params),
1434        body: owned_stmts(&f.body),
1435        return_type: f.return_type.as_ref().map(owned_type_hint),
1436        by_ref: f.by_ref,
1437        attributes: owned_attrs(&f.attributes),
1438        doc_comment: owned_opt_comment(&f.doc_comment),
1439    }
1440}
1441
1442fn owned_class_member(m: &arena_ast::ClassMember<'_, '_>) -> ClassMember {
1443    ClassMember {
1444        kind: match &m.kind {
1445            arena_ast::ClassMemberKind::Property(p) => ClassMemberKind::Property(PropertyDecl {
1446                name: owned_ident(p.name),
1447                visibility: p.visibility,
1448                set_visibility: p.set_visibility,
1449                is_static: p.is_static,
1450                is_readonly: p.is_readonly,
1451                type_hint: p.type_hint.as_ref().map(owned_type_hint),
1452                default: p.default.as_ref().map(owned_expr),
1453                attributes: owned_attrs(&p.attributes),
1454                hooks: owned_hooks(&p.hooks),
1455                doc_comment: owned_opt_comment(&p.doc_comment),
1456            }),
1457            arena_ast::ClassMemberKind::Method(m) => ClassMemberKind::Method(MethodDecl {
1458                name: owned_ident(m.name),
1459                visibility: m.visibility,
1460                is_static: m.is_static,
1461                is_abstract: m.is_abstract,
1462                is_final: m.is_final,
1463                by_ref: m.by_ref,
1464                params: owned_params(&m.params),
1465                return_type: m.return_type.as_ref().map(owned_type_hint),
1466                body: m.body.as_ref().map(|stmts| owned_stmts(stmts)),
1467                attributes: owned_attrs(&m.attributes),
1468                doc_comment: owned_opt_comment(&m.doc_comment),
1469            }),
1470            arena_ast::ClassMemberKind::ClassConst(c) => {
1471                ClassMemberKind::ClassConst(owned_class_const(c))
1472            }
1473            arena_ast::ClassMemberKind::TraitUse(t) => {
1474                ClassMemberKind::TraitUse(owned_trait_use(t))
1475            }
1476        },
1477        span: m.span,
1478    }
1479}
1480
1481fn owned_class_members(members: &[arena_ast::ClassMember<'_, '_>]) -> Box<[ClassMember]> {
1482    members
1483        .iter()
1484        .map(owned_class_member)
1485        .collect::<Vec<_>>()
1486        .into_boxed_slice()
1487}
1488
1489fn owned_class_const(c: &arena_ast::ClassConstDecl<'_, '_>) -> ClassConstDecl {
1490    ClassConstDecl {
1491        name: owned_ident(c.name),
1492        visibility: c.visibility,
1493        is_final: c.is_final,
1494        type_hint: c.type_hint.map(|th| Box::new(owned_type_hint(th))),
1495        value: owned_expr(&c.value),
1496        attributes: owned_attrs(&c.attributes),
1497        doc_comment: owned_opt_comment(&c.doc_comment),
1498    }
1499}
1500
1501fn owned_trait_use(t: &arena_ast::TraitUseDecl<'_, '_>) -> TraitUseDecl {
1502    TraitUseDecl {
1503        traits: t
1504            .traits
1505            .iter()
1506            .map(owned_name)
1507            .collect::<Vec<_>>()
1508            .into_boxed_slice(),
1509        adaptations: t
1510            .adaptations
1511            .iter()
1512            .map(|a| TraitAdaptation {
1513                kind: match &a.kind {
1514                    arena_ast::TraitAdaptationKind::Precedence {
1515                        trait_name,
1516                        method,
1517                        insteadof,
1518                    } => TraitAdaptationKind::Precedence {
1519                        trait_name: owned_name(trait_name),
1520                        method: owned_name(method),
1521                        insteadof: insteadof
1522                            .iter()
1523                            .map(owned_name)
1524                            .collect::<Vec<_>>()
1525                            .into_boxed_slice(),
1526                    },
1527                    arena_ast::TraitAdaptationKind::Alias {
1528                        trait_name,
1529                        method,
1530                        new_modifier,
1531                        new_name,
1532                    } => TraitAdaptationKind::Alias {
1533                        trait_name: trait_name.as_ref().map(owned_name),
1534                        method: owned_name(method),
1535                        new_modifier: *new_modifier,
1536                        new_name: new_name.as_ref().map(owned_name),
1537                    },
1538                },
1539                span: a.span,
1540            })
1541            .collect::<Vec<_>>()
1542            .into_boxed_slice(),
1543    }
1544}
1545
1546fn owned_class_decl(cls: &arena_ast::ClassDecl<'_, '_>) -> ClassDecl {
1547    ClassDecl {
1548        name: cls
1549            .name
1550            .map(|ident| Some(owned_ident(ident)))
1551            .unwrap_or(None),
1552        modifiers: cls.modifiers.clone(),
1553        extends: cls.extends.as_ref().map(owned_name),
1554        implements: cls
1555            .implements
1556            .iter()
1557            .map(owned_name)
1558            .collect::<Vec<_>>()
1559            .into_boxed_slice(),
1560        members: owned_class_members(&cls.members),
1561        attributes: owned_attrs(&cls.attributes),
1562        doc_comment: owned_opt_comment(&cls.doc_comment),
1563    }
1564}
1565
1566fn owned_interface_decl(iface: &arena_ast::InterfaceDecl<'_, '_>) -> InterfaceDecl {
1567    InterfaceDecl {
1568        name: owned_ident(iface.name),
1569        extends: iface
1570            .extends
1571            .iter()
1572            .map(owned_name)
1573            .collect::<Vec<_>>()
1574            .into_boxed_slice(),
1575        members: owned_class_members(&iface.members),
1576        attributes: owned_attrs(&iface.attributes),
1577        doc_comment: owned_opt_comment(&iface.doc_comment),
1578    }
1579}
1580
1581fn owned_trait_decl(tr: &arena_ast::TraitDecl<'_, '_>) -> TraitDecl {
1582    TraitDecl {
1583        name: owned_ident(tr.name),
1584        members: owned_class_members(&tr.members),
1585        attributes: owned_attrs(&tr.attributes),
1586        doc_comment: owned_opt_comment(&tr.doc_comment),
1587    }
1588}
1589
1590fn owned_enum_decl(en: &arena_ast::EnumDecl<'_, '_>) -> EnumDecl {
1591    EnumDecl {
1592        name: owned_ident(en.name),
1593        scalar_type: en.scalar_type.as_ref().map(owned_name),
1594        implements: en
1595            .implements
1596            .iter()
1597            .map(owned_name)
1598            .collect::<Vec<_>>()
1599            .into_boxed_slice(),
1600        members: en
1601            .members
1602            .iter()
1603            .map(|m| EnumMember {
1604                kind: match &m.kind {
1605                    arena_ast::EnumMemberKind::Case(c) => EnumMemberKind::Case(EnumCase {
1606                        name: owned_ident(c.name),
1607                        value: c.value.as_ref().map(owned_expr),
1608                        attributes: owned_attrs(&c.attributes),
1609                        doc_comment: owned_opt_comment(&c.doc_comment),
1610                    }),
1611                    arena_ast::EnumMemberKind::Method(m) => EnumMemberKind::Method(MethodDecl {
1612                        name: owned_ident(m.name),
1613                        visibility: m.visibility,
1614                        is_static: m.is_static,
1615                        is_abstract: m.is_abstract,
1616                        is_final: m.is_final,
1617                        by_ref: m.by_ref,
1618                        params: owned_params(&m.params),
1619                        return_type: m.return_type.as_ref().map(owned_type_hint),
1620                        body: m.body.as_ref().map(|stmts| owned_stmts(stmts)),
1621                        attributes: owned_attrs(&m.attributes),
1622                        doc_comment: owned_opt_comment(&m.doc_comment),
1623                    }),
1624                    arena_ast::EnumMemberKind::ClassConst(c) => {
1625                        EnumMemberKind::ClassConst(owned_class_const(c))
1626                    }
1627                    arena_ast::EnumMemberKind::TraitUse(t) => {
1628                        EnumMemberKind::TraitUse(owned_trait_use(t))
1629                    }
1630                },
1631                span: m.span,
1632            })
1633            .collect::<Vec<_>>()
1634            .into_boxed_slice(),
1635        attributes: owned_attrs(&en.attributes),
1636        doc_comment: owned_opt_comment(&en.doc_comment),
1637    }
1638}
1639
1640/// Convert an arena-allocated [`Program`](crate::ast::Program) into a [`Program`].
1641pub fn to_owned_program(program: &arena_ast::Program<'_, '_>) -> Program {
1642    Program {
1643        stmts: owned_stmts(&program.stmts),
1644        span: program.span,
1645    }
1646}
1647
1648// ===========================================================================
1649// Reverse conversion: owned → arena
1650// ===========================================================================
1651
1652/// Convert an owned [`Program`] into an arena-allocated
1653/// [`Program`](crate::ast::Program).
1654///
1655/// All strings are copied into `arena`. Both lifetime parameters of the
1656/// returned type are `'arena` because every string originates in the arena
1657/// rather than a source buffer.
1658///
1659/// Used internally by the pretty printer so callers can pass an owned program
1660/// without round-tripping through `parse_arena()`.
1661pub fn from_owned_program<'arena>(
1662    arena: &'arena bumpalo::Bump,
1663    program: &Program,
1664) -> arena_ast::Program<'arena, 'arena> {
1665    arena_ast::Program {
1666        stmts: av_stmts(arena, &program.stmts),
1667        span: program.span,
1668    }
1669}
1670
1671// Helper: allocate a string in the arena.
1672#[inline]
1673fn av_str<'a>(arena: &'a bumpalo::Bump, s: &str) -> &'a str {
1674    arena.alloc_str(s)
1675}
1676
1677fn av_ident<'a>(arena: &'a bumpalo::Bump, ident: &Ident) -> arena_ast::Ident<'a> {
1678    match ident {
1679        Some(s) => arena_ast::Ident::name(arena.alloc_str(s)),
1680        None => arena_ast::Ident::ERROR,
1681    }
1682}
1683
1684fn av_name<'a>(arena: &'a bumpalo::Bump, name: &Name) -> arena_ast::Name<'a, 'a> {
1685    if name.kind == NameKind::Error {
1686        return arena_ast::Name::Error { span: name.span };
1687    }
1688    if name.parts.len() == 1 && name.kind == NameKind::Unqualified {
1689        return arena_ast::Name::Simple {
1690            value: arena.alloc_str(&name.parts[0]),
1691            span: name.span,
1692        };
1693    }
1694    let mut parts = arena_ast::ArenaVec::with_capacity_in(name.parts.len(), arena);
1695    for p in name.parts.iter() {
1696        parts.push(arena.alloc_str(p) as &str);
1697    }
1698    arena_ast::Name::Complex {
1699        parts,
1700        kind: name.kind,
1701        span: name.span,
1702    }
1703}
1704
1705fn av_type_hint<'a>(arena: &'a bumpalo::Bump, th: &TypeHint) -> arena_ast::TypeHint<'a, 'a> {
1706    arena_ast::TypeHint {
1707        kind: av_type_hint_kind(arena, &th.kind),
1708        span: th.span,
1709    }
1710}
1711
1712fn av_type_hint_kind<'a>(
1713    arena: &'a bumpalo::Bump,
1714    k: &TypeHintKind,
1715) -> arena_ast::TypeHintKind<'a, 'a> {
1716    match k {
1717        TypeHintKind::Named(n) => arena_ast::TypeHintKind::Named(av_name(arena, n)),
1718        TypeHintKind::Keyword(b, span) => arena_ast::TypeHintKind::Keyword(*b, *span),
1719        TypeHintKind::Nullable(inner) => {
1720            arena_ast::TypeHintKind::Nullable(arena.alloc(av_type_hint(arena, inner)))
1721        }
1722        TypeHintKind::Union(types) => {
1723            let mut v = arena_ast::ArenaVec::with_capacity_in(types.len(), arena);
1724            for t in types.iter() {
1725                v.push(av_type_hint(arena, t));
1726            }
1727            arena_ast::TypeHintKind::Union(v)
1728        }
1729        TypeHintKind::Intersection(types) => {
1730            let mut v = arena_ast::ArenaVec::with_capacity_in(types.len(), arena);
1731            for t in types.iter() {
1732                v.push(av_type_hint(arena, t));
1733            }
1734            arena_ast::TypeHintKind::Intersection(v)
1735        }
1736    }
1737}
1738
1739fn av_arg<'a>(arena: &'a bumpalo::Bump, arg: &Arg) -> arena_ast::Arg<'a, 'a> {
1740    arena_ast::Arg {
1741        name: arg.name.as_ref().map(|n| av_name(arena, n)),
1742        value: av_expr(arena, &arg.value),
1743        unpack: arg.unpack,
1744        by_ref: arg.by_ref,
1745        span: arg.span,
1746    }
1747}
1748
1749fn av_args<'a>(
1750    arena: &'a bumpalo::Bump,
1751    args: &[Arg],
1752) -> arena_ast::ArenaVec<'a, arena_ast::Arg<'a, 'a>> {
1753    let mut v = arena_ast::ArenaVec::with_capacity_in(args.len(), arena);
1754    for a in args.iter() {
1755        v.push(av_arg(arena, a));
1756    }
1757    v
1758}
1759
1760fn av_attr<'a>(arena: &'a bumpalo::Bump, attr: &Attribute) -> arena_ast::Attribute<'a, 'a> {
1761    arena_ast::Attribute {
1762        name: av_name(arena, &attr.name),
1763        args: av_args(arena, &attr.args),
1764        span: attr.span,
1765    }
1766}
1767
1768fn av_attrs<'a>(
1769    arena: &'a bumpalo::Bump,
1770    attrs: &[Attribute],
1771) -> arena_ast::ArenaVec<'a, arena_ast::Attribute<'a, 'a>> {
1772    let mut v = arena_ast::ArenaVec::with_capacity_in(attrs.len(), arena);
1773    for a in attrs.iter() {
1774        v.push(av_attr(arena, a));
1775    }
1776    v
1777}
1778
1779fn av_comment<'a>(arena: &'a bumpalo::Bump, c: &Comment) -> arena_ast::Comment<'a> {
1780    arena_ast::Comment {
1781        kind: c.kind,
1782        text: arena.alloc_str(&c.text),
1783        span: c.span,
1784    }
1785}
1786
1787fn av_opt_comment<'a>(
1788    arena: &'a bumpalo::Bump,
1789    c: &Option<Comment>,
1790) -> Option<arena_ast::Comment<'a>> {
1791    c.as_ref().map(|c| av_comment(arena, c))
1792}
1793
1794fn av_string_parts<'a>(
1795    arena: &'a bumpalo::Bump,
1796    parts: &[StringPart],
1797) -> arena_ast::ArenaVec<'a, arena_ast::StringPart<'a, 'a>> {
1798    let mut v = arena_ast::ArenaVec::with_capacity_in(parts.len(), arena);
1799    for p in parts.iter() {
1800        v.push(match p {
1801            StringPart::Literal(s) => arena_ast::StringPart::Literal(arena.alloc_str(s)),
1802            StringPart::Expr(e) => arena_ast::StringPart::Expr(av_expr(arena, e)),
1803        });
1804    }
1805    v
1806}
1807
1808fn av_expr<'a>(arena: &'a bumpalo::Bump, expr: &Expr) -> arena_ast::Expr<'a, 'a> {
1809    arena_ast::Expr {
1810        kind: av_expr_kind(arena, &expr.kind),
1811        span: expr.span,
1812    }
1813}
1814
1815fn av_exprs<'a>(
1816    arena: &'a bumpalo::Bump,
1817    exprs: &[Expr],
1818) -> arena_ast::ArenaVec<'a, arena_ast::Expr<'a, 'a>> {
1819    let mut v = arena_ast::ArenaVec::with_capacity_in(exprs.len(), arena);
1820    for e in exprs.iter() {
1821        v.push(av_expr(arena, e));
1822    }
1823    v
1824}
1825
1826fn av_namestr<'a>(arena: &'a bumpalo::Bump, s: &str) -> arena_ast::NameStr<'a, 'a> {
1827    arena_ast::NameStr::__arena(arena.alloc_str(s))
1828}
1829
1830fn av_expr_kind<'a>(arena: &'a bumpalo::Bump, k: &ExprKind) -> arena_ast::ExprKind<'a, 'a> {
1831    match k {
1832        ExprKind::Int(v) => arena_ast::ExprKind::Int(*v),
1833        ExprKind::Float(v) => arena_ast::ExprKind::Float(*v),
1834        ExprKind::String(s) => arena_ast::ExprKind::String(arena.alloc_str(s)),
1835        ExprKind::InterpolatedString(parts) => {
1836            arena_ast::ExprKind::InterpolatedString(av_string_parts(arena, parts))
1837        }
1838        ExprKind::Heredoc { label, parts } => arena_ast::ExprKind::Heredoc {
1839            label: arena.alloc_str(label),
1840            parts: av_string_parts(arena, parts),
1841        },
1842        ExprKind::Nowdoc { label, value } => arena_ast::ExprKind::Nowdoc {
1843            label: arena.alloc_str(label),
1844            value: arena.alloc_str(value),
1845        },
1846        ExprKind::ShellExec(parts) => arena_ast::ExprKind::ShellExec(av_string_parts(arena, parts)),
1847        ExprKind::Bool(v) => arena_ast::ExprKind::Bool(*v),
1848        ExprKind::Null => arena_ast::ExprKind::Null,
1849        ExprKind::Variable(s) => arena_ast::ExprKind::Variable(av_namestr(arena, s)),
1850        ExprKind::VariableVariable(inner) => {
1851            arena_ast::ExprKind::VariableVariable(arena.alloc(av_expr(arena, inner)))
1852        }
1853        ExprKind::Identifier(s) => arena_ast::ExprKind::Identifier(av_namestr(arena, s)),
1854        ExprKind::Assign(a) => arena_ast::ExprKind::Assign(arena_ast::AssignExpr {
1855            target: arena.alloc(av_expr(arena, &a.target)),
1856            op: a.op,
1857            value: arena.alloc(av_expr(arena, &a.value)),
1858            by_ref: a.by_ref,
1859        }),
1860        ExprKind::Binary(b) => arena_ast::ExprKind::Binary(arena_ast::BinaryExpr {
1861            left: arena.alloc(av_expr(arena, &b.left)),
1862            op: b.op,
1863            right: arena.alloc(av_expr(arena, &b.right)),
1864        }),
1865        ExprKind::UnaryPrefix(u) => arena_ast::ExprKind::UnaryPrefix(arena_ast::UnaryPrefixExpr {
1866            op: u.op,
1867            operand: arena.alloc(av_expr(arena, &u.operand)),
1868        }),
1869        ExprKind::UnaryPostfix(u) => {
1870            arena_ast::ExprKind::UnaryPostfix(arena_ast::UnaryPostfixExpr {
1871                operand: arena.alloc(av_expr(arena, &u.operand)),
1872                op: u.op,
1873            })
1874        }
1875        ExprKind::Ternary(t) => arena_ast::ExprKind::Ternary(arena_ast::TernaryExpr {
1876            condition: arena.alloc(av_expr(arena, &t.condition)),
1877            then_expr: t
1878                .then_expr
1879                .as_ref()
1880                .map(|e| &*arena.alloc(av_expr(arena, e))),
1881            else_expr: arena.alloc(av_expr(arena, &t.else_expr)),
1882        }),
1883        ExprKind::NullCoalesce(n) => {
1884            arena_ast::ExprKind::NullCoalesce(arena_ast::NullCoalesceExpr {
1885                left: arena.alloc(av_expr(arena, &n.left)),
1886                right: arena.alloc(av_expr(arena, &n.right)),
1887            })
1888        }
1889        ExprKind::FunctionCall(f) => {
1890            arena_ast::ExprKind::FunctionCall(arena_ast::FunctionCallExpr {
1891                name: arena.alloc(av_expr(arena, &f.name)),
1892                args: av_args(arena, &f.args),
1893            })
1894        }
1895        ExprKind::Array(elems) => {
1896            let mut v = arena_ast::ArenaVec::with_capacity_in(elems.len(), arena);
1897            for e in elems.iter() {
1898                v.push(arena_ast::ArrayElement {
1899                    key: e.key.as_ref().map(|k| av_expr(arena, k)),
1900                    value: av_expr(arena, &e.value),
1901                    unpack: e.unpack,
1902                    by_ref: e.by_ref,
1903                    span: e.span,
1904                });
1905            }
1906            arena_ast::ExprKind::Array(v)
1907        }
1908        ExprKind::ArrayAccess(a) => arena_ast::ExprKind::ArrayAccess(arena_ast::ArrayAccessExpr {
1909            array: arena.alloc(av_expr(arena, &a.array)),
1910            index: a.index.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
1911        }),
1912        ExprKind::Print(e) => arena_ast::ExprKind::Print(arena.alloc(av_expr(arena, e))),
1913        ExprKind::Parenthesized(e) => {
1914            arena_ast::ExprKind::Parenthesized(arena.alloc(av_expr(arena, e)))
1915        }
1916        ExprKind::Cast(kind, e) => arena_ast::ExprKind::Cast(*kind, arena.alloc(av_expr(arena, e))),
1917        ExprKind::ErrorSuppress(e) => {
1918            arena_ast::ExprKind::ErrorSuppress(arena.alloc(av_expr(arena, e)))
1919        }
1920        ExprKind::Isset(exprs) => arena_ast::ExprKind::Isset(av_exprs(arena, exprs)),
1921        ExprKind::Empty(e) => arena_ast::ExprKind::Empty(arena.alloc(av_expr(arena, e))),
1922        ExprKind::Include(kind, e) => {
1923            arena_ast::ExprKind::Include(*kind, arena.alloc(av_expr(arena, e)))
1924        }
1925        ExprKind::Eval(e) => arena_ast::ExprKind::Eval(arena.alloc(av_expr(arena, e))),
1926        ExprKind::Exit(e) => {
1927            arena_ast::ExprKind::Exit(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
1928        }
1929        ExprKind::MagicConst(m) => arena_ast::ExprKind::MagicConst(*m),
1930        ExprKind::Clone(e) => arena_ast::ExprKind::Clone(arena.alloc(av_expr(arena, e))),
1931        ExprKind::CloneWith(obj, props) => arena_ast::ExprKind::CloneWith(
1932            arena.alloc(av_expr(arena, obj)),
1933            arena.alloc(av_expr(arena, props)),
1934        ),
1935        ExprKind::New(n) => arena_ast::ExprKind::New(arena_ast::NewExpr {
1936            class: arena.alloc(av_expr(arena, &n.class)),
1937            args: av_args(arena, &n.args),
1938        }),
1939        ExprKind::PropertyAccess(p) => {
1940            arena_ast::ExprKind::PropertyAccess(arena_ast::PropertyAccessExpr {
1941                object: arena.alloc(av_expr(arena, &p.object)),
1942                property: arena.alloc(av_expr(arena, &p.property)),
1943            })
1944        }
1945        ExprKind::NullsafePropertyAccess(p) => {
1946            arena_ast::ExprKind::NullsafePropertyAccess(arena_ast::PropertyAccessExpr {
1947                object: arena.alloc(av_expr(arena, &p.object)),
1948                property: arena.alloc(av_expr(arena, &p.property)),
1949            })
1950        }
1951        ExprKind::MethodCall(m) => {
1952            arena_ast::ExprKind::MethodCall(arena.alloc(arena_ast::MethodCallExpr {
1953                object: arena.alloc(av_expr(arena, &m.object)),
1954                method: arena.alloc(av_expr(arena, &m.method)),
1955                args: av_args(arena, &m.args),
1956            }))
1957        }
1958        ExprKind::NullsafeMethodCall(m) => {
1959            arena_ast::ExprKind::NullsafeMethodCall(arena.alloc(arena_ast::MethodCallExpr {
1960                object: arena.alloc(av_expr(arena, &m.object)),
1961                method: arena.alloc(av_expr(arena, &m.method)),
1962                args: av_args(arena, &m.args),
1963            }))
1964        }
1965        ExprKind::StaticPropertyAccess(s) => {
1966            arena_ast::ExprKind::StaticPropertyAccess(arena_ast::StaticAccessExpr {
1967                class: arena.alloc(av_expr(arena, &s.class)),
1968                member: arena.alloc(av_expr(arena, &s.member)),
1969            })
1970        }
1971        ExprKind::StaticMethodCall(s) => {
1972            arena_ast::ExprKind::StaticMethodCall(arena.alloc(arena_ast::StaticMethodCallExpr {
1973                class: arena.alloc(av_expr(arena, &s.class)),
1974                method: arena.alloc(av_expr(arena, &s.method)),
1975                args: av_args(arena, &s.args),
1976            }))
1977        }
1978        ExprKind::StaticDynMethodCall(s) => arena_ast::ExprKind::StaticDynMethodCall(arena.alloc(
1979            arena_ast::StaticDynMethodCallExpr {
1980                class: arena.alloc(av_expr(arena, &s.class)),
1981                method: arena.alloc(av_expr(arena, &s.method)),
1982                args: av_args(arena, &s.args),
1983            },
1984        )),
1985        ExprKind::ClassConstAccess(s) => {
1986            arena_ast::ExprKind::ClassConstAccess(arena_ast::StaticAccessExpr {
1987                class: arena.alloc(av_expr(arena, &s.class)),
1988                member: arena.alloc(av_expr(arena, &s.member)),
1989            })
1990        }
1991        ExprKind::ClassConstAccessDynamic { class, member } => {
1992            arena_ast::ExprKind::ClassConstAccessDynamic {
1993                class: arena.alloc(av_expr(arena, class)),
1994                member: arena.alloc(av_expr(arena, member)),
1995            }
1996        }
1997        ExprKind::StaticPropertyAccessDynamic { class, member } => {
1998            arena_ast::ExprKind::StaticPropertyAccessDynamic {
1999                class: arena.alloc(av_expr(arena, class)),
2000                member: arena.alloc(av_expr(arena, member)),
2001            }
2002        }
2003        ExprKind::Closure(c) => arena_ast::ExprKind::Closure(arena.alloc(arena_ast::ClosureExpr {
2004            is_static: c.is_static,
2005            by_ref: c.by_ref,
2006            params: av_params(arena, &c.params),
2007            use_vars: {
2008                let mut v = arena_ast::ArenaVec::with_capacity_in(c.use_vars.len(), arena);
2009                for uv in c.use_vars.iter() {
2010                    v.push(arena_ast::ClosureUseVar {
2011                        name: arena.alloc_str(&uv.name),
2012                        by_ref: uv.by_ref,
2013                        span: uv.span,
2014                    });
2015                }
2016                v
2017            },
2018            return_type: c.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2019            body: av_stmts(arena, &c.body),
2020            attributes: av_attrs(arena, &c.attributes),
2021        })),
2022        ExprKind::ArrowFunction(f) => {
2023            arena_ast::ExprKind::ArrowFunction(arena.alloc(arena_ast::ArrowFunctionExpr {
2024                is_static: f.is_static,
2025                by_ref: f.by_ref,
2026                params: av_params(arena, &f.params),
2027                return_type: f.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2028                body: arena.alloc(av_expr(arena, &f.body)),
2029                attributes: av_attrs(arena, &f.attributes),
2030            }))
2031        }
2032        ExprKind::Match(m) => arena_ast::ExprKind::Match(arena_ast::MatchExpr {
2033            subject: arena.alloc(av_expr(arena, &m.subject)),
2034            arms: {
2035                let mut v = arena_ast::ArenaVec::with_capacity_in(m.arms.len(), arena);
2036                for arm in m.arms.iter() {
2037                    v.push(arena_ast::MatchArm {
2038                        conditions: arm.conditions.as_ref().map(|conds| av_exprs(arena, conds)),
2039                        body: av_expr(arena, &arm.body),
2040                        span: arm.span,
2041                    });
2042                }
2043                v
2044            },
2045        }),
2046        ExprKind::ThrowExpr(e) => arena_ast::ExprKind::ThrowExpr(arena.alloc(av_expr(arena, e))),
2047        ExprKind::Yield(y) => arena_ast::ExprKind::Yield(arena_ast::YieldExpr {
2048            key: y.key.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
2049            value: y.value.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
2050            is_from: y.is_from,
2051        }),
2052        ExprKind::AnonymousClass(cls) => {
2053            arena_ast::ExprKind::AnonymousClass(arena.alloc(av_class_decl(arena, cls)))
2054        }
2055        ExprKind::CallableCreate(c) => {
2056            arena_ast::ExprKind::CallableCreate(arena_ast::CallableCreateExpr {
2057                kind: match &c.kind {
2058                    CallableCreateKind::Function(e) => {
2059                        arena_ast::CallableCreateKind::Function(arena.alloc(av_expr(arena, e)))
2060                    }
2061                    CallableCreateKind::Method { object, method } => {
2062                        arena_ast::CallableCreateKind::Method {
2063                            object: arena.alloc(av_expr(arena, object)),
2064                            method: arena.alloc(av_expr(arena, method)),
2065                        }
2066                    }
2067                    CallableCreateKind::NullsafeMethod { object, method } => {
2068                        arena_ast::CallableCreateKind::NullsafeMethod {
2069                            object: arena.alloc(av_expr(arena, object)),
2070                            method: arena.alloc(av_expr(arena, method)),
2071                        }
2072                    }
2073                    CallableCreateKind::StaticMethod { class, method } => {
2074                        arena_ast::CallableCreateKind::StaticMethod {
2075                            class: arena.alloc(av_expr(arena, class)),
2076                            method: arena.alloc(av_expr(arena, method)),
2077                        }
2078                    }
2079                },
2080            })
2081        }
2082        ExprKind::Omit => arena_ast::ExprKind::Omit,
2083        ExprKind::Error => arena_ast::ExprKind::Error,
2084    }
2085}
2086
2087fn av_param<'a>(arena: &'a bumpalo::Bump, p: &Param) -> arena_ast::Param<'a, 'a> {
2088    arena_ast::Param {
2089        name: av_ident(arena, &p.name),
2090        type_hint: p.type_hint.as_ref().map(|t| av_type_hint(arena, t)),
2091        default: p.default.as_ref().map(|e| av_expr(arena, e)),
2092        by_ref: p.by_ref,
2093        variadic: p.variadic,
2094        is_readonly: p.is_readonly,
2095        is_final: p.is_final,
2096        visibility: p.visibility,
2097        set_visibility: p.set_visibility,
2098        attributes: av_attrs(arena, &p.attributes),
2099        hooks: av_hooks(arena, &p.hooks),
2100        span: p.span,
2101    }
2102}
2103
2104fn av_params<'a>(
2105    arena: &'a bumpalo::Bump,
2106    params: &[Param],
2107) -> arena_ast::ArenaVec<'a, arena_ast::Param<'a, 'a>> {
2108    let mut v = arena_ast::ArenaVec::with_capacity_in(params.len(), arena);
2109    for p in params.iter() {
2110        v.push(av_param(arena, p));
2111    }
2112    v
2113}
2114
2115fn av_hook<'a>(arena: &'a bumpalo::Bump, h: &PropertyHook) -> arena_ast::PropertyHook<'a, 'a> {
2116    arena_ast::PropertyHook {
2117        kind: h.kind,
2118        body: match &h.body {
2119            PropertyHookBody::Block(stmts) => {
2120                arena_ast::PropertyHookBody::Block(av_stmts(arena, stmts))
2121            }
2122            PropertyHookBody::Expression(e) => {
2123                arena_ast::PropertyHookBody::Expression(av_expr(arena, e))
2124            }
2125            PropertyHookBody::Abstract => arena_ast::PropertyHookBody::Abstract,
2126        },
2127        is_final: h.is_final,
2128        by_ref: h.by_ref,
2129        params: av_params(arena, &h.params),
2130        attributes: av_attrs(arena, &h.attributes),
2131        span: h.span,
2132    }
2133}
2134
2135fn av_hooks<'a>(
2136    arena: &'a bumpalo::Bump,
2137    hooks: &[PropertyHook],
2138) -> arena_ast::ArenaVec<'a, arena_ast::PropertyHook<'a, 'a>> {
2139    let mut v = arena_ast::ArenaVec::with_capacity_in(hooks.len(), arena);
2140    for h in hooks.iter() {
2141        v.push(av_hook(arena, h));
2142    }
2143    v
2144}
2145
2146fn av_stmt<'a>(arena: &'a bumpalo::Bump, stmt: &Stmt) -> arena_ast::Stmt<'a, 'a> {
2147    arena_ast::Stmt {
2148        kind: av_stmt_kind(arena, &stmt.kind),
2149        span: stmt.span,
2150    }
2151}
2152
2153fn av_stmts<'a>(
2154    arena: &'a bumpalo::Bump,
2155    stmts: &[Stmt],
2156) -> arena_ast::ArenaVec<'a, arena_ast::Stmt<'a, 'a>> {
2157    let mut v = arena_ast::ArenaVec::with_capacity_in(stmts.len(), arena);
2158    for s in stmts.iter() {
2159        v.push(av_stmt(arena, s));
2160    }
2161    v
2162}
2163
2164fn av_stmt_kind<'a>(arena: &'a bumpalo::Bump, k: &StmtKind) -> arena_ast::StmtKind<'a, 'a> {
2165    match k {
2166        StmtKind::Expression(e) => arena_ast::StmtKind::Expression(arena.alloc(av_expr(arena, e))),
2167        StmtKind::Echo(exprs) => arena_ast::StmtKind::Echo(av_exprs(arena, exprs)),
2168        StmtKind::Return(e) => {
2169            arena_ast::StmtKind::Return(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2170        }
2171        StmtKind::Block(stmts) => arena_ast::StmtKind::Block(av_stmts(arena, stmts)),
2172        StmtKind::If(s) => arena_ast::StmtKind::If(
2173            arena.alloc(arena_ast::IfStmt {
2174                condition: av_expr(arena, &s.condition),
2175                then_branch: arena.alloc(av_stmt(arena, &s.then_branch)),
2176                elseif_branches: {
2177                    let mut v =
2178                        arena_ast::ArenaVec::with_capacity_in(s.elseif_branches.len(), arena);
2179                    for b in s.elseif_branches.iter() {
2180                        v.push(arena_ast::ElseIfBranch {
2181                            condition: av_expr(arena, &b.condition),
2182                            body: av_stmt(arena, &b.body),
2183                            span: b.span,
2184                        });
2185                    }
2186                    v
2187                },
2188                else_branch: s
2189                    .else_branch
2190                    .as_ref()
2191                    .map(|b| &*arena.alloc(av_stmt(arena, b))),
2192                uses_alternative: s.uses_alternative,
2193            }),
2194        ),
2195        StmtKind::While(s) => arena_ast::StmtKind::While(arena.alloc(arena_ast::WhileStmt {
2196            condition: av_expr(arena, &s.condition),
2197            body: arena.alloc(av_stmt(arena, &s.body)),
2198            uses_alternative: s.uses_alternative,
2199        })),
2200        StmtKind::For(s) => arena_ast::StmtKind::For(arena.alloc(arena_ast::ForStmt {
2201            init: av_exprs(arena, &s.init),
2202            condition: av_exprs(arena, &s.condition),
2203            update: av_exprs(arena, &s.update),
2204            body: arena.alloc(av_stmt(arena, &s.body)),
2205            uses_alternative: s.uses_alternative,
2206        })),
2207        StmtKind::Foreach(s) => arena_ast::StmtKind::Foreach(arena.alloc(arena_ast::ForeachStmt {
2208            expr: av_expr(arena, &s.expr),
2209            key: s.key.as_ref().map(|e| av_expr(arena, e)),
2210            value: av_expr(arena, &s.value),
2211            body: arena.alloc(av_stmt(arena, &s.body)),
2212            uses_alternative: s.uses_alternative,
2213        })),
2214        StmtKind::DoWhile(s) => arena_ast::StmtKind::DoWhile(arena.alloc(arena_ast::DoWhileStmt {
2215            body: arena.alloc(av_stmt(arena, &s.body)),
2216            condition: av_expr(arena, &s.condition),
2217        })),
2218        StmtKind::Function(f) => {
2219            arena_ast::StmtKind::Function(arena.alloc(av_function_decl(arena, f)))
2220        }
2221        StmtKind::Break(e) => {
2222            arena_ast::StmtKind::Break(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2223        }
2224        StmtKind::Continue(e) => {
2225            arena_ast::StmtKind::Continue(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2226        }
2227        StmtKind::Switch(s) => arena_ast::StmtKind::Switch(arena.alloc(arena_ast::SwitchStmt {
2228            expr: av_expr(arena, &s.expr),
2229            cases: {
2230                let mut v = arena_ast::ArenaVec::with_capacity_in(s.cases.len(), arena);
2231                for c in s.cases.iter() {
2232                    v.push(arena_ast::SwitchCase {
2233                        value: c.value.as_ref().map(|vl| av_expr(arena, vl)),
2234                        body: av_stmts(arena, &c.body),
2235                        span: c.span,
2236                    });
2237                }
2238                v
2239            },
2240            uses_alternative: s.uses_alternative,
2241        })),
2242        StmtKind::Goto(ident) => arena_ast::StmtKind::Goto(av_ident(arena, ident)),
2243        StmtKind::Label(s) => arena_ast::StmtKind::Label(av_str(arena, s)),
2244        StmtKind::Declare(d) => arena_ast::StmtKind::Declare(arena.alloc(arena_ast::DeclareStmt {
2245            directives: {
2246                let mut v = arena_ast::ArenaVec::with_capacity_in(d.directives.len(), arena);
2247                for (k, val) in d.directives.iter() {
2248                    v.push((arena.alloc_str(k) as &str, av_expr(arena, val)));
2249                }
2250                v
2251            },
2252            body: d.body.as_ref().map(|b| &*arena.alloc(av_stmt(arena, b))),
2253            uses_alternative: d.uses_alternative,
2254        })),
2255        StmtKind::Unset(exprs) => arena_ast::StmtKind::Unset(av_exprs(arena, exprs)),
2256        StmtKind::Throw(e) => arena_ast::StmtKind::Throw(arena.alloc(av_expr(arena, e))),
2257        StmtKind::TryCatch(t) => {
2258            arena_ast::StmtKind::TryCatch(arena.alloc(arena_ast::TryCatchStmt {
2259                body: av_stmts(arena, &t.body),
2260                catches: {
2261                    let mut v = arena_ast::ArenaVec::with_capacity_in(t.catches.len(), arena);
2262                    for c in t.catches.iter() {
2263                        let mut types = arena_ast::ArenaVec::with_capacity_in(c.types.len(), arena);
2264                        for n in c.types.iter() {
2265                            types.push(av_name(arena, n));
2266                        }
2267                        v.push(arena_ast::CatchClause {
2268                            types,
2269                            var: c.var.as_deref().map(|s| av_str(arena, s)),
2270                            body: av_stmts(arena, &c.body),
2271                            span: c.span,
2272                        });
2273                    }
2274                    v
2275                },
2276                finally: t.finally.as_ref().map(|stmts| av_stmts(arena, stmts)),
2277            }))
2278        }
2279        StmtKind::Global(exprs) => arena_ast::StmtKind::Global(av_exprs(arena, exprs)),
2280        StmtKind::Class(cls) => arena_ast::StmtKind::Class(arena.alloc(av_class_decl(arena, cls))),
2281        StmtKind::Interface(iface) => {
2282            arena_ast::StmtKind::Interface(arena.alloc(av_interface_decl(arena, iface)))
2283        }
2284        StmtKind::Trait(tr) => arena_ast::StmtKind::Trait(arena.alloc(av_trait_decl(arena, tr))),
2285        StmtKind::Enum(en) => arena_ast::StmtKind::Enum(arena.alloc(av_enum_decl(arena, en))),
2286        StmtKind::Namespace(ns) => {
2287            arena_ast::StmtKind::Namespace(arena.alloc(arena_ast::NamespaceDecl {
2288                name: ns.name.as_ref().map(|n| av_name(arena, n)),
2289                body: match &ns.body {
2290                    NamespaceBody::Braced(stmts) => {
2291                        arena_ast::NamespaceBody::Braced(av_stmts(arena, stmts))
2292                    }
2293                    NamespaceBody::Simple => arena_ast::NamespaceBody::Simple,
2294                },
2295            }))
2296        }
2297        StmtKind::Use(u) => arena_ast::StmtKind::Use(arena.alloc(arena_ast::UseDecl {
2298            kind: u.kind,
2299            uses: {
2300                let mut v = arena_ast::ArenaVec::with_capacity_in(u.uses.len(), arena);
2301                for item in u.uses.iter() {
2302                    v.push(arena_ast::UseItem {
2303                        name: av_name(arena, &item.name),
2304                        alias: item.alias.as_deref().map(|s| av_str(arena, s)),
2305                        kind: item.kind,
2306                        span: item.span,
2307                    });
2308                }
2309                v
2310            },
2311        })),
2312        StmtKind::Const(items) => {
2313            let mut v = arena_ast::ArenaVec::with_capacity_in(items.len(), arena);
2314            for item in items.iter() {
2315                v.push(arena_ast::ConstItem {
2316                    name: av_ident(arena, &item.name),
2317                    value: av_expr(arena, &item.value),
2318                    attributes: av_attrs(arena, &item.attributes),
2319                    span: item.span,
2320                    doc_comment: av_opt_comment(arena, &item.doc_comment),
2321                });
2322            }
2323            arena_ast::StmtKind::Const(v)
2324        }
2325        StmtKind::StaticVar(vars) => {
2326            let mut v = arena_ast::ArenaVec::with_capacity_in(vars.len(), arena);
2327            for var in vars.iter() {
2328                v.push(arena_ast::StaticVar {
2329                    name: av_ident(arena, &var.name),
2330                    default: var.default.as_ref().map(|e| av_expr(arena, e)),
2331                    span: var.span,
2332                });
2333            }
2334            arena_ast::StmtKind::StaticVar(v)
2335        }
2336        StmtKind::HaltCompiler(s) => arena_ast::StmtKind::HaltCompiler(av_str(arena, s)),
2337        StmtKind::Nop => arena_ast::StmtKind::Nop,
2338        StmtKind::InlineHtml(s) => arena_ast::StmtKind::InlineHtml(av_str(arena, s)),
2339        StmtKind::Error => arena_ast::StmtKind::Error,
2340    }
2341}
2342
2343fn av_function_decl<'a>(
2344    arena: &'a bumpalo::Bump,
2345    f: &FunctionDecl,
2346) -> arena_ast::FunctionDecl<'a, 'a> {
2347    arena_ast::FunctionDecl {
2348        name: av_ident(arena, &f.name),
2349        params: av_params(arena, &f.params),
2350        body: av_stmts(arena, &f.body),
2351        return_type: f.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2352        by_ref: f.by_ref,
2353        attributes: av_attrs(arena, &f.attributes),
2354        doc_comment: av_opt_comment(arena, &f.doc_comment),
2355    }
2356}
2357
2358fn av_class_member<'a>(
2359    arena: &'a bumpalo::Bump,
2360    m: &ClassMember,
2361) -> arena_ast::ClassMember<'a, 'a> {
2362    arena_ast::ClassMember {
2363        kind: match &m.kind {
2364            ClassMemberKind::Property(p) => {
2365                arena_ast::ClassMemberKind::Property(arena_ast::PropertyDecl {
2366                    name: av_ident(arena, &p.name),
2367                    visibility: p.visibility,
2368                    set_visibility: p.set_visibility,
2369                    is_static: p.is_static,
2370                    is_readonly: p.is_readonly,
2371                    type_hint: p.type_hint.as_ref().map(|t| av_type_hint(arena, t)),
2372                    default: p.default.as_ref().map(|e| av_expr(arena, e)),
2373                    attributes: av_attrs(arena, &p.attributes),
2374                    hooks: av_hooks(arena, &p.hooks),
2375                    doc_comment: av_opt_comment(arena, &p.doc_comment),
2376                })
2377            }
2378            ClassMemberKind::Method(m) => {
2379                arena_ast::ClassMemberKind::Method(arena_ast::MethodDecl {
2380                    name: av_ident(arena, &m.name),
2381                    visibility: m.visibility,
2382                    is_static: m.is_static,
2383                    is_abstract: m.is_abstract,
2384                    is_final: m.is_final,
2385                    by_ref: m.by_ref,
2386                    params: av_params(arena, &m.params),
2387                    return_type: m.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2388                    body: m.body.as_ref().map(|stmts| av_stmts(arena, stmts)),
2389                    attributes: av_attrs(arena, &m.attributes),
2390                    doc_comment: av_opt_comment(arena, &m.doc_comment),
2391                })
2392            }
2393            ClassMemberKind::ClassConst(c) => {
2394                arena_ast::ClassMemberKind::ClassConst(av_class_const(arena, c))
2395            }
2396            ClassMemberKind::TraitUse(t) => {
2397                arena_ast::ClassMemberKind::TraitUse(av_trait_use(arena, t))
2398            }
2399        },
2400        span: m.span,
2401    }
2402}
2403
2404fn av_class_members<'a>(
2405    arena: &'a bumpalo::Bump,
2406    members: &[ClassMember],
2407) -> arena_ast::ArenaVec<'a, arena_ast::ClassMember<'a, 'a>> {
2408    let mut v = arena_ast::ArenaVec::with_capacity_in(members.len(), arena);
2409    for m in members.iter() {
2410        v.push(av_class_member(arena, m));
2411    }
2412    v
2413}
2414
2415fn av_class_const<'a>(
2416    arena: &'a bumpalo::Bump,
2417    c: &ClassConstDecl,
2418) -> arena_ast::ClassConstDecl<'a, 'a> {
2419    arena_ast::ClassConstDecl {
2420        name: av_ident(arena, &c.name),
2421        visibility: c.visibility,
2422        is_final: c.is_final,
2423        type_hint: c
2424            .type_hint
2425            .as_ref()
2426            .map(|th| arena.alloc(av_type_hint(arena, th)) as &_),
2427        value: av_expr(arena, &c.value),
2428        attributes: av_attrs(arena, &c.attributes),
2429        doc_comment: av_opt_comment(arena, &c.doc_comment),
2430    }
2431}
2432
2433fn av_trait_use<'a>(arena: &'a bumpalo::Bump, t: &TraitUseDecl) -> arena_ast::TraitUseDecl<'a, 'a> {
2434    let mut traits = arena_ast::ArenaVec::with_capacity_in(t.traits.len(), arena);
2435    for n in t.traits.iter() {
2436        traits.push(av_name(arena, n));
2437    }
2438    let mut adaptations = arena_ast::ArenaVec::with_capacity_in(t.adaptations.len(), arena);
2439    for a in t.adaptations.iter() {
2440        adaptations.push(arena_ast::TraitAdaptation {
2441            kind: match &a.kind {
2442                TraitAdaptationKind::Precedence {
2443                    trait_name,
2444                    method,
2445                    insteadof,
2446                } => {
2447                    let mut instead = arena_ast::ArenaVec::with_capacity_in(insteadof.len(), arena);
2448                    for n in insteadof.iter() {
2449                        instead.push(av_name(arena, n));
2450                    }
2451                    arena_ast::TraitAdaptationKind::Precedence {
2452                        trait_name: av_name(arena, trait_name),
2453                        method: av_name(arena, method),
2454                        insteadof: instead,
2455                    }
2456                }
2457                TraitAdaptationKind::Alias {
2458                    trait_name,
2459                    method,
2460                    new_modifier,
2461                    new_name,
2462                } => arena_ast::TraitAdaptationKind::Alias {
2463                    trait_name: trait_name.as_ref().map(|n| av_name(arena, n)),
2464                    method: av_name(arena, method),
2465                    new_modifier: *new_modifier,
2466                    new_name: new_name.as_ref().map(|n| av_name(arena, n)),
2467                },
2468            },
2469            span: a.span,
2470        });
2471    }
2472    arena_ast::TraitUseDecl {
2473        traits,
2474        adaptations,
2475    }
2476}
2477
2478fn av_class_decl<'a>(arena: &'a bumpalo::Bump, cls: &ClassDecl) -> arena_ast::ClassDecl<'a, 'a> {
2479    let mut implements = arena_ast::ArenaVec::with_capacity_in(cls.implements.len(), arena);
2480    for n in cls.implements.iter() {
2481        implements.push(av_name(arena, n));
2482    }
2483    arena_ast::ClassDecl {
2484        name: cls.name.as_ref().map(|ident| av_ident(arena, ident)),
2485        modifiers: cls.modifiers.clone(),
2486        extends: cls.extends.as_ref().map(|n| av_name(arena, n)),
2487        implements,
2488        members: av_class_members(arena, &cls.members),
2489        attributes: av_attrs(arena, &cls.attributes),
2490        doc_comment: av_opt_comment(arena, &cls.doc_comment),
2491    }
2492}
2493
2494fn av_interface_decl<'a>(
2495    arena: &'a bumpalo::Bump,
2496    iface: &InterfaceDecl,
2497) -> arena_ast::InterfaceDecl<'a, 'a> {
2498    let mut extends = arena_ast::ArenaVec::with_capacity_in(iface.extends.len(), arena);
2499    for n in iface.extends.iter() {
2500        extends.push(av_name(arena, n));
2501    }
2502    arena_ast::InterfaceDecl {
2503        name: av_ident(arena, &iface.name),
2504        extends,
2505        members: av_class_members(arena, &iface.members),
2506        attributes: av_attrs(arena, &iface.attributes),
2507        doc_comment: av_opt_comment(arena, &iface.doc_comment),
2508    }
2509}
2510
2511fn av_trait_decl<'a>(arena: &'a bumpalo::Bump, tr: &TraitDecl) -> arena_ast::TraitDecl<'a, 'a> {
2512    arena_ast::TraitDecl {
2513        name: av_ident(arena, &tr.name),
2514        members: av_class_members(arena, &tr.members),
2515        attributes: av_attrs(arena, &tr.attributes),
2516        doc_comment: av_opt_comment(arena, &tr.doc_comment),
2517    }
2518}
2519
2520fn av_enum_decl<'a>(arena: &'a bumpalo::Bump, en: &EnumDecl) -> arena_ast::EnumDecl<'a, 'a> {
2521    let mut implements = arena_ast::ArenaVec::with_capacity_in(en.implements.len(), arena);
2522    for n in en.implements.iter() {
2523        implements.push(av_name(arena, n));
2524    }
2525    let mut members = arena_ast::ArenaVec::with_capacity_in(en.members.len(), arena);
2526    for m in en.members.iter() {
2527        members.push(arena_ast::EnumMember {
2528            kind: match &m.kind {
2529                EnumMemberKind::Case(c) => arena_ast::EnumMemberKind::Case(arena_ast::EnumCase {
2530                    name: av_ident(arena, &c.name),
2531                    value: c.value.as_ref().map(|e| av_expr(arena, e)),
2532                    attributes: av_attrs(arena, &c.attributes),
2533                    doc_comment: av_opt_comment(arena, &c.doc_comment),
2534                }),
2535                EnumMemberKind::Method(m) => {
2536                    arena_ast::EnumMemberKind::Method(arena_ast::MethodDecl {
2537                        name: av_ident(arena, &m.name),
2538                        visibility: m.visibility,
2539                        is_static: m.is_static,
2540                        is_abstract: m.is_abstract,
2541                        is_final: m.is_final,
2542                        by_ref: m.by_ref,
2543                        params: av_params(arena, &m.params),
2544                        return_type: m.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2545                        body: m.body.as_ref().map(|stmts| av_stmts(arena, stmts)),
2546                        attributes: av_attrs(arena, &m.attributes),
2547                        doc_comment: av_opt_comment(arena, &m.doc_comment),
2548                    })
2549                }
2550                EnumMemberKind::ClassConst(c) => {
2551                    arena_ast::EnumMemberKind::ClassConst(av_class_const(arena, c))
2552                }
2553                EnumMemberKind::TraitUse(t) => {
2554                    arena_ast::EnumMemberKind::TraitUse(av_trait_use(arena, t))
2555                }
2556            },
2557            span: m.span,
2558        });
2559    }
2560    arena_ast::EnumDecl {
2561        name: av_ident(arena, &en.name),
2562        scalar_type: en.scalar_type.as_ref().map(|n| av_name(arena, n)),
2563        implements,
2564        members,
2565        attributes: av_attrs(arena, &en.attributes),
2566        doc_comment: av_opt_comment(arena, &en.doc_comment),
2567    }
2568}