Skip to main content

php_ast/owned/
mod.rs

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