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