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