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