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/// A possibly qualified PHP name.
81#[derive(Debug, Clone, Serialize)]
82pub struct Name {
83    /// Name segments split on `\`.
84    pub parts: Box<[Box<str>]>,
85    /// Qualification style.
86    pub kind: NameKind,
87    /// Source span.
88    pub span: Span,
89}
90
91// ---------------------------------------------------------------------------
92// Comment
93// ---------------------------------------------------------------------------
94
95/// A source comment.
96#[derive(Debug, Clone, Serialize)]
97pub struct Comment {
98    /// Comment syntax.
99    pub kind: CommentKind,
100    /// Raw comment text including delimiters.
101    pub text: Box<str>,
102    /// Source span.
103    pub span: Span,
104}
105
106// ---------------------------------------------------------------------------
107// TypeHint / TypeHintKind
108//
109// `TypeHintKind::Keyword` has a custom Serialize that emits the same JSON as
110// `Named`. We replicate that here.
111// ---------------------------------------------------------------------------
112
113/// A type declaration.
114#[derive(Debug, Clone, Serialize)]
115pub struct TypeHint {
116    /// Type shape.
117    pub kind: TypeHintKind,
118    /// Source span.
119    pub span: Span,
120}
121
122/// The shape of a type declaration.
123#[derive(Debug, Clone)]
124pub enum TypeHintKind {
125    /// A class or other user-defined type name.
126    Named(Name),
127    /// Serialises as `Named` — see [`TypeHintKind::Keyword`](crate::ast::TypeHintKind::Keyword).
128    Keyword(BuiltinType, Span),
129    /// `?T`.
130    Nullable(Box<TypeHint>),
131    /// `A|B`.
132    Union(Box<[TypeHint]>),
133    /// `A&B`.
134    Intersection(Box<[TypeHint]>),
135}
136
137impl Serialize for TypeHintKind {
138    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
139        match self {
140            Self::Named(name) => s.serialize_newtype_variant("TypeHintKind", 0, "Named", name),
141            Self::Nullable(inner) => {
142                s.serialize_newtype_variant("TypeHintKind", 2, "Nullable", inner)
143            }
144            Self::Union(types) => s.serialize_newtype_variant("TypeHintKind", 3, "Union", types),
145            Self::Intersection(types) => {
146                s.serialize_newtype_variant("TypeHintKind", 4, "Intersection", types)
147            }
148            Self::Keyword(builtin, span) => {
149                struct BuiltinNameRepr<'a>(&'a BuiltinType, &'a Span);
150                impl Serialize for BuiltinNameRepr<'_> {
151                    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
152                        use serde::ser::SerializeStruct;
153                        let mut st = s.serialize_struct("Name", 3)?;
154                        st.serialize_field("parts", &[self.0.as_str()])?;
155                        st.serialize_field("kind", &NameKind::Unqualified)?;
156                        st.serialize_field("span", self.1)?;
157                        st.end()
158                    }
159                }
160                s.serialize_newtype_variant(
161                    "TypeHintKind",
162                    0,
163                    "Named",
164                    &BuiltinNameRepr(builtin, span),
165                )
166            }
167        }
168    }
169}
170
171// ---------------------------------------------------------------------------
172// Arg / Attribute
173// ---------------------------------------------------------------------------
174
175/// A call argument.
176#[derive(Debug, Clone, Serialize)]
177pub struct Arg {
178    /// Name of a named argument.
179    pub name: Option<Name>,
180    /// `None` is a PHP 8.6 partial-application placeholder (`?`, or `...` when `unpack` is set).
181    pub value: Option<Expr>,
182    /// Whether the argument is spread with `...`.
183    pub unpack: bool,
184    /// Whether the argument is prefixed with `&`.
185    pub by_ref: bool,
186    /// Source span.
187    pub span: Span,
188}
189
190/// A `#[...]` attribute.
191#[derive(Debug, Clone, Serialize)]
192pub struct Attribute {
193    /// Attribute class name.
194    pub name: Name,
195    /// Arguments passed to the attribute.
196    pub args: Box<[Arg]>,
197    /// Source span.
198    pub span: Span,
199}
200
201// ---------------------------------------------------------------------------
202// Program
203// ---------------------------------------------------------------------------
204
205/// Root of a parsed file.
206#[derive(Debug, Clone, Serialize)]
207pub struct Program {
208    /// Top-level statements.
209    pub stmts: Box<[Stmt]>,
210    /// Source span.
211    pub span: Span,
212}
213
214// ---------------------------------------------------------------------------
215// Stmt / StmtKind
216// ---------------------------------------------------------------------------
217
218/// A statement.
219#[derive(Debug, Clone, Serialize)]
220pub struct Stmt {
221    /// Statement kind.
222    pub kind: StmtKind,
223    /// Source span.
224    pub span: Span,
225    /// Preceding doc comment.
226    #[serde(skip_serializing_if = "Option::is_none")]
227    pub doc_comment: Option<Comment>,
228}
229
230impl Stmt {
231    /// The leading `/** */` doc-block for this statement, regardless of where
232    /// it is stored.  See [`crate::ast::Stmt::leading_doc_comment`] for the
233    /// full contract.
234    pub fn leading_doc_comment(&self) -> Option<&Comment> {
235        if let Some(doc) = &self.doc_comment {
236            return Some(doc);
237        }
238        match &self.kind {
239            StmtKind::Function(f) => f.doc_comment.as_ref(),
240            StmtKind::Class(c) => c.doc_comment.as_ref(),
241            StmtKind::Interface(i) => i.doc_comment.as_ref(),
242            StmtKind::Trait(t) => t.doc_comment.as_ref(),
243            StmtKind::Enum(e) => e.doc_comment.as_ref(),
244            StmtKind::Const(items) => items.first().and_then(|i| i.doc_comment.as_ref()),
245            _ => None,
246        }
247    }
248}
249
250/// Owned mirror of [`crate::ast::Block`] — a brace-delimited statement block.
251#[derive(Debug, Clone, Serialize)]
252#[serde(transparent)]
253pub struct Block {
254    /// Statements in the block.
255    pub stmts: Box<[Stmt]>,
256    /// Source span.
257    #[serde(skip)]
258    pub span: Span,
259}
260
261/// The kind of a statement.
262#[derive(Debug, Clone, Serialize)]
263pub enum StmtKind {
264    /// An expression statement.
265    Expression(Box<Expr>),
266    /// `echo` with one or more expressions.
267    Echo(Box<[Expr]>),
268    /// `return` with optional value.
269    Return(Option<Box<Expr>>),
270    /// A nested block.
271    Block(Box<Block>),
272    /// An `if` statement.
273    If(Box<IfStmt>),
274    /// A `while` loop.
275    While(Box<WhileStmt>),
276    /// A `for` loop.
277    For(Box<ForStmt>),
278    /// A `foreach` loop.
279    Foreach(Box<ForeachStmt>),
280    /// A `do ... while` loop.
281    DoWhile(Box<DoWhileStmt>),
282    /// A function declaration.
283    Function(Box<FunctionDecl>),
284    /// `break` with optional level.
285    Break(Option<Box<Expr>>),
286    /// `continue` with optional level.
287    Continue(Option<Box<Expr>>),
288    /// A `switch` statement.
289    Switch(Box<SwitchStmt>),
290    /// `goto` with target label.
291    Goto(Ident),
292    /// A `label:` definition.
293    Label(Box<str>),
294    /// A `declare(...)` statement.
295    Declare(Box<DeclareStmt>),
296    /// `unset(...)` targets.
297    Unset(Box<[Expr]>),
298    /// A `throw` statement.
299    Throw(Box<Expr>),
300    /// A `try`/`catch`/`finally` statement.
301    TryCatch(Box<TryCatchStmt>),
302    /// `global` variables.
303    Global(Box<[Expr]>),
304    /// A class declaration.
305    Class(Box<ClassDecl>),
306    /// An interface declaration.
307    Interface(Box<InterfaceDecl>),
308    /// A trait declaration.
309    Trait(Box<TraitDecl>),
310    /// An enum declaration.
311    Enum(Box<EnumDecl>),
312    /// A namespace declaration.
313    Namespace(Box<NamespaceDecl>),
314    /// A `use` import.
315    Use(Box<UseDecl>),
316    /// A top-level `const` statement.
317    Const(Box<[ConstItem]>),
318    /// A `static` variable statement.
319    StaticVar(Box<[StaticVar]>),
320    /// `__halt_compiler()` with the trailing raw data.
321    HaltCompiler(Box<str>),
322    /// An empty statement (`;`).
323    Nop,
324    /// Text outside PHP tags.
325    InlineHtml(Box<str>),
326    /// Placeholder for a statement that failed to parse.
327    Error,
328}
329
330/// An `if` statement.
331#[derive(Debug, Clone, Serialize)]
332pub struct IfStmt {
333    /// The `if` condition.
334    pub condition: Expr,
335    /// Statement run when the condition holds.
336    pub then_branch: Box<Stmt>,
337    /// `elseif` branches in source order.
338    pub elseif_branches: Box<[ElseIfBranch]>,
339    /// The `else` statement, if any.
340    pub else_branch: Option<Box<Stmt>>,
341    /// Byte offset of the `else` keyword.
342    #[serde(skip)]
343    pub else_kw_start: Option<u32>,
344    /// Whether the `if: ... endif;` syntax is used.
345    #[serde(default, skip_serializing_if = "is_false")]
346    pub uses_alternative: bool,
347}
348
349/// An `elseif` branch.
350#[derive(Debug, Clone, Serialize)]
351pub struct ElseIfBranch {
352    /// The `elseif` condition.
353    pub condition: Expr,
354    /// Statement run when the condition holds.
355    pub body: Stmt,
356    /// Source span.
357    pub span: Span,
358}
359
360/// A `while` loop.
361#[derive(Debug, Clone, Serialize)]
362pub struct WhileStmt {
363    /// The loop condition.
364    pub condition: Expr,
365    /// The loop body.
366    pub body: Box<Stmt>,
367    /// Whether the `while: ... endwhile;` syntax is used.
368    #[serde(default, skip_serializing_if = "is_false")]
369    pub uses_alternative: bool,
370}
371
372/// A `for` loop.
373#[derive(Debug, Clone, Serialize)]
374pub struct ForStmt {
375    /// Initializer expressions.
376    pub init: Box<[Expr]>,
377    /// Condition expressions.
378    pub condition: Box<[Expr]>,
379    /// Update expressions.
380    pub update: Box<[Expr]>,
381    /// The loop body.
382    pub body: Box<Stmt>,
383    /// Whether the `for: ... endfor;` syntax is used.
384    #[serde(default, skip_serializing_if = "is_false")]
385    pub uses_alternative: bool,
386}
387
388/// A `foreach` loop.
389#[derive(Debug, Clone, Serialize)]
390pub struct ForeachStmt {
391    /// The iterated expression.
392    pub expr: Expr,
393    /// The key target, if any.
394    pub key: Option<Expr>,
395    /// The value target.
396    pub value: Expr,
397    /// The loop body.
398    pub body: Box<Stmt>,
399    /// Whether the `foreach: ... endforeach;` syntax is used.
400    #[serde(default, skip_serializing_if = "is_false")]
401    pub uses_alternative: bool,
402}
403
404/// A `do ... while` loop.
405#[derive(Debug, Clone, Serialize)]
406pub struct DoWhileStmt {
407    /// The loop body.
408    pub body: Box<Stmt>,
409    /// The loop condition.
410    pub condition: Expr,
411}
412
413/// The braced body of a `switch`.
414#[derive(Debug, Clone, Serialize)]
415pub struct SwitchBody {
416    /// The cases in source order.
417    pub cases: Box<[SwitchCase]>,
418    /// Source span.
419    #[serde(skip)]
420    pub span: Span,
421}
422
423/// A `switch` statement.
424#[derive(Debug, Clone, Serialize)]
425pub struct SwitchStmt {
426    /// The switched-on expression.
427    pub expr: Expr,
428    /// The braced case list.
429    #[serde(flatten)]
430    pub body: SwitchBody,
431    /// Whether the `switch: ... endswitch;` syntax is used.
432    #[serde(default, skip_serializing_if = "is_false")]
433    pub uses_alternative: bool,
434}
435
436/// A `case` or `default` arm.
437#[derive(Debug, Clone, Serialize)]
438pub struct SwitchCase {
439    /// Case value; `None` for `default`.
440    pub value: Option<Expr>,
441    /// Statements of the case.
442    pub body: Box<[Stmt]>,
443    /// Source span.
444    pub span: Span,
445}
446
447/// A `try` statement.
448#[derive(Debug, Clone, Serialize)]
449pub struct TryCatchStmt {
450    /// The `try` block.
451    pub body: Box<Block>,
452    /// The `catch` clauses.
453    pub catches: Box<[CatchClause]>,
454    /// The `finally` block, if any.
455    pub finally: Option<Box<Block>>,
456    /// Byte offset of the `finally` keyword.
457    #[serde(skip)]
458    pub finally_kw_start: Option<u32>,
459}
460
461/// A `catch` clause.
462#[derive(Debug, Clone, Serialize)]
463pub struct CatchClause {
464    /// Caught exception types (multi-catch allowed).
465    pub types: Box<[Name]>,
466    /// Bound variable name without `$`, if any.
467    pub var: Option<Box<str>>,
468    /// The `catch` block.
469    pub body: Box<Block>,
470    /// Source span.
471    pub span: Span,
472}
473
474/// A `namespace` declaration.
475#[derive(Debug, Clone, Serialize)]
476pub struct NamespaceDecl {
477    /// Namespace name; `None` for the global namespace.
478    pub name: Option<Name>,
479    /// Braced or simple form.
480    pub body: NamespaceBody,
481}
482
483/// Whether a namespace is braced or statement-terminated.
484#[derive(Debug, Clone, Serialize)]
485pub enum NamespaceBody {
486    /// `namespace X { ... }`.
487    Braced(Box<Block>),
488    /// `namespace X;`.
489    Simple,
490}
491
492/// A `declare(...)` statement.
493#[derive(Debug, Clone, Serialize)]
494pub struct DeclareStmt {
495    /// Directive name and value pairs.
496    pub directives: Box<[(Box<str>, Expr)]>,
497    /// Statement governed by the declare, if any.
498    pub body: Option<Box<Stmt>>,
499    /// Whether the `declare: ... enddeclare;` syntax is used.
500    #[serde(default, skip_serializing_if = "is_false")]
501    pub uses_alternative: bool,
502}
503
504/// A `use` import declaration.
505#[derive(Debug, Clone, Serialize)]
506pub struct UseDecl {
507    /// Import kind (class, function or const).
508    pub kind: UseKind,
509    /// The imported items.
510    pub uses: Box<[UseItem]>,
511}
512
513/// One imported name in a `use` declaration.
514#[derive(Debug, Clone, Serialize)]
515pub struct UseItem {
516    /// The imported name.
517    pub name: Name,
518    /// Local alias from `as`.
519    pub alias: Option<Box<str>>,
520    /// Per-item kind in a mixed group use.
521    #[serde(skip_serializing_if = "Option::is_none")]
522    pub kind: Option<UseKind>,
523    /// Source span.
524    pub span: Span,
525}
526
527/// One item of a top-level `const` statement.
528#[derive(Debug, Clone, Serialize)]
529pub struct ConstItem {
530    /// Constant name.
531    pub name: Ident,
532    /// Constant value.
533    pub value: Expr,
534    /// Attributes on the constant.
535    pub attributes: Box<[Attribute]>,
536    /// Source span.
537    pub span: Span,
538    /// Preceding doc comment.
539    #[serde(skip_serializing_if = "Option::is_none")]
540    pub doc_comment: Option<Comment>,
541}
542
543/// One variable of a `static` statement.
544#[derive(Debug, Clone, Serialize)]
545pub struct StaticVar {
546    /// Variable name without `$`.
547    pub name: Ident,
548    /// Initial value, if any.
549    pub default: Option<Expr>,
550    /// Source span.
551    pub span: Span,
552}
553
554// ---------------------------------------------------------------------------
555// Expr / ExprKind
556// ---------------------------------------------------------------------------
557
558/// An expression.
559#[derive(Debug, Clone, Serialize)]
560pub struct Expr {
561    /// Expression kind.
562    pub kind: ExprKind,
563    /// Source span.
564    pub span: Span,
565}
566
567/// The kind of an expression.
568#[derive(Debug, Clone, Serialize)]
569pub enum ExprKind {
570    /// Integer literal.
571    Int(i64),
572    /// Float literal.
573    Float(f64),
574    /// String literal without interpolation.
575    String(Box<str>),
576    /// Double-quoted string with interpolation.
577    InterpolatedString(Box<[StringPart]>),
578    /// Heredoc string.
579    Heredoc {
580        /// Heredoc delimiter.
581        label: Box<str>,
582        /// Body segments.
583        parts: Box<[StringPart]>,
584    },
585    /// Nowdoc string.
586    Nowdoc {
587        /// Nowdoc delimiter.
588        label: Box<str>,
589        /// Raw body text.
590        value: Box<str>,
591    },
592    /// Backtick shell command.
593    ShellExec(Box<[StringPart]>),
594    /// `true` or `false`.
595    Bool(bool),
596    /// `null`.
597    Null,
598    /// Variable name without `$`.
599    Variable(Box<str>),
600    /// `$$x` or `${expr}`.
601    VariableVariable(Box<Expr>),
602    /// Bare identifier such as a constant name.
603    Identifier(Box<str>),
604    /// Assignment.
605    Assign(AssignExpr),
606    /// Binary operation.
607    Binary(BinaryExpr),
608    /// Prefix unary operation.
609    UnaryPrefix(UnaryPrefixExpr),
610    /// Postfix unary operation.
611    UnaryPostfix(UnaryPostfixExpr),
612    /// Ternary conditional.
613    Ternary(TernaryExpr),
614    /// `??` expression.
615    NullCoalesce(NullCoalesceExpr),
616    /// Function call.
617    FunctionCall(FunctionCallExpr),
618    /// Array literal.
619    Array(Box<[ArrayElement]>),
620    /// Array offset access.
621    ArrayAccess(ArrayAccessExpr),
622    /// `print` expression.
623    Print(Box<Expr>),
624    /// Parenthesized expression.
625    Parenthesized(Box<Expr>),
626    /// Type cast.
627    Cast(CastKind, Box<Expr>),
628    /// `@` error suppression.
629    ErrorSuppress(Box<Expr>),
630    /// `isset(...)`.
631    Isset(Box<[Expr]>),
632    /// `empty(...)`.
633    Empty(Box<Expr>),
634    /// `include`/`require` variant.
635    Include(IncludeKind, Box<Expr>),
636    /// `eval(...)`.
637    Eval(Box<Expr>),
638    /// `exit`/`die` with optional argument.
639    Exit(Option<Box<Expr>>),
640    /// Magic constant such as `__LINE__`.
641    MagicConst(MagicConstKind),
642    /// `clone` expression.
643    Clone(Box<Expr>),
644    /// `clone` with property overrides.
645    CloneWith(Box<Expr>, Box<Expr>),
646    /// `new` expression.
647    New(NewExpr),
648    /// `->` property access.
649    PropertyAccess(PropertyAccessExpr),
650    /// `?->` property access.
651    NullsafePropertyAccess(PropertyAccessExpr),
652    /// `->` method call.
653    MethodCall(Box<MethodCallExpr>),
654    /// `?->` method call.
655    NullsafeMethodCall(Box<MethodCallExpr>),
656    /// `Class::$prop` access.
657    StaticPropertyAccess(StaticAccessExpr),
658    /// `Class::method()` call.
659    StaticMethodCall(Box<StaticMethodCallExpr>),
660    /// `Class::$method()` call.
661    StaticDynMethodCall(Box<StaticDynMethodCallExpr>),
662    /// `Class::CONST` access.
663    ClassConstAccess(StaticAccessExpr),
664    /// `Class::{expr}` constant access.
665    ClassConstAccessDynamic {
666        /// Class expression.
667        class: Box<Expr>,
668        /// Constant name expression.
669        member: Box<Expr>,
670    },
671    /// `Class::$$member` property access.
672    StaticPropertyAccessDynamic {
673        /// Class expression.
674        class: Box<Expr>,
675        /// Property name expression.
676        member: Box<Expr>,
677    },
678    /// Anonymous function.
679    Closure(Box<ClosureExpr>),
680    /// Arrow function.
681    ArrowFunction(Box<ArrowFunctionExpr>),
682    /// `match` expression.
683    Match(MatchExpr),
684    /// `throw` used as an expression.
685    ThrowExpr(Box<Expr>),
686    /// `yield` expression.
687    Yield(YieldExpr),
688    /// `new class { ... }`.
689    AnonymousClass(Box<ClassDecl>),
690    /// First-class callable syntax.
691    CallableCreate(CallableCreateExpr),
692    /// Skipped slot in a list destructuring pattern.
693    Omit,
694    /// Placeholder for an expression that failed to parse.
695    Error,
696}
697
698/// An assignment.
699#[derive(Debug, Clone, Serialize)]
700pub struct AssignExpr {
701    /// Assignment target.
702    pub target: Box<Expr>,
703    /// Assignment operator.
704    pub op: AssignOp,
705    /// Assigned value.
706    pub value: Box<Expr>,
707    /// Whether this is a reference assignment (`=&`).
708    #[serde(skip_serializing_if = "is_false")]
709    pub by_ref: bool,
710}
711
712/// A binary operation.
713#[derive(Debug, Clone, Serialize)]
714pub struct BinaryExpr {
715    /// Left operand.
716    pub left: Box<Expr>,
717    /// Operator.
718    pub op: BinaryOp,
719    /// Right operand.
720    pub right: Box<Expr>,
721}
722
723/// A prefix unary operation.
724#[derive(Debug, Clone, Serialize)]
725pub struct UnaryPrefixExpr {
726    /// Operator.
727    pub op: UnaryPrefixOp,
728    /// Operand.
729    pub operand: Box<Expr>,
730}
731
732/// A postfix unary operation.
733#[derive(Debug, Clone, Serialize)]
734pub struct UnaryPostfixExpr {
735    /// Operand.
736    pub operand: Box<Expr>,
737    /// Operator.
738    pub op: UnaryPostfixOp,
739}
740
741/// A ternary or short-ternary conditional.
742#[derive(Debug, Clone, Serialize)]
743pub struct TernaryExpr {
744    /// Condition.
745    pub condition: Box<Expr>,
746    /// Value when true; `None` for `?:`.
747    pub then_expr: Option<Box<Expr>>,
748    /// Value when false.
749    pub else_expr: Box<Expr>,
750}
751
752/// A `??` expression.
753#[derive(Debug, Clone, Serialize)]
754pub struct NullCoalesceExpr {
755    /// Value tested for null.
756    pub left: Box<Expr>,
757    /// Fallback value.
758    pub right: Box<Expr>,
759}
760
761/// A function call.
762#[derive(Debug, Clone, Serialize)]
763pub struct FunctionCallExpr {
764    /// Callee expression.
765    pub name: Box<Expr>,
766    /// Call arguments.
767    pub args: Box<[Arg]>,
768}
769
770/// An element of an array literal.
771#[derive(Debug, Clone, Serialize)]
772pub struct ArrayElement {
773    /// Key expression, if any.
774    pub key: Option<Expr>,
775    /// Element value.
776    pub value: Expr,
777    /// Whether the element is spread with `...`.
778    pub unpack: bool,
779    /// Whether the value is taken by reference.
780    #[serde(skip_serializing_if = "is_false")]
781    pub by_ref: bool,
782    /// Source span.
783    pub span: Span,
784}
785
786/// An array offset access.
787#[derive(Debug, Clone, Serialize)]
788pub struct ArrayAccessExpr {
789    /// Accessed expression.
790    pub array: Box<Expr>,
791    /// Offset; `None` for `$a[]`.
792    pub index: Option<Box<Expr>>,
793}
794
795/// A `new` expression.
796#[derive(Debug, Clone, Serialize)]
797pub struct NewExpr {
798    /// Class name or expression.
799    pub class: Box<Expr>,
800    /// Constructor arguments.
801    pub args: Box<[Arg]>,
802}
803
804/// A `->` or `?->` property access.
805#[derive(Debug, Clone, Serialize)]
806pub struct PropertyAccessExpr {
807    /// Object expression.
808    pub object: Box<Expr>,
809    /// Property name or expression.
810    pub property: Box<Expr>,
811}
812
813/// A `->` or `?->` method call.
814#[derive(Debug, Clone, Serialize)]
815pub struct MethodCallExpr {
816    /// Object expression.
817    pub object: Box<Expr>,
818    /// Method name or expression.
819    pub method: Box<Expr>,
820    /// Call arguments.
821    pub args: Box<[Arg]>,
822}
823
824/// A `::` static property or constant access.
825#[derive(Debug, Clone, Serialize)]
826pub struct StaticAccessExpr {
827    /// Class expression.
828    pub class: Box<Expr>,
829    /// Member name or expression.
830    pub member: Box<Expr>,
831}
832
833/// A `Class::method()` call.
834#[derive(Debug, Clone, Serialize)]
835pub struct StaticMethodCallExpr {
836    /// Class expression.
837    pub class: Box<Expr>,
838    /// Method name.
839    pub method: Box<Expr>,
840    /// Call arguments.
841    pub args: Box<[Arg]>,
842}
843
844/// A static call with a dynamic method expression.
845#[derive(Debug, Clone, Serialize)]
846pub struct StaticDynMethodCallExpr {
847    /// Class expression.
848    pub class: Box<Expr>,
849    /// Method name expression.
850    pub method: Box<Expr>,
851    /// Call arguments.
852    pub args: Box<[Arg]>,
853}
854
855/// An anonymous function.
856#[derive(Debug, Clone, Serialize)]
857pub struct ClosureExpr {
858    /// Whether declared `static`.
859    pub is_static: bool,
860    /// Whether it returns by reference.
861    pub by_ref: bool,
862    /// Parameters.
863    pub params: Box<[Param]>,
864    /// Captured variables.
865    pub use_vars: Box<[ClosureUseVar]>,
866    /// Declared return type.
867    pub return_type: Option<TypeHint>,
868    /// Function body.
869    pub body: Box<Block>,
870    /// Attributes on the closure.
871    pub attributes: Box<[Attribute]>,
872}
873
874/// A variable captured by a closure `use` clause.
875#[derive(Debug, Clone, Serialize)]
876pub struct ClosureUseVar {
877    /// Captured variable name without `$`.
878    pub name: Box<str>,
879    /// Whether captured by reference.
880    pub by_ref: bool,
881    /// Source span.
882    pub span: Span,
883}
884
885/// An `fn` arrow function.
886#[derive(Debug, Clone, Serialize)]
887pub struct ArrowFunctionExpr {
888    /// Whether declared `static`.
889    pub is_static: bool,
890    /// Whether it returns by reference.
891    pub by_ref: bool,
892    /// Parameters.
893    pub params: Box<[Param]>,
894    /// Declared return type.
895    pub return_type: Option<TypeHint>,
896    /// Result expression.
897    pub body: Box<Expr>,
898    /// Attributes on the arrow function.
899    pub attributes: Box<[Attribute]>,
900}
901
902/// A `match` expression.
903#[derive(Debug, Clone, Serialize)]
904pub struct MatchExpr {
905    /// Matched expression.
906    pub subject: Box<Expr>,
907    /// Match arms.
908    pub arms: Box<[MatchArm]>,
909    /// Byte offset of the opening `{`.
910    #[serde(skip)]
911    pub brace_start: u32,
912}
913
914/// An arm of a `match` expression.
915#[derive(Debug, Clone, Serialize)]
916pub struct MatchArm {
917    /// Conditions; `None` for `default`.
918    pub conditions: Option<Box<[Expr]>>,
919    /// Result expression.
920    pub body: Expr,
921    /// Source span.
922    pub span: Span,
923}
924
925/// A `yield` or `yield from` expression.
926#[derive(Debug, Clone, Serialize)]
927pub struct YieldExpr {
928    /// Yielded key, if any.
929    pub key: Option<Box<Expr>>,
930    /// Yielded value, if any.
931    pub value: Option<Box<Expr>>,
932    /// Whether this is `yield from`.
933    pub is_from: bool,
934}
935
936/// A first-class callable, e.g. `f(...)`.
937#[derive(Debug, Clone, Serialize)]
938pub struct CallableCreateExpr {
939    /// The callee.
940    pub kind: CallableCreateKind,
941}
942
943/// The callee of a first-class callable.
944#[derive(Debug, Clone, Serialize)]
945pub enum CallableCreateKind {
946    /// `f(...)`.
947    Function(Box<Expr>),
948    /// `$obj->method(...)`.
949    Method {
950        /// Object expression.
951        object: Box<Expr>,
952        /// Method name or expression.
953        method: Box<Expr>,
954    },
955    /// `$obj?->method(...)`.
956    NullsafeMethod {
957        /// Object expression.
958        object: Box<Expr>,
959        /// Method name or expression.
960        method: Box<Expr>,
961    },
962    /// `Class::method(...)`.
963    StaticMethod {
964        /// Class expression.
965        class: Box<Expr>,
966        /// Method name or expression.
967        method: Box<Expr>,
968    },
969}
970
971/// A segment of an interpolated string.
972#[derive(Debug, Clone, Serialize)]
973pub enum StringPart {
974    /// Literal text.
975    Literal(Box<str>),
976    /// Interpolated expression.
977    Expr(Expr),
978}
979
980// ---------------------------------------------------------------------------
981// Owned declaration types
982// ---------------------------------------------------------------------------
983
984/// A function declaration.
985#[derive(Debug, Clone, Serialize)]
986pub struct FunctionDecl {
987    /// Function name.
988    pub name: Ident,
989    /// Parameters.
990    pub params: Box<[Param]>,
991    /// Function body.
992    pub body: Box<Block>,
993    /// Declared return type.
994    pub return_type: Option<TypeHint>,
995    /// Whether it returns by reference.
996    pub by_ref: bool,
997    /// Attributes on the function.
998    pub attributes: Box<[Attribute]>,
999    /// Preceding doc comment.
1000    #[serde(skip_serializing_if = "Option::is_none")]
1001    pub doc_comment: Option<Comment>,
1002}
1003
1004/// A function or method parameter.
1005#[derive(Debug, Clone, Serialize)]
1006pub struct Param {
1007    /// Parameter name without `$`.
1008    pub name: Ident,
1009    /// Declared type.
1010    pub type_hint: Option<TypeHint>,
1011    /// Default value.
1012    pub default: Option<Expr>,
1013    /// Whether passed by reference.
1014    pub by_ref: bool,
1015    /// Whether variadic (`...`).
1016    pub variadic: bool,
1017    /// Whether a `readonly` promoted property.
1018    pub is_readonly: bool,
1019    /// Whether a `final` promoted property.
1020    pub is_final: bool,
1021    /// Promotion visibility.
1022    pub visibility: Option<Visibility>,
1023    /// Asymmetric set visibility of a promoted property.
1024    pub set_visibility: Option<Visibility>,
1025    /// Attributes on the parameter.
1026    pub attributes: Box<[Attribute]>,
1027    /// Property hooks of a promoted property.
1028    #[serde(skip_serializing_if = "slice_is_empty")]
1029    pub hooks: Box<[PropertyHook]>,
1030    /// Preceding doc comment.
1031    #[serde(skip_serializing_if = "Option::is_none")]
1032    pub doc_comment: Option<Comment>,
1033    /// Source span.
1034    pub span: Span,
1035}
1036
1037/// The braced member list of a class-like declaration.
1038#[derive(Debug, Clone, Serialize)]
1039pub struct ClassBody {
1040    /// Members in source order.
1041    pub members: Box<[ClassMember]>,
1042    /// Source span.
1043    #[serde(skip)]
1044    pub span: Span,
1045}
1046
1047/// A class declaration.
1048#[derive(Debug, Clone, Serialize)]
1049pub struct ClassDecl {
1050    /// Class name; `None` for anonymous classes.
1051    pub name: Option<Ident>,
1052    /// Class modifiers.
1053    pub modifiers: ClassModifiers,
1054    /// Parent class.
1055    pub extends: Option<Name>,
1056    /// Implemented interfaces.
1057    pub implements: Box<[Name]>,
1058    /// Class body.
1059    #[serde(flatten)]
1060    pub body: ClassBody,
1061    /// Attributes on the class.
1062    pub attributes: Box<[Attribute]>,
1063    /// Preceding doc comment.
1064    #[serde(skip_serializing_if = "Option::is_none")]
1065    pub doc_comment: Option<Comment>,
1066}
1067
1068/// A member of a class-like body.
1069#[derive(Debug, Clone, Serialize)]
1070pub struct ClassMember {
1071    /// Member kind.
1072    pub kind: ClassMemberKind,
1073    /// Source span.
1074    pub span: Span,
1075}
1076
1077/// The kind of a class member.
1078#[derive(Debug, Clone, Serialize)]
1079pub enum ClassMemberKind {
1080    /// Property declaration.
1081    Property(PropertyDecl),
1082    /// Method declaration.
1083    Method(MethodDecl),
1084    /// Class constant declaration.
1085    ClassConst(ClassConstDecl),
1086    /// Trait use clause.
1087    TraitUse(TraitUseDecl),
1088}
1089
1090/// A property declaration.
1091#[derive(Debug, Clone, Serialize)]
1092pub struct PropertyDecl {
1093    /// Property name without `$`.
1094    pub name: Ident,
1095    /// Read visibility.
1096    pub visibility: Option<Visibility>,
1097    /// Asymmetric set visibility.
1098    pub set_visibility: Option<Visibility>,
1099    /// Whether `static`.
1100    pub is_static: bool,
1101    /// Whether `readonly`.
1102    pub is_readonly: bool,
1103    /// Declared type.
1104    pub type_hint: Option<TypeHint>,
1105    /// Default value.
1106    pub default: Option<Expr>,
1107    /// Attributes on the property.
1108    pub attributes: Box<[Attribute]>,
1109    /// Property hooks.
1110    #[serde(skip_serializing_if = "slice_is_empty")]
1111    pub hooks: Box<[PropertyHook]>,
1112    /// Preceding doc comment.
1113    #[serde(skip_serializing_if = "Option::is_none")]
1114    pub doc_comment: Option<Comment>,
1115}
1116
1117/// A property `get` or `set` hook.
1118#[derive(Debug, Clone, Serialize)]
1119pub struct PropertyHook {
1120    /// `get` or `set`.
1121    pub kind: PropertyHookKind,
1122    /// Hook body.
1123    pub body: PropertyHookBody,
1124    /// Whether `final`.
1125    pub is_final: bool,
1126    /// Whether it returns by reference.
1127    pub by_ref: bool,
1128    /// Parameters of a `set` hook.
1129    pub params: Box<[Param]>,
1130    /// Attributes on the hook.
1131    pub attributes: Box<[Attribute]>,
1132    /// Source span.
1133    pub span: Span,
1134}
1135
1136/// The body of a property hook.
1137#[derive(Debug, Clone, Serialize)]
1138pub enum PropertyHookBody {
1139    /// Braced body.
1140    Block(Box<Block>),
1141    /// Arrow expression body (`=> expr`).
1142    Expression(Expr),
1143    /// No body (`;`).
1144    Abstract,
1145}
1146
1147/// A method declaration.
1148#[derive(Debug, Clone, Serialize)]
1149pub struct MethodDecl {
1150    /// Method name.
1151    pub name: Ident,
1152    /// Visibility modifier.
1153    pub visibility: Option<Visibility>,
1154    /// Whether `static`.
1155    pub is_static: bool,
1156    /// Whether `abstract`.
1157    pub is_abstract: bool,
1158    /// Whether `final`.
1159    pub is_final: bool,
1160    /// Whether it returns by reference.
1161    pub by_ref: bool,
1162    /// Parameters.
1163    pub params: Box<[Param]>,
1164    /// Declared return type.
1165    pub return_type: Option<TypeHint>,
1166    /// Method body; `None` for abstract or interface methods.
1167    pub body: Option<Box<Block>>,
1168    /// Attributes on the method.
1169    pub attributes: Box<[Attribute]>,
1170    /// Preceding doc comment.
1171    #[serde(skip_serializing_if = "Option::is_none")]
1172    pub doc_comment: Option<Comment>,
1173}
1174
1175/// A class constant declaration.
1176#[derive(Debug, Clone, Serialize)]
1177pub struct ClassConstDecl {
1178    /// Constant name.
1179    pub name: Ident,
1180    /// Visibility modifier.
1181    pub visibility: Option<Visibility>,
1182    /// Whether `final`.
1183    pub is_final: bool,
1184    /// Declared type.
1185    #[serde(skip_serializing_if = "Option::is_none")]
1186    pub type_hint: Option<Box<TypeHint>>,
1187    /// Constant value.
1188    pub value: Expr,
1189    /// Attributes on the constant.
1190    pub attributes: Box<[Attribute]>,
1191    /// Preceding doc comment.
1192    #[serde(skip_serializing_if = "Option::is_none")]
1193    pub doc_comment: Option<Comment>,
1194}
1195
1196/// A `use Trait;` clause inside a class.
1197#[derive(Debug, Clone, Serialize)]
1198pub struct TraitUseDecl {
1199    /// Used traits.
1200    pub traits: Box<[Name]>,
1201    /// Rules in the `{ ... }` block.
1202    pub adaptations: Box<[TraitAdaptation]>,
1203    /// Byte offset of the opening `{`.
1204    #[serde(skip)]
1205    pub adaptations_brace_start: Option<u32>,
1206    /// Preceding doc comment.
1207    #[serde(skip_serializing_if = "Option::is_none")]
1208    pub doc_comment: Option<Comment>,
1209}
1210
1211/// A `insteadof` or `as` rule in a trait use.
1212#[derive(Debug, Clone, Serialize)]
1213pub struct TraitAdaptation {
1214    /// Rule kind.
1215    pub kind: TraitAdaptationKind,
1216    /// Source span.
1217    pub span: Span,
1218}
1219
1220/// The kind of trait adaptation rule.
1221#[derive(Debug, Clone, Serialize)]
1222pub enum TraitAdaptationKind {
1223    /// `A::m insteadof B;`.
1224    Precedence {
1225        /// Trait providing the method.
1226        trait_name: Name,
1227        /// Method name.
1228        method: Name,
1229        /// Traits whose method is excluded.
1230        insteadof: Box<[Name]>,
1231    },
1232    /// `m as [visibility] [alias];`.
1233    Alias {
1234        /// Explicit trait qualifier, if any.
1235        trait_name: Option<Name>,
1236        /// Method name.
1237        method: Name,
1238        /// New visibility, if any.
1239        new_modifier: Option<Visibility>,
1240        /// Alias name, if any.
1241        new_name: Option<Name>,
1242    },
1243}
1244
1245/// An interface declaration.
1246#[derive(Debug, Clone, Serialize)]
1247pub struct InterfaceDecl {
1248    /// Interface name.
1249    pub name: Ident,
1250    /// Extended interfaces.
1251    pub extends: Box<[Name]>,
1252    /// Interface body.
1253    #[serde(flatten)]
1254    pub body: ClassBody,
1255    /// Attributes on the interface.
1256    pub attributes: Box<[Attribute]>,
1257    /// Preceding doc comment.
1258    #[serde(skip_serializing_if = "Option::is_none")]
1259    pub doc_comment: Option<Comment>,
1260}
1261
1262/// A trait declaration.
1263#[derive(Debug, Clone, Serialize)]
1264pub struct TraitDecl {
1265    /// Trait name.
1266    pub name: Ident,
1267    /// Trait body.
1268    #[serde(flatten)]
1269    pub body: ClassBody,
1270    /// Attributes on the trait.
1271    pub attributes: Box<[Attribute]>,
1272    /// Preceding doc comment.
1273    #[serde(skip_serializing_if = "Option::is_none")]
1274    pub doc_comment: Option<Comment>,
1275}
1276
1277/// The braced member list of an enum.
1278#[derive(Debug, Clone, Serialize)]
1279pub struct EnumBody {
1280    /// Members in source order.
1281    pub members: Box<[EnumMember]>,
1282    /// Source span.
1283    #[serde(skip)]
1284    pub span: Span,
1285}
1286
1287/// An enum declaration.
1288#[derive(Debug, Clone, Serialize)]
1289pub struct EnumDecl {
1290    /// Enum name.
1291    pub name: Ident,
1292    /// Backing type of a backed enum.
1293    pub scalar_type: Option<Name>,
1294    /// Implemented interfaces.
1295    pub implements: Box<[Name]>,
1296    /// Enum body.
1297    #[serde(flatten)]
1298    pub body: EnumBody,
1299    /// Attributes on the enum.
1300    pub attributes: Box<[Attribute]>,
1301    /// Preceding doc comment.
1302    #[serde(skip_serializing_if = "Option::is_none")]
1303    pub doc_comment: Option<Comment>,
1304}
1305
1306/// A member of an enum body.
1307#[derive(Debug, Clone, Serialize)]
1308pub struct EnumMember {
1309    /// Member kind.
1310    pub kind: EnumMemberKind,
1311    /// Source span.
1312    pub span: Span,
1313}
1314
1315/// The kind of an enum member.
1316#[derive(Debug, Clone, Serialize)]
1317pub enum EnumMemberKind {
1318    /// Enum case.
1319    Case(EnumCase),
1320    /// Method declaration.
1321    Method(MethodDecl),
1322    /// Class constant declaration.
1323    ClassConst(ClassConstDecl),
1324    /// Trait use clause.
1325    TraitUse(TraitUseDecl),
1326}
1327
1328/// An enum `case`.
1329#[derive(Debug, Clone, Serialize)]
1330pub struct EnumCase {
1331    /// Case name.
1332    pub name: Ident,
1333    /// Backing value.
1334    pub value: Option<Expr>,
1335    /// Attributes on the case.
1336    pub attributes: Box<[Attribute]>,
1337    /// Preceding doc comment.
1338    #[serde(skip_serializing_if = "Option::is_none")]
1339    pub doc_comment: Option<Comment>,
1340}
1341
1342// ===========================================================================
1343// Conversion functions
1344// ===========================================================================
1345
1346fn owned_ident(ident: arena_ast::Ident<'_>) -> Ident {
1347    ident.as_str().map(|s| s.into())
1348}
1349
1350fn owned_name(name: &arena_ast::Name<'_, '_>) -> Name {
1351    match name {
1352        arena_ast::Name::Simple { value, span } => Name {
1353            parts: vec![Box::from(*value)].into_boxed_slice(),
1354            kind: NameKind::Unqualified,
1355            span: *span,
1356        },
1357        arena_ast::Name::Complex { parts, kind, span } => Name {
1358            parts: parts
1359                .iter()
1360                .map(|s| Box::from(*s))
1361                .collect::<Vec<_>>()
1362                .into_boxed_slice(),
1363            kind: *kind,
1364            span: *span,
1365        },
1366        arena_ast::Name::Error { span } => Name {
1367            parts: Box::from([]),
1368            kind: NameKind::Error,
1369            span: *span,
1370        },
1371    }
1372}
1373
1374fn owned_comment(c: &arena_ast::Comment<'_>) -> Comment {
1375    Comment {
1376        kind: c.kind,
1377        text: c.text.into(),
1378        span: c.span,
1379    }
1380}
1381
1382fn owned_opt_comment(c: &Option<arena_ast::Comment<'_>>) -> Option<Comment> {
1383    c.as_ref().map(owned_comment)
1384}
1385
1386fn owned_type_hint(th: &arena_ast::TypeHint<'_, '_>) -> TypeHint {
1387    TypeHint {
1388        kind: owned_type_hint_kind(&th.kind),
1389        span: th.span,
1390    }
1391}
1392
1393fn owned_type_hint_kind(k: &arena_ast::TypeHintKind<'_, '_>) -> TypeHintKind {
1394    match k {
1395        arena_ast::TypeHintKind::Named(n) => TypeHintKind::Named(owned_name(n)),
1396        arena_ast::TypeHintKind::Keyword(b, span) => TypeHintKind::Keyword(*b, *span),
1397        arena_ast::TypeHintKind::Nullable(inner) => {
1398            TypeHintKind::Nullable(Box::new(owned_type_hint(inner)))
1399        }
1400        arena_ast::TypeHintKind::Union(types) => TypeHintKind::Union(
1401            types
1402                .iter()
1403                .map(owned_type_hint)
1404                .collect::<Vec<_>>()
1405                .into_boxed_slice(),
1406        ),
1407        arena_ast::TypeHintKind::Intersection(types) => TypeHintKind::Intersection(
1408            types
1409                .iter()
1410                .map(owned_type_hint)
1411                .collect::<Vec<_>>()
1412                .into_boxed_slice(),
1413        ),
1414    }
1415}
1416
1417fn owned_arg(arg: &arena_ast::Arg<'_, '_>) -> Arg {
1418    Arg {
1419        name: arg.name.as_ref().map(owned_name),
1420        value: arg.value.as_ref().map(owned_expr),
1421        unpack: arg.unpack,
1422        by_ref: arg.by_ref,
1423        span: arg.span,
1424    }
1425}
1426
1427fn owned_args(args: &[arena_ast::Arg<'_, '_>]) -> Box<[Arg]> {
1428    args.iter()
1429        .map(owned_arg)
1430        .collect::<Vec<_>>()
1431        .into_boxed_slice()
1432}
1433
1434fn owned_attr(attr: &arena_ast::Attribute<'_, '_>) -> Attribute {
1435    Attribute {
1436        name: owned_name(&attr.name),
1437        args: owned_args(&attr.args),
1438        span: attr.span,
1439    }
1440}
1441
1442fn owned_attrs(attrs: &[arena_ast::Attribute<'_, '_>]) -> Box<[Attribute]> {
1443    attrs
1444        .iter()
1445        .map(owned_attr)
1446        .collect::<Vec<_>>()
1447        .into_boxed_slice()
1448}
1449
1450fn owned_string_parts(parts: &[arena_ast::StringPart<'_, '_>]) -> Box<[StringPart]> {
1451    parts
1452        .iter()
1453        .map(|p| match p {
1454            arena_ast::StringPart::Literal(s) => StringPart::Literal(Box::from(*s)),
1455            arena_ast::StringPart::Expr(e) => StringPart::Expr(owned_expr(e)),
1456        })
1457        .collect::<Vec<_>>()
1458        .into_boxed_slice()
1459}
1460
1461/// Convert an arena-allocated [`Expr`](crate::ast::Expr) into an [`Expr`].
1462pub fn to_owned_expr(expr: &arena_ast::Expr<'_, '_>) -> Expr {
1463    owned_expr(expr)
1464}
1465
1466/// Convert an arena-allocated [`Name`](crate::ast::Name) into a [`Name`].
1467pub fn to_owned_name(name: &arena_ast::Name<'_, '_>) -> Name {
1468    owned_name(name)
1469}
1470
1471/// Convert an arena-allocated [`TypeHint`](crate::ast::TypeHint) into a [`TypeHint`].
1472pub fn to_owned_type_hint(hint: &arena_ast::TypeHint<'_, '_>) -> TypeHint {
1473    owned_type_hint(hint)
1474}
1475
1476fn owned_expr(expr: &arena_ast::Expr<'_, '_>) -> Expr {
1477    Expr {
1478        kind: owned_expr_kind(&expr.kind),
1479        span: expr.span,
1480    }
1481}
1482
1483fn owned_exprs(exprs: &[arena_ast::Expr<'_, '_>]) -> Box<[Expr]> {
1484    exprs
1485        .iter()
1486        .map(owned_expr)
1487        .collect::<Vec<_>>()
1488        .into_boxed_slice()
1489}
1490
1491fn owned_expr_kind(k: &arena_ast::ExprKind<'_, '_>) -> ExprKind {
1492    match k {
1493        arena_ast::ExprKind::Int(v) => ExprKind::Int(*v),
1494        arena_ast::ExprKind::Float(v) => ExprKind::Float(*v),
1495        arena_ast::ExprKind::String(s) => ExprKind::String(Box::from(*s)),
1496        arena_ast::ExprKind::InterpolatedString(parts) => {
1497            ExprKind::InterpolatedString(owned_string_parts(parts))
1498        }
1499        arena_ast::ExprKind::Heredoc { label, parts } => ExprKind::Heredoc {
1500            label: Box::from(*label),
1501            parts: owned_string_parts(parts),
1502        },
1503        arena_ast::ExprKind::Nowdoc { label, value } => ExprKind::Nowdoc {
1504            label: Box::from(*label),
1505            value: Box::from(*value),
1506        },
1507        arena_ast::ExprKind::ShellExec(parts) => ExprKind::ShellExec(owned_string_parts(parts)),
1508        arena_ast::ExprKind::Bool(v) => ExprKind::Bool(*v),
1509        arena_ast::ExprKind::Null => ExprKind::Null,
1510        arena_ast::ExprKind::Variable(s) => ExprKind::Variable(s.as_str().into()),
1511        arena_ast::ExprKind::VariableVariable(inner) => {
1512            ExprKind::VariableVariable(Box::new(owned_expr(inner)))
1513        }
1514        arena_ast::ExprKind::Identifier(s) => ExprKind::Identifier(s.as_str().into()),
1515        arena_ast::ExprKind::Assign(a) => ExprKind::Assign(AssignExpr {
1516            target: Box::new(owned_expr(a.target)),
1517            op: a.op,
1518            value: Box::new(owned_expr(a.value)),
1519            by_ref: a.by_ref,
1520        }),
1521        arena_ast::ExprKind::Binary(b) => ExprKind::Binary(BinaryExpr {
1522            left: Box::new(owned_expr(b.left)),
1523            op: b.op,
1524            right: Box::new(owned_expr(b.right)),
1525        }),
1526        arena_ast::ExprKind::UnaryPrefix(u) => ExprKind::UnaryPrefix(UnaryPrefixExpr {
1527            op: u.op,
1528            operand: Box::new(owned_expr(u.operand)),
1529        }),
1530        arena_ast::ExprKind::UnaryPostfix(u) => ExprKind::UnaryPostfix(UnaryPostfixExpr {
1531            operand: Box::new(owned_expr(u.operand)),
1532            op: u.op,
1533        }),
1534        arena_ast::ExprKind::Ternary(t) => ExprKind::Ternary(TernaryExpr {
1535            condition: Box::new(owned_expr(t.condition)),
1536            then_expr: t.then_expr.map(|e| Box::new(owned_expr(e))),
1537            else_expr: Box::new(owned_expr(t.else_expr)),
1538        }),
1539        arena_ast::ExprKind::NullCoalesce(n) => ExprKind::NullCoalesce(NullCoalesceExpr {
1540            left: Box::new(owned_expr(n.left)),
1541            right: Box::new(owned_expr(n.right)),
1542        }),
1543        arena_ast::ExprKind::FunctionCall(f) => ExprKind::FunctionCall(FunctionCallExpr {
1544            name: Box::new(owned_expr(f.name)),
1545            args: owned_args(&f.args),
1546        }),
1547        arena_ast::ExprKind::Array(elems) => ExprKind::Array(
1548            elems
1549                .iter()
1550                .map(|e| ArrayElement {
1551                    key: e.key.as_ref().map(owned_expr),
1552                    value: owned_expr(&e.value),
1553                    unpack: e.unpack,
1554                    by_ref: e.by_ref,
1555                    span: e.span,
1556                })
1557                .collect::<Vec<_>>()
1558                .into_boxed_slice(),
1559        ),
1560        arena_ast::ExprKind::ArrayAccess(a) => ExprKind::ArrayAccess(ArrayAccessExpr {
1561            array: Box::new(owned_expr(a.array)),
1562            index: a.index.map(|e| Box::new(owned_expr(e))),
1563        }),
1564        arena_ast::ExprKind::Print(e) => ExprKind::Print(Box::new(owned_expr(e))),
1565        arena_ast::ExprKind::Parenthesized(e) => ExprKind::Parenthesized(Box::new(owned_expr(e))),
1566        arena_ast::ExprKind::Cast(kind, e) => ExprKind::Cast(*kind, Box::new(owned_expr(e))),
1567        arena_ast::ExprKind::ErrorSuppress(e) => ExprKind::ErrorSuppress(Box::new(owned_expr(e))),
1568        arena_ast::ExprKind::Isset(exprs) => ExprKind::Isset(owned_exprs(exprs)),
1569        arena_ast::ExprKind::Empty(e) => ExprKind::Empty(Box::new(owned_expr(e))),
1570        arena_ast::ExprKind::Include(kind, e) => ExprKind::Include(*kind, Box::new(owned_expr(e))),
1571        arena_ast::ExprKind::Eval(e) => ExprKind::Eval(Box::new(owned_expr(e))),
1572        arena_ast::ExprKind::Exit(e) => ExprKind::Exit(e.map(|e| Box::new(owned_expr(e)))),
1573        arena_ast::ExprKind::MagicConst(m) => ExprKind::MagicConst(*m),
1574        arena_ast::ExprKind::Clone(e) => ExprKind::Clone(Box::new(owned_expr(e))),
1575        arena_ast::ExprKind::CloneWith(obj, props) => {
1576            ExprKind::CloneWith(Box::new(owned_expr(obj)), Box::new(owned_expr(props)))
1577        }
1578        arena_ast::ExprKind::New(n) => ExprKind::New(NewExpr {
1579            class: Box::new(owned_expr(n.class)),
1580            args: owned_args(&n.args),
1581        }),
1582        arena_ast::ExprKind::PropertyAccess(p) => ExprKind::PropertyAccess(PropertyAccessExpr {
1583            object: Box::new(owned_expr(p.object)),
1584            property: Box::new(owned_expr(p.property)),
1585        }),
1586        arena_ast::ExprKind::NullsafePropertyAccess(p) => {
1587            ExprKind::NullsafePropertyAccess(PropertyAccessExpr {
1588                object: Box::new(owned_expr(p.object)),
1589                property: Box::new(owned_expr(p.property)),
1590            })
1591        }
1592        arena_ast::ExprKind::MethodCall(m) => ExprKind::MethodCall(Box::new(MethodCallExpr {
1593            object: Box::new(owned_expr(m.object)),
1594            method: Box::new(owned_expr(m.method)),
1595            args: owned_args(&m.args),
1596        })),
1597        arena_ast::ExprKind::NullsafeMethodCall(m) => {
1598            ExprKind::NullsafeMethodCall(Box::new(MethodCallExpr {
1599                object: Box::new(owned_expr(m.object)),
1600                method: Box::new(owned_expr(m.method)),
1601                args: owned_args(&m.args),
1602            }))
1603        }
1604        arena_ast::ExprKind::StaticPropertyAccess(s) => {
1605            ExprKind::StaticPropertyAccess(StaticAccessExpr {
1606                class: Box::new(owned_expr(s.class)),
1607                member: Box::new(owned_expr(s.member)),
1608            })
1609        }
1610        arena_ast::ExprKind::StaticMethodCall(s) => {
1611            ExprKind::StaticMethodCall(Box::new(StaticMethodCallExpr {
1612                class: Box::new(owned_expr(s.class)),
1613                method: Box::new(owned_expr(s.method)),
1614                args: owned_args(&s.args),
1615            }))
1616        }
1617        arena_ast::ExprKind::StaticDynMethodCall(s) => {
1618            ExprKind::StaticDynMethodCall(Box::new(StaticDynMethodCallExpr {
1619                class: Box::new(owned_expr(s.class)),
1620                method: Box::new(owned_expr(s.method)),
1621                args: owned_args(&s.args),
1622            }))
1623        }
1624        arena_ast::ExprKind::ClassConstAccess(s) => ExprKind::ClassConstAccess(StaticAccessExpr {
1625            class: Box::new(owned_expr(s.class)),
1626            member: Box::new(owned_expr(s.member)),
1627        }),
1628        arena_ast::ExprKind::ClassConstAccessDynamic { class, member } => {
1629            ExprKind::ClassConstAccessDynamic {
1630                class: Box::new(owned_expr(class)),
1631                member: Box::new(owned_expr(member)),
1632            }
1633        }
1634        arena_ast::ExprKind::StaticPropertyAccessDynamic { class, member } => {
1635            ExprKind::StaticPropertyAccessDynamic {
1636                class: Box::new(owned_expr(class)),
1637                member: Box::new(owned_expr(member)),
1638            }
1639        }
1640        arena_ast::ExprKind::Closure(c) => ExprKind::Closure(Box::new(ClosureExpr {
1641            is_static: c.is_static,
1642            by_ref: c.by_ref,
1643            params: owned_params(&c.params),
1644            use_vars: c
1645                .use_vars
1646                .iter()
1647                .map(|v| ClosureUseVar {
1648                    name: v.name.into(),
1649                    by_ref: v.by_ref,
1650                    span: v.span,
1651                })
1652                .collect::<Vec<_>>()
1653                .into_boxed_slice(),
1654            return_type: c.return_type.as_ref().map(owned_type_hint),
1655            body: Box::new(owned_block(c.body)),
1656            attributes: owned_attrs(&c.attributes),
1657        })),
1658        arena_ast::ExprKind::ArrowFunction(f) => {
1659            ExprKind::ArrowFunction(Box::new(ArrowFunctionExpr {
1660                is_static: f.is_static,
1661                by_ref: f.by_ref,
1662                params: owned_params(&f.params),
1663                return_type: f.return_type.as_ref().map(owned_type_hint),
1664                body: Box::new(owned_expr(f.body)),
1665                attributes: owned_attrs(&f.attributes),
1666            }))
1667        }
1668        arena_ast::ExprKind::Match(m) => ExprKind::Match(MatchExpr {
1669            subject: Box::new(owned_expr(m.subject)),
1670            arms: m
1671                .arms
1672                .iter()
1673                .map(|arm| MatchArm {
1674                    conditions: arm.conditions.as_ref().map(|conds| owned_exprs(conds)),
1675                    body: owned_expr(&arm.body),
1676                    span: arm.span,
1677                })
1678                .collect::<Vec<_>>()
1679                .into_boxed_slice(),
1680            brace_start: m.brace_start,
1681        }),
1682        arena_ast::ExprKind::ThrowExpr(e) => ExprKind::ThrowExpr(Box::new(owned_expr(e))),
1683        arena_ast::ExprKind::Yield(y) => ExprKind::Yield(YieldExpr {
1684            key: y.key.map(|e| Box::new(owned_expr(e))),
1685            value: y.value.map(|e| Box::new(owned_expr(e))),
1686            is_from: y.is_from,
1687        }),
1688        arena_ast::ExprKind::AnonymousClass(cls) => {
1689            ExprKind::AnonymousClass(Box::new(owned_class_decl(cls)))
1690        }
1691        arena_ast::ExprKind::CallableCreate(c) => ExprKind::CallableCreate(CallableCreateExpr {
1692            kind: match &c.kind {
1693                arena_ast::CallableCreateKind::Function(e) => {
1694                    CallableCreateKind::Function(Box::new(owned_expr(e)))
1695                }
1696                arena_ast::CallableCreateKind::Method { object, method } => {
1697                    CallableCreateKind::Method {
1698                        object: Box::new(owned_expr(object)),
1699                        method: Box::new(owned_expr(method)),
1700                    }
1701                }
1702                arena_ast::CallableCreateKind::NullsafeMethod { object, method } => {
1703                    CallableCreateKind::NullsafeMethod {
1704                        object: Box::new(owned_expr(object)),
1705                        method: Box::new(owned_expr(method)),
1706                    }
1707                }
1708                arena_ast::CallableCreateKind::StaticMethod { class, method } => {
1709                    CallableCreateKind::StaticMethod {
1710                        class: Box::new(owned_expr(class)),
1711                        method: Box::new(owned_expr(method)),
1712                    }
1713                }
1714            },
1715        }),
1716        arena_ast::ExprKind::Omit => ExprKind::Omit,
1717        arena_ast::ExprKind::Error => ExprKind::Error,
1718    }
1719}
1720
1721fn owned_param(p: &arena_ast::Param<'_, '_>) -> Param {
1722    Param {
1723        name: owned_ident(p.name),
1724        type_hint: p.type_hint.as_ref().map(owned_type_hint),
1725        default: p.default.as_ref().map(owned_expr),
1726        by_ref: p.by_ref,
1727        variadic: p.variadic,
1728        is_readonly: p.is_readonly,
1729        is_final: p.is_final,
1730        visibility: p.visibility,
1731        set_visibility: p.set_visibility,
1732        attributes: owned_attrs(&p.attributes),
1733        hooks: owned_hooks(&p.hooks),
1734        doc_comment: p.doc_comment.as_ref().map(owned_comment),
1735        span: p.span,
1736    }
1737}
1738
1739fn owned_params(params: &[arena_ast::Param<'_, '_>]) -> Box<[Param]> {
1740    params
1741        .iter()
1742        .map(owned_param)
1743        .collect::<Vec<_>>()
1744        .into_boxed_slice()
1745}
1746
1747fn owned_hook(h: &arena_ast::PropertyHook<'_, '_>) -> PropertyHook {
1748    PropertyHook {
1749        kind: h.kind,
1750        body: match &h.body {
1751            arena_ast::PropertyHookBody::Block(block) => {
1752                PropertyHookBody::Block(Box::new(owned_block(block)))
1753            }
1754            arena_ast::PropertyHookBody::Expression(e) => {
1755                PropertyHookBody::Expression(owned_expr(e))
1756            }
1757            arena_ast::PropertyHookBody::Abstract => PropertyHookBody::Abstract,
1758        },
1759        is_final: h.is_final,
1760        by_ref: h.by_ref,
1761        params: owned_params(&h.params),
1762        attributes: owned_attrs(&h.attributes),
1763        span: h.span,
1764    }
1765}
1766
1767fn owned_hooks(hooks: &[arena_ast::PropertyHook<'_, '_>]) -> Box<[PropertyHook]> {
1768    hooks
1769        .iter()
1770        .map(owned_hook)
1771        .collect::<Vec<_>>()
1772        .into_boxed_slice()
1773}
1774
1775fn owned_stmts(stmts: &[arena_ast::Stmt<'_, '_>]) -> Box<[Stmt]> {
1776    stmts
1777        .iter()
1778        .map(owned_stmt)
1779        .collect::<Vec<_>>()
1780        .into_boxed_slice()
1781}
1782
1783/// Convert an arena-allocated [`Stmt`](crate::ast::Stmt) into a [`Stmt`].
1784pub fn to_owned_stmt(stmt: &arena_ast::Stmt<'_, '_>) -> Stmt {
1785    owned_stmt(stmt)
1786}
1787
1788fn owned_block(block: &arena_ast::Block<'_, '_>) -> Block {
1789    Block {
1790        stmts: owned_stmts(&block.stmts),
1791        span: block.span,
1792    }
1793}
1794
1795fn owned_stmt(stmt: &arena_ast::Stmt<'_, '_>) -> Stmt {
1796    Stmt {
1797        kind: owned_stmt_kind(&stmt.kind),
1798        span: stmt.span,
1799        doc_comment: stmt.doc_comment.map(owned_comment),
1800    }
1801}
1802
1803fn owned_stmt_kind(k: &arena_ast::StmtKind<'_, '_>) -> StmtKind {
1804    match k {
1805        arena_ast::StmtKind::Expression(e) => StmtKind::Expression(Box::new(owned_expr(e))),
1806        arena_ast::StmtKind::Echo(exprs) => StmtKind::Echo(owned_exprs(exprs)),
1807        arena_ast::StmtKind::Return(e) => StmtKind::Return(e.map(|e| Box::new(owned_expr(e)))),
1808        arena_ast::StmtKind::Block(block) => StmtKind::Block(Box::new(owned_block(block))),
1809        arena_ast::StmtKind::If(s) => StmtKind::If(Box::new(IfStmt {
1810            condition: owned_expr(&s.condition),
1811            then_branch: Box::new(owned_stmt(s.then_branch)),
1812            elseif_branches: s
1813                .elseif_branches
1814                .iter()
1815                .map(|b| ElseIfBranch {
1816                    condition: owned_expr(&b.condition),
1817                    body: owned_stmt(&b.body),
1818                    span: b.span,
1819                })
1820                .collect::<Vec<_>>()
1821                .into_boxed_slice(),
1822            else_branch: s.else_branch.map(|b| Box::new(owned_stmt(b))),
1823            else_kw_start: s.else_kw_start,
1824            uses_alternative: s.uses_alternative,
1825        })),
1826        arena_ast::StmtKind::While(s) => StmtKind::While(Box::new(WhileStmt {
1827            condition: owned_expr(&s.condition),
1828            body: Box::new(owned_stmt(s.body)),
1829            uses_alternative: s.uses_alternative,
1830        })),
1831        arena_ast::StmtKind::For(s) => StmtKind::For(Box::new(ForStmt {
1832            init: owned_exprs(&s.init),
1833            condition: owned_exprs(&s.condition),
1834            update: owned_exprs(&s.update),
1835            body: Box::new(owned_stmt(s.body)),
1836            uses_alternative: s.uses_alternative,
1837        })),
1838        arena_ast::StmtKind::Foreach(s) => StmtKind::Foreach(Box::new(ForeachStmt {
1839            expr: owned_expr(&s.expr),
1840            key: s.key.as_ref().map(owned_expr),
1841            value: owned_expr(&s.value),
1842            body: Box::new(owned_stmt(s.body)),
1843            uses_alternative: s.uses_alternative,
1844        })),
1845        arena_ast::StmtKind::DoWhile(s) => StmtKind::DoWhile(Box::new(DoWhileStmt {
1846            body: Box::new(owned_stmt(s.body)),
1847            condition: owned_expr(&s.condition),
1848        })),
1849        arena_ast::StmtKind::Function(f) => StmtKind::Function(Box::new(owned_function_decl(f))),
1850        arena_ast::StmtKind::Break(e) => StmtKind::Break(e.map(|e| Box::new(owned_expr(e)))),
1851        arena_ast::StmtKind::Continue(e) => StmtKind::Continue(e.map(|e| Box::new(owned_expr(e)))),
1852        arena_ast::StmtKind::Switch(s) => StmtKind::Switch(Box::new(SwitchStmt {
1853            expr: owned_expr(&s.expr),
1854            body: SwitchBody {
1855                cases: s
1856                    .body
1857                    .cases
1858                    .iter()
1859                    .map(|c| SwitchCase {
1860                        value: c.value.as_ref().map(owned_expr),
1861                        body: owned_stmts(&c.body),
1862                        span: c.span,
1863                    })
1864                    .collect::<Vec<_>>()
1865                    .into_boxed_slice(),
1866                span: s.body.span,
1867            },
1868            uses_alternative: s.uses_alternative,
1869        })),
1870        arena_ast::StmtKind::Goto(ident) => StmtKind::Goto(owned_ident(*ident)),
1871        arena_ast::StmtKind::Label(s) => StmtKind::Label(Box::from(*s)),
1872        arena_ast::StmtKind::Declare(d) => StmtKind::Declare(Box::new(DeclareStmt {
1873            directives: d
1874                .directives
1875                .iter()
1876                .map(|(k, v)| (Box::from(*k), owned_expr(v)))
1877                .collect::<Vec<_>>()
1878                .into_boxed_slice(),
1879            body: d.body.map(|b| Box::new(owned_stmt(b))),
1880            uses_alternative: d.uses_alternative,
1881        })),
1882        arena_ast::StmtKind::Unset(exprs) => StmtKind::Unset(owned_exprs(exprs)),
1883        arena_ast::StmtKind::Throw(e) => StmtKind::Throw(Box::new(owned_expr(e))),
1884        arena_ast::StmtKind::TryCatch(t) => StmtKind::TryCatch(Box::new(TryCatchStmt {
1885            body: Box::new(owned_block(t.body)),
1886            catches: t
1887                .catches
1888                .iter()
1889                .map(|c| CatchClause {
1890                    types: c
1891                        .types
1892                        .iter()
1893                        .map(owned_name)
1894                        .collect::<Vec<_>>()
1895                        .into_boxed_slice(),
1896                    var: c.var.map(Box::from),
1897                    body: Box::new(owned_block(c.body)),
1898                    span: c.span,
1899                })
1900                .collect::<Vec<_>>()
1901                .into_boxed_slice(),
1902            finally: t.finally.map(|f| Box::new(owned_block(f))),
1903            finally_kw_start: t.finally_kw_start,
1904        })),
1905        arena_ast::StmtKind::Global(exprs) => StmtKind::Global(owned_exprs(exprs)),
1906        arena_ast::StmtKind::Class(cls) => StmtKind::Class(Box::new(owned_class_decl(cls))),
1907        arena_ast::StmtKind::Interface(iface) => {
1908            StmtKind::Interface(Box::new(owned_interface_decl(iface)))
1909        }
1910        arena_ast::StmtKind::Trait(tr) => StmtKind::Trait(Box::new(owned_trait_decl(tr))),
1911        arena_ast::StmtKind::Enum(en) => StmtKind::Enum(Box::new(owned_enum_decl(en))),
1912        arena_ast::StmtKind::Namespace(ns) => StmtKind::Namespace(Box::new(NamespaceDecl {
1913            name: ns.name.as_ref().map(owned_name),
1914            body: match &ns.body {
1915                arena_ast::NamespaceBody::Braced(block) => {
1916                    NamespaceBody::Braced(Box::new(owned_block(block)))
1917                }
1918                arena_ast::NamespaceBody::Simple => NamespaceBody::Simple,
1919            },
1920        })),
1921        arena_ast::StmtKind::Use(u) => StmtKind::Use(Box::new(UseDecl {
1922            kind: u.kind,
1923            uses: u
1924                .uses
1925                .iter()
1926                .map(|item| UseItem {
1927                    name: owned_name(&item.name),
1928                    alias: item.alias.map(Box::from),
1929                    kind: item.kind,
1930                    span: item.span,
1931                })
1932                .collect::<Vec<_>>()
1933                .into_boxed_slice(),
1934        })),
1935        arena_ast::StmtKind::Const(items) => StmtKind::Const(
1936            items
1937                .iter()
1938                .map(|item| ConstItem {
1939                    name: owned_ident(item.name),
1940                    value: owned_expr(&item.value),
1941                    attributes: owned_attrs(&item.attributes),
1942                    span: item.span,
1943                    doc_comment: owned_opt_comment(&item.doc_comment),
1944                })
1945                .collect::<Vec<_>>()
1946                .into_boxed_slice(),
1947        ),
1948        arena_ast::StmtKind::StaticVar(vars) => StmtKind::StaticVar(
1949            vars.iter()
1950                .map(|v| StaticVar {
1951                    name: owned_ident(v.name),
1952                    default: v.default.as_ref().map(owned_expr),
1953                    span: v.span,
1954                })
1955                .collect::<Vec<_>>()
1956                .into_boxed_slice(),
1957        ),
1958        arena_ast::StmtKind::HaltCompiler(s) => StmtKind::HaltCompiler(Box::from(*s)),
1959        arena_ast::StmtKind::Nop => StmtKind::Nop,
1960        arena_ast::StmtKind::InlineHtml(s) => StmtKind::InlineHtml(Box::from(*s)),
1961        arena_ast::StmtKind::Error => StmtKind::Error,
1962    }
1963}
1964
1965fn owned_function_decl(f: &arena_ast::FunctionDecl<'_, '_>) -> FunctionDecl {
1966    FunctionDecl {
1967        name: owned_ident(f.name),
1968        params: owned_params(&f.params),
1969        body: Box::new(owned_block(f.body)),
1970        return_type: f.return_type.as_ref().map(owned_type_hint),
1971        by_ref: f.by_ref,
1972        attributes: owned_attrs(&f.attributes),
1973        doc_comment: owned_opt_comment(&f.doc_comment),
1974    }
1975}
1976
1977fn owned_class_member(m: &arena_ast::ClassMember<'_, '_>) -> ClassMember {
1978    ClassMember {
1979        kind: match &m.kind {
1980            arena_ast::ClassMemberKind::Property(p) => ClassMemberKind::Property(PropertyDecl {
1981                name: owned_ident(p.name),
1982                visibility: p.visibility,
1983                set_visibility: p.set_visibility,
1984                is_static: p.is_static,
1985                is_readonly: p.is_readonly,
1986                type_hint: p.type_hint.as_ref().map(owned_type_hint),
1987                default: p.default.as_ref().map(owned_expr),
1988                attributes: owned_attrs(&p.attributes),
1989                hooks: owned_hooks(&p.hooks),
1990                doc_comment: owned_opt_comment(&p.doc_comment),
1991            }),
1992            arena_ast::ClassMemberKind::Method(m) => ClassMemberKind::Method(MethodDecl {
1993                name: owned_ident(m.name),
1994                visibility: m.visibility,
1995                is_static: m.is_static,
1996                is_abstract: m.is_abstract,
1997                is_final: m.is_final,
1998                by_ref: m.by_ref,
1999                params: owned_params(&m.params),
2000                return_type: m.return_type.as_ref().map(owned_type_hint),
2001                body: m.body.map(|b| Box::new(owned_block(b))),
2002                attributes: owned_attrs(&m.attributes),
2003                doc_comment: owned_opt_comment(&m.doc_comment),
2004            }),
2005            arena_ast::ClassMemberKind::ClassConst(c) => {
2006                ClassMemberKind::ClassConst(owned_class_const(c))
2007            }
2008            arena_ast::ClassMemberKind::TraitUse(t) => {
2009                ClassMemberKind::TraitUse(owned_trait_use(t))
2010            }
2011        },
2012        span: m.span,
2013    }
2014}
2015
2016fn owned_class_members(members: &[arena_ast::ClassMember<'_, '_>]) -> Box<[ClassMember]> {
2017    members
2018        .iter()
2019        .map(owned_class_member)
2020        .collect::<Vec<_>>()
2021        .into_boxed_slice()
2022}
2023
2024fn owned_class_const(c: &arena_ast::ClassConstDecl<'_, '_>) -> ClassConstDecl {
2025    ClassConstDecl {
2026        name: owned_ident(c.name),
2027        visibility: c.visibility,
2028        is_final: c.is_final,
2029        type_hint: c.type_hint.map(|th| Box::new(owned_type_hint(th))),
2030        value: owned_expr(&c.value),
2031        attributes: owned_attrs(&c.attributes),
2032        doc_comment: owned_opt_comment(&c.doc_comment),
2033    }
2034}
2035
2036fn owned_trait_use(t: &arena_ast::TraitUseDecl<'_, '_>) -> TraitUseDecl {
2037    TraitUseDecl {
2038        traits: t
2039            .traits
2040            .iter()
2041            .map(owned_name)
2042            .collect::<Vec<_>>()
2043            .into_boxed_slice(),
2044        adaptations: t
2045            .adaptations
2046            .iter()
2047            .map(|a| TraitAdaptation {
2048                kind: match &a.kind {
2049                    arena_ast::TraitAdaptationKind::Precedence {
2050                        trait_name,
2051                        method,
2052                        insteadof,
2053                    } => TraitAdaptationKind::Precedence {
2054                        trait_name: owned_name(trait_name),
2055                        method: owned_name(method),
2056                        insteadof: insteadof
2057                            .iter()
2058                            .map(owned_name)
2059                            .collect::<Vec<_>>()
2060                            .into_boxed_slice(),
2061                    },
2062                    arena_ast::TraitAdaptationKind::Alias {
2063                        trait_name,
2064                        method,
2065                        new_modifier,
2066                        new_name,
2067                    } => TraitAdaptationKind::Alias {
2068                        trait_name: trait_name.as_ref().map(owned_name),
2069                        method: owned_name(method),
2070                        new_modifier: *new_modifier,
2071                        new_name: new_name.as_ref().map(owned_name),
2072                    },
2073                },
2074                span: a.span,
2075            })
2076            .collect::<Vec<_>>()
2077            .into_boxed_slice(),
2078        adaptations_brace_start: t.adaptations_brace_start,
2079        doc_comment: owned_opt_comment(&t.doc_comment),
2080    }
2081}
2082
2083fn owned_class_decl(cls: &arena_ast::ClassDecl<'_, '_>) -> ClassDecl {
2084    ClassDecl {
2085        name: cls
2086            .name
2087            .map(|ident| Some(owned_ident(ident)))
2088            .unwrap_or(None),
2089        modifiers: cls.modifiers.clone(),
2090        extends: cls.extends.as_ref().map(owned_name),
2091        implements: cls
2092            .implements
2093            .iter()
2094            .map(owned_name)
2095            .collect::<Vec<_>>()
2096            .into_boxed_slice(),
2097        body: ClassBody {
2098            members: owned_class_members(&cls.body.members),
2099            span: cls.body.span,
2100        },
2101        attributes: owned_attrs(&cls.attributes),
2102        doc_comment: owned_opt_comment(&cls.doc_comment),
2103    }
2104}
2105
2106fn owned_interface_decl(iface: &arena_ast::InterfaceDecl<'_, '_>) -> InterfaceDecl {
2107    InterfaceDecl {
2108        name: owned_ident(iface.name),
2109        extends: iface
2110            .extends
2111            .iter()
2112            .map(owned_name)
2113            .collect::<Vec<_>>()
2114            .into_boxed_slice(),
2115        body: ClassBody {
2116            members: owned_class_members(&iface.body.members),
2117            span: iface.body.span,
2118        },
2119        attributes: owned_attrs(&iface.attributes),
2120        doc_comment: owned_opt_comment(&iface.doc_comment),
2121    }
2122}
2123
2124fn owned_trait_decl(tr: &arena_ast::TraitDecl<'_, '_>) -> TraitDecl {
2125    TraitDecl {
2126        name: owned_ident(tr.name),
2127        body: ClassBody {
2128            members: owned_class_members(&tr.body.members),
2129            span: tr.body.span,
2130        },
2131        attributes: owned_attrs(&tr.attributes),
2132        doc_comment: owned_opt_comment(&tr.doc_comment),
2133    }
2134}
2135
2136fn owned_enum_decl(en: &arena_ast::EnumDecl<'_, '_>) -> EnumDecl {
2137    EnumDecl {
2138        name: owned_ident(en.name),
2139        scalar_type: en.scalar_type.as_ref().map(owned_name),
2140        implements: en
2141            .implements
2142            .iter()
2143            .map(owned_name)
2144            .collect::<Vec<_>>()
2145            .into_boxed_slice(),
2146        body: EnumBody {
2147            members: en
2148                .body
2149                .members
2150                .iter()
2151                .map(|m| EnumMember {
2152                    kind: match &m.kind {
2153                        arena_ast::EnumMemberKind::Case(c) => EnumMemberKind::Case(EnumCase {
2154                            name: owned_ident(c.name),
2155                            value: c.value.as_ref().map(owned_expr),
2156                            attributes: owned_attrs(&c.attributes),
2157                            doc_comment: owned_opt_comment(&c.doc_comment),
2158                        }),
2159                        arena_ast::EnumMemberKind::Method(m) => {
2160                            EnumMemberKind::Method(MethodDecl {
2161                                name: owned_ident(m.name),
2162                                visibility: m.visibility,
2163                                is_static: m.is_static,
2164                                is_abstract: m.is_abstract,
2165                                is_final: m.is_final,
2166                                by_ref: m.by_ref,
2167                                params: owned_params(&m.params),
2168                                return_type: m.return_type.as_ref().map(owned_type_hint),
2169                                body: m.body.map(|b| Box::new(owned_block(b))),
2170                                attributes: owned_attrs(&m.attributes),
2171                                doc_comment: owned_opt_comment(&m.doc_comment),
2172                            })
2173                        }
2174                        arena_ast::EnumMemberKind::ClassConst(c) => {
2175                            EnumMemberKind::ClassConst(owned_class_const(c))
2176                        }
2177                        arena_ast::EnumMemberKind::TraitUse(t) => {
2178                            EnumMemberKind::TraitUse(owned_trait_use(t))
2179                        }
2180                    },
2181                    span: m.span,
2182                })
2183                .collect::<Vec<_>>()
2184                .into_boxed_slice(),
2185            span: en.body.span,
2186        },
2187        attributes: owned_attrs(&en.attributes),
2188        doc_comment: owned_opt_comment(&en.doc_comment),
2189    }
2190}
2191
2192/// Convert an arena-allocated [`Program`](crate::ast::Program) into a [`Program`].
2193pub fn to_owned_program(program: &arena_ast::Program<'_, '_>) -> Program {
2194    Program {
2195        stmts: owned_stmts(&program.stmts),
2196        span: program.span,
2197    }
2198}
2199
2200// ===========================================================================
2201// Reverse conversion: owned → arena
2202// ===========================================================================
2203
2204/// Convert an owned [`Program`] into an arena-allocated
2205/// [`Program`](crate::ast::Program).
2206///
2207/// All strings are copied into `arena`. Both lifetime parameters of the
2208/// returned type are `'arena` because every string originates in the arena
2209/// rather than a source buffer.
2210///
2211/// Used internally by the pretty printer so callers can pass an owned program
2212/// without round-tripping through `parse_arena()`.
2213pub fn from_owned_program<'arena>(
2214    arena: &'arena bumpalo::Bump,
2215    program: &Program,
2216) -> arena_ast::Program<'arena, 'arena> {
2217    arena_ast::Program {
2218        stmts: av_stmts(arena, &program.stmts),
2219        span: program.span,
2220    }
2221}
2222
2223// Helper: allocate a string in the arena.
2224#[inline]
2225fn av_str<'a>(arena: &'a bumpalo::Bump, s: &str) -> &'a str {
2226    arena.alloc_str(s)
2227}
2228
2229fn av_ident<'a>(arena: &'a bumpalo::Bump, ident: &Ident) -> arena_ast::Ident<'a> {
2230    match ident {
2231        Some(s) => arena_ast::Ident::name(arena.alloc_str(s)),
2232        None => arena_ast::Ident::ERROR,
2233    }
2234}
2235
2236fn av_name<'a>(arena: &'a bumpalo::Bump, name: &Name) -> arena_ast::Name<'a, 'a> {
2237    if name.kind == NameKind::Error {
2238        return arena_ast::Name::Error { span: name.span };
2239    }
2240    if name.parts.len() == 1 && name.kind == NameKind::Unqualified {
2241        return arena_ast::Name::Simple {
2242            value: arena.alloc_str(&name.parts[0]),
2243            span: name.span,
2244        };
2245    }
2246    let mut parts = arena_ast::ArenaVec::with_capacity_in(name.parts.len(), arena);
2247    for p in name.parts.iter() {
2248        parts.push(arena.alloc_str(p) as &str);
2249    }
2250    arena_ast::Name::Complex {
2251        parts,
2252        kind: name.kind,
2253        span: name.span,
2254    }
2255}
2256
2257fn av_type_hint<'a>(arena: &'a bumpalo::Bump, th: &TypeHint) -> arena_ast::TypeHint<'a, 'a> {
2258    arena_ast::TypeHint {
2259        kind: av_type_hint_kind(arena, &th.kind),
2260        span: th.span,
2261    }
2262}
2263
2264fn av_type_hint_kind<'a>(
2265    arena: &'a bumpalo::Bump,
2266    k: &TypeHintKind,
2267) -> arena_ast::TypeHintKind<'a, 'a> {
2268    match k {
2269        TypeHintKind::Named(n) => arena_ast::TypeHintKind::Named(av_name(arena, n)),
2270        TypeHintKind::Keyword(b, span) => arena_ast::TypeHintKind::Keyword(*b, *span),
2271        TypeHintKind::Nullable(inner) => {
2272            arena_ast::TypeHintKind::Nullable(arena.alloc(av_type_hint(arena, inner)))
2273        }
2274        TypeHintKind::Union(types) => {
2275            let mut v = arena_ast::ArenaVec::with_capacity_in(types.len(), arena);
2276            for t in types.iter() {
2277                v.push(av_type_hint(arena, t));
2278            }
2279            arena_ast::TypeHintKind::Union(v)
2280        }
2281        TypeHintKind::Intersection(types) => {
2282            let mut v = arena_ast::ArenaVec::with_capacity_in(types.len(), arena);
2283            for t in types.iter() {
2284                v.push(av_type_hint(arena, t));
2285            }
2286            arena_ast::TypeHintKind::Intersection(v)
2287        }
2288    }
2289}
2290
2291fn av_arg<'a>(arena: &'a bumpalo::Bump, arg: &Arg) -> arena_ast::Arg<'a, 'a> {
2292    arena_ast::Arg {
2293        name: arg.name.as_ref().map(|n| av_name(arena, n)),
2294        value: arg.value.as_ref().map(|v| av_expr(arena, v)),
2295        unpack: arg.unpack,
2296        by_ref: arg.by_ref,
2297        span: arg.span,
2298    }
2299}
2300
2301fn av_args<'a>(
2302    arena: &'a bumpalo::Bump,
2303    args: &[Arg],
2304) -> arena_ast::ArenaVec<'a, arena_ast::Arg<'a, 'a>> {
2305    let mut v = arena_ast::ArenaVec::with_capacity_in(args.len(), arena);
2306    for a in args.iter() {
2307        v.push(av_arg(arena, a));
2308    }
2309    v
2310}
2311
2312fn av_attr<'a>(arena: &'a bumpalo::Bump, attr: &Attribute) -> arena_ast::Attribute<'a, 'a> {
2313    arena_ast::Attribute {
2314        name: av_name(arena, &attr.name),
2315        args: av_args(arena, &attr.args),
2316        span: attr.span,
2317    }
2318}
2319
2320fn av_attrs<'a>(
2321    arena: &'a bumpalo::Bump,
2322    attrs: &[Attribute],
2323) -> arena_ast::ArenaVec<'a, arena_ast::Attribute<'a, 'a>> {
2324    let mut v = arena_ast::ArenaVec::with_capacity_in(attrs.len(), arena);
2325    for a in attrs.iter() {
2326        v.push(av_attr(arena, a));
2327    }
2328    v
2329}
2330
2331fn av_comment<'a>(arena: &'a bumpalo::Bump, c: &Comment) -> arena_ast::Comment<'a> {
2332    arena_ast::Comment {
2333        kind: c.kind,
2334        text: arena.alloc_str(&c.text),
2335        span: c.span,
2336    }
2337}
2338
2339fn av_opt_comment<'a>(
2340    arena: &'a bumpalo::Bump,
2341    c: &Option<Comment>,
2342) -> Option<arena_ast::Comment<'a>> {
2343    c.as_ref().map(|c| av_comment(arena, c))
2344}
2345
2346fn av_string_parts<'a>(
2347    arena: &'a bumpalo::Bump,
2348    parts: &[StringPart],
2349) -> arena_ast::ArenaVec<'a, arena_ast::StringPart<'a, 'a>> {
2350    let mut v = arena_ast::ArenaVec::with_capacity_in(parts.len(), arena);
2351    for p in parts.iter() {
2352        v.push(match p {
2353            StringPart::Literal(s) => arena_ast::StringPart::Literal(arena.alloc_str(s)),
2354            StringPart::Expr(e) => arena_ast::StringPart::Expr(av_expr(arena, e)),
2355        });
2356    }
2357    v
2358}
2359
2360fn av_expr<'a>(arena: &'a bumpalo::Bump, expr: &Expr) -> arena_ast::Expr<'a, 'a> {
2361    arena_ast::Expr {
2362        kind: av_expr_kind(arena, &expr.kind),
2363        span: expr.span,
2364    }
2365}
2366
2367fn av_exprs<'a>(
2368    arena: &'a bumpalo::Bump,
2369    exprs: &[Expr],
2370) -> arena_ast::ArenaVec<'a, arena_ast::Expr<'a, 'a>> {
2371    let mut v = arena_ast::ArenaVec::with_capacity_in(exprs.len(), arena);
2372    for e in exprs.iter() {
2373        v.push(av_expr(arena, e));
2374    }
2375    v
2376}
2377
2378fn av_namestr<'a>(arena: &'a bumpalo::Bump, s: &str) -> arena_ast::NameStr<'a, 'a> {
2379    arena_ast::NameStr::__arena(arena.alloc_str(s))
2380}
2381
2382fn av_expr_kind<'a>(arena: &'a bumpalo::Bump, k: &ExprKind) -> arena_ast::ExprKind<'a, 'a> {
2383    match k {
2384        ExprKind::Int(v) => arena_ast::ExprKind::Int(*v),
2385        ExprKind::Float(v) => arena_ast::ExprKind::Float(*v),
2386        ExprKind::String(s) => arena_ast::ExprKind::String(arena.alloc_str(s)),
2387        ExprKind::InterpolatedString(parts) => {
2388            arena_ast::ExprKind::InterpolatedString(av_string_parts(arena, parts))
2389        }
2390        ExprKind::Heredoc { label, parts } => arena_ast::ExprKind::Heredoc {
2391            label: arena.alloc_str(label),
2392            parts: av_string_parts(arena, parts),
2393        },
2394        ExprKind::Nowdoc { label, value } => arena_ast::ExprKind::Nowdoc {
2395            label: arena.alloc_str(label),
2396            value: arena.alloc_str(value),
2397        },
2398        ExprKind::ShellExec(parts) => arena_ast::ExprKind::ShellExec(av_string_parts(arena, parts)),
2399        ExprKind::Bool(v) => arena_ast::ExprKind::Bool(*v),
2400        ExprKind::Null => arena_ast::ExprKind::Null,
2401        ExprKind::Variable(s) => arena_ast::ExprKind::Variable(av_namestr(arena, s)),
2402        ExprKind::VariableVariable(inner) => {
2403            arena_ast::ExprKind::VariableVariable(arena.alloc(av_expr(arena, inner)))
2404        }
2405        ExprKind::Identifier(s) => arena_ast::ExprKind::Identifier(av_namestr(arena, s)),
2406        ExprKind::Assign(a) => arena_ast::ExprKind::Assign(arena_ast::AssignExpr {
2407            target: arena.alloc(av_expr(arena, &a.target)),
2408            op: a.op,
2409            value: arena.alloc(av_expr(arena, &a.value)),
2410            by_ref: a.by_ref,
2411        }),
2412        ExprKind::Binary(b) => arena_ast::ExprKind::Binary(arena_ast::BinaryExpr {
2413            left: arena.alloc(av_expr(arena, &b.left)),
2414            op: b.op,
2415            right: arena.alloc(av_expr(arena, &b.right)),
2416        }),
2417        ExprKind::UnaryPrefix(u) => arena_ast::ExprKind::UnaryPrefix(arena_ast::UnaryPrefixExpr {
2418            op: u.op,
2419            operand: arena.alloc(av_expr(arena, &u.operand)),
2420        }),
2421        ExprKind::UnaryPostfix(u) => {
2422            arena_ast::ExprKind::UnaryPostfix(arena_ast::UnaryPostfixExpr {
2423                operand: arena.alloc(av_expr(arena, &u.operand)),
2424                op: u.op,
2425            })
2426        }
2427        ExprKind::Ternary(t) => arena_ast::ExprKind::Ternary(arena_ast::TernaryExpr {
2428            condition: arena.alloc(av_expr(arena, &t.condition)),
2429            then_expr: t
2430                .then_expr
2431                .as_ref()
2432                .map(|e| &*arena.alloc(av_expr(arena, e))),
2433            else_expr: arena.alloc(av_expr(arena, &t.else_expr)),
2434        }),
2435        ExprKind::NullCoalesce(n) => {
2436            arena_ast::ExprKind::NullCoalesce(arena_ast::NullCoalesceExpr {
2437                left: arena.alloc(av_expr(arena, &n.left)),
2438                right: arena.alloc(av_expr(arena, &n.right)),
2439            })
2440        }
2441        ExprKind::FunctionCall(f) => {
2442            arena_ast::ExprKind::FunctionCall(arena_ast::FunctionCallExpr {
2443                name: arena.alloc(av_expr(arena, &f.name)),
2444                args: av_args(arena, &f.args),
2445            })
2446        }
2447        ExprKind::Array(elems) => {
2448            let mut v = arena_ast::ArenaVec::with_capacity_in(elems.len(), arena);
2449            for e in elems.iter() {
2450                v.push(arena_ast::ArrayElement {
2451                    key: e.key.as_ref().map(|k| av_expr(arena, k)),
2452                    value: av_expr(arena, &e.value),
2453                    unpack: e.unpack,
2454                    by_ref: e.by_ref,
2455                    span: e.span,
2456                });
2457            }
2458            arena_ast::ExprKind::Array(v)
2459        }
2460        ExprKind::ArrayAccess(a) => arena_ast::ExprKind::ArrayAccess(arena_ast::ArrayAccessExpr {
2461            array: arena.alloc(av_expr(arena, &a.array)),
2462            index: a.index.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
2463        }),
2464        ExprKind::Print(e) => arena_ast::ExprKind::Print(arena.alloc(av_expr(arena, e))),
2465        ExprKind::Parenthesized(e) => {
2466            arena_ast::ExprKind::Parenthesized(arena.alloc(av_expr(arena, e)))
2467        }
2468        ExprKind::Cast(kind, e) => arena_ast::ExprKind::Cast(*kind, arena.alloc(av_expr(arena, e))),
2469        ExprKind::ErrorSuppress(e) => {
2470            arena_ast::ExprKind::ErrorSuppress(arena.alloc(av_expr(arena, e)))
2471        }
2472        ExprKind::Isset(exprs) => arena_ast::ExprKind::Isset(av_exprs(arena, exprs)),
2473        ExprKind::Empty(e) => arena_ast::ExprKind::Empty(arena.alloc(av_expr(arena, e))),
2474        ExprKind::Include(kind, e) => {
2475            arena_ast::ExprKind::Include(*kind, arena.alloc(av_expr(arena, e)))
2476        }
2477        ExprKind::Eval(e) => arena_ast::ExprKind::Eval(arena.alloc(av_expr(arena, e))),
2478        ExprKind::Exit(e) => {
2479            arena_ast::ExprKind::Exit(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2480        }
2481        ExprKind::MagicConst(m) => arena_ast::ExprKind::MagicConst(*m),
2482        ExprKind::Clone(e) => arena_ast::ExprKind::Clone(arena.alloc(av_expr(arena, e))),
2483        ExprKind::CloneWith(obj, props) => arena_ast::ExprKind::CloneWith(
2484            arena.alloc(av_expr(arena, obj)),
2485            arena.alloc(av_expr(arena, props)),
2486        ),
2487        ExprKind::New(n) => arena_ast::ExprKind::New(arena_ast::NewExpr {
2488            class: arena.alloc(av_expr(arena, &n.class)),
2489            args: av_args(arena, &n.args),
2490        }),
2491        ExprKind::PropertyAccess(p) => {
2492            arena_ast::ExprKind::PropertyAccess(arena_ast::PropertyAccessExpr {
2493                object: arena.alloc(av_expr(arena, &p.object)),
2494                property: arena.alloc(av_expr(arena, &p.property)),
2495            })
2496        }
2497        ExprKind::NullsafePropertyAccess(p) => {
2498            arena_ast::ExprKind::NullsafePropertyAccess(arena_ast::PropertyAccessExpr {
2499                object: arena.alloc(av_expr(arena, &p.object)),
2500                property: arena.alloc(av_expr(arena, &p.property)),
2501            })
2502        }
2503        ExprKind::MethodCall(m) => {
2504            arena_ast::ExprKind::MethodCall(arena.alloc(arena_ast::MethodCallExpr {
2505                object: arena.alloc(av_expr(arena, &m.object)),
2506                method: arena.alloc(av_expr(arena, &m.method)),
2507                args: av_args(arena, &m.args),
2508            }))
2509        }
2510        ExprKind::NullsafeMethodCall(m) => {
2511            arena_ast::ExprKind::NullsafeMethodCall(arena.alloc(arena_ast::MethodCallExpr {
2512                object: arena.alloc(av_expr(arena, &m.object)),
2513                method: arena.alloc(av_expr(arena, &m.method)),
2514                args: av_args(arena, &m.args),
2515            }))
2516        }
2517        ExprKind::StaticPropertyAccess(s) => {
2518            arena_ast::ExprKind::StaticPropertyAccess(arena_ast::StaticAccessExpr {
2519                class: arena.alloc(av_expr(arena, &s.class)),
2520                member: arena.alloc(av_expr(arena, &s.member)),
2521            })
2522        }
2523        ExprKind::StaticMethodCall(s) => {
2524            arena_ast::ExprKind::StaticMethodCall(arena.alloc(arena_ast::StaticMethodCallExpr {
2525                class: arena.alloc(av_expr(arena, &s.class)),
2526                method: arena.alloc(av_expr(arena, &s.method)),
2527                args: av_args(arena, &s.args),
2528            }))
2529        }
2530        ExprKind::StaticDynMethodCall(s) => arena_ast::ExprKind::StaticDynMethodCall(arena.alloc(
2531            arena_ast::StaticDynMethodCallExpr {
2532                class: arena.alloc(av_expr(arena, &s.class)),
2533                method: arena.alloc(av_expr(arena, &s.method)),
2534                args: av_args(arena, &s.args),
2535            },
2536        )),
2537        ExprKind::ClassConstAccess(s) => {
2538            arena_ast::ExprKind::ClassConstAccess(arena_ast::StaticAccessExpr {
2539                class: arena.alloc(av_expr(arena, &s.class)),
2540                member: arena.alloc(av_expr(arena, &s.member)),
2541            })
2542        }
2543        ExprKind::ClassConstAccessDynamic { class, member } => {
2544            arena_ast::ExprKind::ClassConstAccessDynamic {
2545                class: arena.alloc(av_expr(arena, class)),
2546                member: arena.alloc(av_expr(arena, member)),
2547            }
2548        }
2549        ExprKind::StaticPropertyAccessDynamic { class, member } => {
2550            arena_ast::ExprKind::StaticPropertyAccessDynamic {
2551                class: arena.alloc(av_expr(arena, class)),
2552                member: arena.alloc(av_expr(arena, member)),
2553            }
2554        }
2555        ExprKind::Closure(c) => arena_ast::ExprKind::Closure(arena.alloc(arena_ast::ClosureExpr {
2556            is_static: c.is_static,
2557            by_ref: c.by_ref,
2558            params: av_params(arena, &c.params),
2559            use_vars: {
2560                let mut v = arena_ast::ArenaVec::with_capacity_in(c.use_vars.len(), arena);
2561                for uv in c.use_vars.iter() {
2562                    v.push(arena_ast::ClosureUseVar {
2563                        name: arena.alloc_str(&uv.name),
2564                        by_ref: uv.by_ref,
2565                        span: uv.span,
2566                    });
2567                }
2568                v
2569            },
2570            return_type: c.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2571            body: &*arena.alloc(av_block(arena, &c.body)),
2572            attributes: av_attrs(arena, &c.attributes),
2573        })),
2574        ExprKind::ArrowFunction(f) => {
2575            arena_ast::ExprKind::ArrowFunction(arena.alloc(arena_ast::ArrowFunctionExpr {
2576                is_static: f.is_static,
2577                by_ref: f.by_ref,
2578                params: av_params(arena, &f.params),
2579                return_type: f.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2580                body: arena.alloc(av_expr(arena, &f.body)),
2581                attributes: av_attrs(arena, &f.attributes),
2582            }))
2583        }
2584        ExprKind::Match(m) => arena_ast::ExprKind::Match(arena_ast::MatchExpr {
2585            subject: arena.alloc(av_expr(arena, &m.subject)),
2586            arms: {
2587                let mut v = arena_ast::ArenaVec::with_capacity_in(m.arms.len(), arena);
2588                for arm in m.arms.iter() {
2589                    v.push(arena_ast::MatchArm {
2590                        conditions: arm.conditions.as_ref().map(|conds| av_exprs(arena, conds)),
2591                        body: av_expr(arena, &arm.body),
2592                        span: arm.span,
2593                    });
2594                }
2595                v
2596            },
2597            brace_start: m.brace_start,
2598        }),
2599        ExprKind::ThrowExpr(e) => arena_ast::ExprKind::ThrowExpr(arena.alloc(av_expr(arena, e))),
2600        ExprKind::Yield(y) => arena_ast::ExprKind::Yield(arena_ast::YieldExpr {
2601            key: y.key.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
2602            value: y.value.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))),
2603            is_from: y.is_from,
2604        }),
2605        ExprKind::AnonymousClass(cls) => {
2606            arena_ast::ExprKind::AnonymousClass(arena.alloc(av_class_decl(arena, cls)))
2607        }
2608        ExprKind::CallableCreate(c) => {
2609            arena_ast::ExprKind::CallableCreate(arena_ast::CallableCreateExpr {
2610                kind: match &c.kind {
2611                    CallableCreateKind::Function(e) => {
2612                        arena_ast::CallableCreateKind::Function(arena.alloc(av_expr(arena, e)))
2613                    }
2614                    CallableCreateKind::Method { object, method } => {
2615                        arena_ast::CallableCreateKind::Method {
2616                            object: arena.alloc(av_expr(arena, object)),
2617                            method: arena.alloc(av_expr(arena, method)),
2618                        }
2619                    }
2620                    CallableCreateKind::NullsafeMethod { object, method } => {
2621                        arena_ast::CallableCreateKind::NullsafeMethod {
2622                            object: arena.alloc(av_expr(arena, object)),
2623                            method: arena.alloc(av_expr(arena, method)),
2624                        }
2625                    }
2626                    CallableCreateKind::StaticMethod { class, method } => {
2627                        arena_ast::CallableCreateKind::StaticMethod {
2628                            class: arena.alloc(av_expr(arena, class)),
2629                            method: arena.alloc(av_expr(arena, method)),
2630                        }
2631                    }
2632                },
2633            })
2634        }
2635        ExprKind::Omit => arena_ast::ExprKind::Omit,
2636        ExprKind::Error => arena_ast::ExprKind::Error,
2637    }
2638}
2639
2640fn av_param<'a>(arena: &'a bumpalo::Bump, p: &Param) -> arena_ast::Param<'a, 'a> {
2641    arena_ast::Param {
2642        name: av_ident(arena, &p.name),
2643        type_hint: p.type_hint.as_ref().map(|t| av_type_hint(arena, t)),
2644        default: p.default.as_ref().map(|e| av_expr(arena, e)),
2645        by_ref: p.by_ref,
2646        variadic: p.variadic,
2647        is_readonly: p.is_readonly,
2648        is_final: p.is_final,
2649        visibility: p.visibility,
2650        set_visibility: p.set_visibility,
2651        attributes: av_attrs(arena, &p.attributes),
2652        hooks: av_hooks(arena, &p.hooks),
2653        doc_comment: av_opt_comment(arena, &p.doc_comment),
2654        span: p.span,
2655    }
2656}
2657
2658fn av_params<'a>(
2659    arena: &'a bumpalo::Bump,
2660    params: &[Param],
2661) -> arena_ast::ArenaVec<'a, arena_ast::Param<'a, 'a>> {
2662    let mut v = arena_ast::ArenaVec::with_capacity_in(params.len(), arena);
2663    for p in params.iter() {
2664        v.push(av_param(arena, p));
2665    }
2666    v
2667}
2668
2669fn av_hook<'a>(arena: &'a bumpalo::Bump, h: &PropertyHook) -> arena_ast::PropertyHook<'a, 'a> {
2670    arena_ast::PropertyHook {
2671        kind: h.kind,
2672        body: match &h.body {
2673            PropertyHookBody::Block(block) => {
2674                arena_ast::PropertyHookBody::Block(arena.alloc(av_block(arena, block)))
2675            }
2676            PropertyHookBody::Expression(e) => {
2677                arena_ast::PropertyHookBody::Expression(av_expr(arena, e))
2678            }
2679            PropertyHookBody::Abstract => arena_ast::PropertyHookBody::Abstract,
2680        },
2681        is_final: h.is_final,
2682        by_ref: h.by_ref,
2683        params: av_params(arena, &h.params),
2684        attributes: av_attrs(arena, &h.attributes),
2685        span: h.span,
2686    }
2687}
2688
2689fn av_hooks<'a>(
2690    arena: &'a bumpalo::Bump,
2691    hooks: &[PropertyHook],
2692) -> arena_ast::ArenaVec<'a, arena_ast::PropertyHook<'a, 'a>> {
2693    let mut v = arena_ast::ArenaVec::with_capacity_in(hooks.len(), arena);
2694    for h in hooks.iter() {
2695        v.push(av_hook(arena, h));
2696    }
2697    v
2698}
2699
2700fn av_stmt<'a>(arena: &'a bumpalo::Bump, stmt: &Stmt) -> arena_ast::Stmt<'a, 'a> {
2701    arena_ast::Stmt {
2702        kind: av_stmt_kind(arena, &stmt.kind),
2703        span: stmt.span,
2704        doc_comment: stmt.doc_comment.as_ref().map(|c| {
2705            &*arena.alloc(arena_ast::Comment {
2706                kind: c.kind,
2707                text: arena.alloc_str(&c.text),
2708                span: c.span,
2709            })
2710        }),
2711    }
2712}
2713
2714fn av_stmts<'a>(
2715    arena: &'a bumpalo::Bump,
2716    stmts: &[Stmt],
2717) -> arena_ast::ArenaVec<'a, arena_ast::Stmt<'a, 'a>> {
2718    let mut v = arena_ast::ArenaVec::with_capacity_in(stmts.len(), arena);
2719    for s in stmts.iter() {
2720        v.push(av_stmt(arena, s));
2721    }
2722    v
2723}
2724
2725fn av_block<'a>(arena: &'a bumpalo::Bump, block: &Block) -> arena_ast::Block<'a, 'a> {
2726    arena_ast::Block {
2727        stmts: av_stmts(arena, &block.stmts),
2728        span: block.span,
2729    }
2730}
2731
2732fn av_stmt_kind<'a>(arena: &'a bumpalo::Bump, k: &StmtKind) -> arena_ast::StmtKind<'a, 'a> {
2733    match k {
2734        StmtKind::Expression(e) => arena_ast::StmtKind::Expression(arena.alloc(av_expr(arena, e))),
2735        StmtKind::Echo(exprs) => arena_ast::StmtKind::Echo(av_exprs(arena, exprs)),
2736        StmtKind::Return(e) => {
2737            arena_ast::StmtKind::Return(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2738        }
2739        StmtKind::Block(block) => arena_ast::StmtKind::Block(arena.alloc(av_block(arena, block))),
2740        StmtKind::If(s) => arena_ast::StmtKind::If(
2741            arena.alloc(arena_ast::IfStmt {
2742                condition: av_expr(arena, &s.condition),
2743                then_branch: arena.alloc(av_stmt(arena, &s.then_branch)),
2744                elseif_branches: {
2745                    let mut v =
2746                        arena_ast::ArenaVec::with_capacity_in(s.elseif_branches.len(), arena);
2747                    for b in s.elseif_branches.iter() {
2748                        v.push(arena_ast::ElseIfBranch {
2749                            condition: av_expr(arena, &b.condition),
2750                            body: av_stmt(arena, &b.body),
2751                            span: b.span,
2752                        });
2753                    }
2754                    v
2755                },
2756                else_branch: s
2757                    .else_branch
2758                    .as_ref()
2759                    .map(|b| &*arena.alloc(av_stmt(arena, b))),
2760                else_kw_start: s.else_kw_start,
2761                uses_alternative: s.uses_alternative,
2762            }),
2763        ),
2764        StmtKind::While(s) => arena_ast::StmtKind::While(arena.alloc(arena_ast::WhileStmt {
2765            condition: av_expr(arena, &s.condition),
2766            body: arena.alloc(av_stmt(arena, &s.body)),
2767            uses_alternative: s.uses_alternative,
2768        })),
2769        StmtKind::For(s) => arena_ast::StmtKind::For(arena.alloc(arena_ast::ForStmt {
2770            init: av_exprs(arena, &s.init),
2771            condition: av_exprs(arena, &s.condition),
2772            update: av_exprs(arena, &s.update),
2773            body: arena.alloc(av_stmt(arena, &s.body)),
2774            uses_alternative: s.uses_alternative,
2775        })),
2776        StmtKind::Foreach(s) => arena_ast::StmtKind::Foreach(arena.alloc(arena_ast::ForeachStmt {
2777            expr: av_expr(arena, &s.expr),
2778            key: s.key.as_ref().map(|e| av_expr(arena, e)),
2779            value: av_expr(arena, &s.value),
2780            body: arena.alloc(av_stmt(arena, &s.body)),
2781            uses_alternative: s.uses_alternative,
2782        })),
2783        StmtKind::DoWhile(s) => arena_ast::StmtKind::DoWhile(arena.alloc(arena_ast::DoWhileStmt {
2784            body: arena.alloc(av_stmt(arena, &s.body)),
2785            condition: av_expr(arena, &s.condition),
2786        })),
2787        StmtKind::Function(f) => {
2788            arena_ast::StmtKind::Function(arena.alloc(av_function_decl(arena, f)))
2789        }
2790        StmtKind::Break(e) => {
2791            arena_ast::StmtKind::Break(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2792        }
2793        StmtKind::Continue(e) => {
2794            arena_ast::StmtKind::Continue(e.as_ref().map(|e| &*arena.alloc(av_expr(arena, e))))
2795        }
2796        StmtKind::Switch(s) => arena_ast::StmtKind::Switch(arena.alloc(arena_ast::SwitchStmt {
2797            expr: av_expr(arena, &s.expr),
2798            body: {
2799                let mut cases = arena_ast::ArenaVec::with_capacity_in(s.body.cases.len(), arena);
2800                for c in s.body.cases.iter() {
2801                    cases.push(arena_ast::SwitchCase {
2802                        value: c.value.as_ref().map(|vl| av_expr(arena, vl)),
2803                        body: av_stmts(arena, &c.body),
2804                        span: c.span,
2805                    });
2806                }
2807                arena_ast::SwitchBody {
2808                    cases,
2809                    span: s.body.span,
2810                }
2811            },
2812            uses_alternative: s.uses_alternative,
2813        })),
2814        StmtKind::Goto(ident) => arena_ast::StmtKind::Goto(av_ident(arena, ident)),
2815        StmtKind::Label(s) => arena_ast::StmtKind::Label(av_str(arena, s)),
2816        StmtKind::Declare(d) => arena_ast::StmtKind::Declare(arena.alloc(arena_ast::DeclareStmt {
2817            directives: {
2818                let mut v = arena_ast::ArenaVec::with_capacity_in(d.directives.len(), arena);
2819                for (k, val) in d.directives.iter() {
2820                    v.push((arena.alloc_str(k) as &str, av_expr(arena, val)));
2821                }
2822                v
2823            },
2824            body: d.body.as_ref().map(|b| &*arena.alloc(av_stmt(arena, b))),
2825            uses_alternative: d.uses_alternative,
2826        })),
2827        StmtKind::Unset(exprs) => arena_ast::StmtKind::Unset(av_exprs(arena, exprs)),
2828        StmtKind::Throw(e) => arena_ast::StmtKind::Throw(arena.alloc(av_expr(arena, e))),
2829        StmtKind::TryCatch(t) => arena_ast::StmtKind::TryCatch(
2830            arena.alloc(arena_ast::TryCatchStmt {
2831                body: arena.alloc(av_block(arena, &t.body)),
2832                catches: {
2833                    let mut v = arena_ast::ArenaVec::with_capacity_in(t.catches.len(), arena);
2834                    for c in t.catches.iter() {
2835                        let mut types = arena_ast::ArenaVec::with_capacity_in(c.types.len(), arena);
2836                        for n in c.types.iter() {
2837                            types.push(av_name(arena, n));
2838                        }
2839                        v.push(arena_ast::CatchClause {
2840                            types,
2841                            var: c.var.as_deref().map(|s| av_str(arena, s)),
2842                            body: arena.alloc(av_block(arena, &c.body)),
2843                            span: c.span,
2844                        });
2845                    }
2846                    v
2847                },
2848                finally: t
2849                    .finally
2850                    .as_deref()
2851                    .map(|f| &*arena.alloc(av_block(arena, f))),
2852                finally_kw_start: t.finally_kw_start,
2853            }),
2854        ),
2855        StmtKind::Global(exprs) => arena_ast::StmtKind::Global(av_exprs(arena, exprs)),
2856        StmtKind::Class(cls) => arena_ast::StmtKind::Class(arena.alloc(av_class_decl(arena, cls))),
2857        StmtKind::Interface(iface) => {
2858            arena_ast::StmtKind::Interface(arena.alloc(av_interface_decl(arena, iface)))
2859        }
2860        StmtKind::Trait(tr) => arena_ast::StmtKind::Trait(arena.alloc(av_trait_decl(arena, tr))),
2861        StmtKind::Enum(en) => arena_ast::StmtKind::Enum(arena.alloc(av_enum_decl(arena, en))),
2862        StmtKind::Namespace(ns) => {
2863            arena_ast::StmtKind::Namespace(arena.alloc(arena_ast::NamespaceDecl {
2864                name: ns.name.as_ref().map(|n| av_name(arena, n)),
2865                body: match &ns.body {
2866                    NamespaceBody::Braced(block) => {
2867                        arena_ast::NamespaceBody::Braced(&*arena.alloc(av_block(arena, block)))
2868                    }
2869                    NamespaceBody::Simple => arena_ast::NamespaceBody::Simple,
2870                },
2871            }))
2872        }
2873        StmtKind::Use(u) => arena_ast::StmtKind::Use(arena.alloc(arena_ast::UseDecl {
2874            kind: u.kind,
2875            uses: {
2876                let mut v = arena_ast::ArenaVec::with_capacity_in(u.uses.len(), arena);
2877                for item in u.uses.iter() {
2878                    v.push(arena_ast::UseItem {
2879                        name: av_name(arena, &item.name),
2880                        alias: item.alias.as_deref().map(|s| av_str(arena, s)),
2881                        kind: item.kind,
2882                        span: item.span,
2883                    });
2884                }
2885                v
2886            },
2887        })),
2888        StmtKind::Const(items) => {
2889            let mut v = arena_ast::ArenaVec::with_capacity_in(items.len(), arena);
2890            for item in items.iter() {
2891                v.push(arena_ast::ConstItem {
2892                    name: av_ident(arena, &item.name),
2893                    value: av_expr(arena, &item.value),
2894                    attributes: av_attrs(arena, &item.attributes),
2895                    span: item.span,
2896                    doc_comment: av_opt_comment(arena, &item.doc_comment),
2897                });
2898            }
2899            arena_ast::StmtKind::Const(v)
2900        }
2901        StmtKind::StaticVar(vars) => {
2902            let mut v = arena_ast::ArenaVec::with_capacity_in(vars.len(), arena);
2903            for var in vars.iter() {
2904                v.push(arena_ast::StaticVar {
2905                    name: av_ident(arena, &var.name),
2906                    default: var.default.as_ref().map(|e| av_expr(arena, e)),
2907                    span: var.span,
2908                });
2909            }
2910            arena_ast::StmtKind::StaticVar(v)
2911        }
2912        StmtKind::HaltCompiler(s) => arena_ast::StmtKind::HaltCompiler(av_str(arena, s)),
2913        StmtKind::Nop => arena_ast::StmtKind::Nop,
2914        StmtKind::InlineHtml(s) => arena_ast::StmtKind::InlineHtml(av_str(arena, s)),
2915        StmtKind::Error => arena_ast::StmtKind::Error,
2916    }
2917}
2918
2919fn av_function_decl<'a>(
2920    arena: &'a bumpalo::Bump,
2921    f: &FunctionDecl,
2922) -> arena_ast::FunctionDecl<'a, 'a> {
2923    arena_ast::FunctionDecl {
2924        name: av_ident(arena, &f.name),
2925        params: av_params(arena, &f.params),
2926        body: &*arena.alloc(av_block(arena, &f.body)),
2927        return_type: f.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2928        by_ref: f.by_ref,
2929        attributes: av_attrs(arena, &f.attributes),
2930        doc_comment: av_opt_comment(arena, &f.doc_comment),
2931    }
2932}
2933
2934fn av_class_member<'a>(
2935    arena: &'a bumpalo::Bump,
2936    m: &ClassMember,
2937) -> arena_ast::ClassMember<'a, 'a> {
2938    arena_ast::ClassMember {
2939        kind: match &m.kind {
2940            ClassMemberKind::Property(p) => {
2941                arena_ast::ClassMemberKind::Property(arena_ast::PropertyDecl {
2942                    name: av_ident(arena, &p.name),
2943                    visibility: p.visibility,
2944                    set_visibility: p.set_visibility,
2945                    is_static: p.is_static,
2946                    is_readonly: p.is_readonly,
2947                    type_hint: p.type_hint.as_ref().map(|t| av_type_hint(arena, t)),
2948                    default: p.default.as_ref().map(|e| av_expr(arena, e)),
2949                    attributes: av_attrs(arena, &p.attributes),
2950                    hooks: av_hooks(arena, &p.hooks),
2951                    doc_comment: av_opt_comment(arena, &p.doc_comment),
2952                })
2953            }
2954            ClassMemberKind::Method(m) => {
2955                arena_ast::ClassMemberKind::Method(arena_ast::MethodDecl {
2956                    name: av_ident(arena, &m.name),
2957                    visibility: m.visibility,
2958                    is_static: m.is_static,
2959                    is_abstract: m.is_abstract,
2960                    is_final: m.is_final,
2961                    by_ref: m.by_ref,
2962                    params: av_params(arena, &m.params),
2963                    return_type: m.return_type.as_ref().map(|t| av_type_hint(arena, t)),
2964                    body: m.body.as_deref().map(|b| &*arena.alloc(av_block(arena, b))),
2965                    attributes: av_attrs(arena, &m.attributes),
2966                    doc_comment: av_opt_comment(arena, &m.doc_comment),
2967                })
2968            }
2969            ClassMemberKind::ClassConst(c) => {
2970                arena_ast::ClassMemberKind::ClassConst(av_class_const(arena, c))
2971            }
2972            ClassMemberKind::TraitUse(t) => {
2973                arena_ast::ClassMemberKind::TraitUse(av_trait_use(arena, t))
2974            }
2975        },
2976        span: m.span,
2977    }
2978}
2979
2980fn av_class_members<'a>(
2981    arena: &'a bumpalo::Bump,
2982    members: &[ClassMember],
2983) -> arena_ast::ArenaVec<'a, arena_ast::ClassMember<'a, 'a>> {
2984    let mut v = arena_ast::ArenaVec::with_capacity_in(members.len(), arena);
2985    for m in members.iter() {
2986        v.push(av_class_member(arena, m));
2987    }
2988    v
2989}
2990
2991fn av_class_const<'a>(
2992    arena: &'a bumpalo::Bump,
2993    c: &ClassConstDecl,
2994) -> arena_ast::ClassConstDecl<'a, 'a> {
2995    arena_ast::ClassConstDecl {
2996        name: av_ident(arena, &c.name),
2997        visibility: c.visibility,
2998        is_final: c.is_final,
2999        type_hint: c
3000            .type_hint
3001            .as_ref()
3002            .map(|th| arena.alloc(av_type_hint(arena, th)) as &_),
3003        value: av_expr(arena, &c.value),
3004        attributes: av_attrs(arena, &c.attributes),
3005        doc_comment: av_opt_comment(arena, &c.doc_comment),
3006    }
3007}
3008
3009fn av_trait_use<'a>(arena: &'a bumpalo::Bump, t: &TraitUseDecl) -> arena_ast::TraitUseDecl<'a, 'a> {
3010    let mut traits = arena_ast::ArenaVec::with_capacity_in(t.traits.len(), arena);
3011    for n in t.traits.iter() {
3012        traits.push(av_name(arena, n));
3013    }
3014    let mut adaptations = arena_ast::ArenaVec::with_capacity_in(t.adaptations.len(), arena);
3015    for a in t.adaptations.iter() {
3016        adaptations.push(arena_ast::TraitAdaptation {
3017            kind: match &a.kind {
3018                TraitAdaptationKind::Precedence {
3019                    trait_name,
3020                    method,
3021                    insteadof,
3022                } => {
3023                    let mut instead = arena_ast::ArenaVec::with_capacity_in(insteadof.len(), arena);
3024                    for n in insteadof.iter() {
3025                        instead.push(av_name(arena, n));
3026                    }
3027                    arena_ast::TraitAdaptationKind::Precedence {
3028                        trait_name: av_name(arena, trait_name),
3029                        method: av_name(arena, method),
3030                        insteadof: instead,
3031                    }
3032                }
3033                TraitAdaptationKind::Alias {
3034                    trait_name,
3035                    method,
3036                    new_modifier,
3037                    new_name,
3038                } => arena_ast::TraitAdaptationKind::Alias {
3039                    trait_name: trait_name.as_ref().map(|n| av_name(arena, n)),
3040                    method: av_name(arena, method),
3041                    new_modifier: *new_modifier,
3042                    new_name: new_name.as_ref().map(|n| av_name(arena, n)),
3043                },
3044            },
3045            span: a.span,
3046        });
3047    }
3048    arena_ast::TraitUseDecl {
3049        traits,
3050        adaptations,
3051        adaptations_brace_start: t.adaptations_brace_start,
3052        doc_comment: av_opt_comment(arena, &t.doc_comment),
3053    }
3054}
3055
3056fn av_class_decl<'a>(arena: &'a bumpalo::Bump, cls: &ClassDecl) -> arena_ast::ClassDecl<'a, 'a> {
3057    let mut implements = arena_ast::ArenaVec::with_capacity_in(cls.implements.len(), arena);
3058    for n in cls.implements.iter() {
3059        implements.push(av_name(arena, n));
3060    }
3061    arena_ast::ClassDecl {
3062        name: cls.name.as_ref().map(|ident| av_ident(arena, ident)),
3063        modifiers: cls.modifiers.clone(),
3064        extends: cls.extends.as_ref().map(|n| av_name(arena, n)),
3065        implements,
3066        body: arena_ast::ClassBody {
3067            members: av_class_members(arena, &cls.body.members),
3068            span: cls.body.span,
3069        },
3070        attributes: av_attrs(arena, &cls.attributes),
3071        doc_comment: av_opt_comment(arena, &cls.doc_comment),
3072    }
3073}
3074
3075fn av_interface_decl<'a>(
3076    arena: &'a bumpalo::Bump,
3077    iface: &InterfaceDecl,
3078) -> arena_ast::InterfaceDecl<'a, 'a> {
3079    let mut extends = arena_ast::ArenaVec::with_capacity_in(iface.extends.len(), arena);
3080    for n in iface.extends.iter() {
3081        extends.push(av_name(arena, n));
3082    }
3083    arena_ast::InterfaceDecl {
3084        name: av_ident(arena, &iface.name),
3085        extends,
3086        body: arena_ast::ClassBody {
3087            members: av_class_members(arena, &iface.body.members),
3088            span: iface.body.span,
3089        },
3090        attributes: av_attrs(arena, &iface.attributes),
3091        doc_comment: av_opt_comment(arena, &iface.doc_comment),
3092    }
3093}
3094
3095fn av_trait_decl<'a>(arena: &'a bumpalo::Bump, tr: &TraitDecl) -> arena_ast::TraitDecl<'a, 'a> {
3096    arena_ast::TraitDecl {
3097        name: av_ident(arena, &tr.name),
3098        body: arena_ast::ClassBody {
3099            members: av_class_members(arena, &tr.body.members),
3100            span: tr.body.span,
3101        },
3102        attributes: av_attrs(arena, &tr.attributes),
3103        doc_comment: av_opt_comment(arena, &tr.doc_comment),
3104    }
3105}
3106
3107fn av_enum_decl<'a>(arena: &'a bumpalo::Bump, en: &EnumDecl) -> arena_ast::EnumDecl<'a, 'a> {
3108    let mut implements = arena_ast::ArenaVec::with_capacity_in(en.implements.len(), arena);
3109    for n in en.implements.iter() {
3110        implements.push(av_name(arena, n));
3111    }
3112    let mut members = arena_ast::ArenaVec::with_capacity_in(en.body.members.len(), arena);
3113    for m in en.body.members.iter() {
3114        members.push(arena_ast::EnumMember {
3115            kind: match &m.kind {
3116                EnumMemberKind::Case(c) => arena_ast::EnumMemberKind::Case(arena_ast::EnumCase {
3117                    name: av_ident(arena, &c.name),
3118                    value: c.value.as_ref().map(|e| av_expr(arena, e)),
3119                    attributes: av_attrs(arena, &c.attributes),
3120                    doc_comment: av_opt_comment(arena, &c.doc_comment),
3121                }),
3122                EnumMemberKind::Method(m) => {
3123                    arena_ast::EnumMemberKind::Method(arena_ast::MethodDecl {
3124                        name: av_ident(arena, &m.name),
3125                        visibility: m.visibility,
3126                        is_static: m.is_static,
3127                        is_abstract: m.is_abstract,
3128                        is_final: m.is_final,
3129                        by_ref: m.by_ref,
3130                        params: av_params(arena, &m.params),
3131                        return_type: m.return_type.as_ref().map(|t| av_type_hint(arena, t)),
3132                        body: m.body.as_deref().map(|b| &*arena.alloc(av_block(arena, b))),
3133                        attributes: av_attrs(arena, &m.attributes),
3134                        doc_comment: av_opt_comment(arena, &m.doc_comment),
3135                    })
3136                }
3137                EnumMemberKind::ClassConst(c) => {
3138                    arena_ast::EnumMemberKind::ClassConst(av_class_const(arena, c))
3139                }
3140                EnumMemberKind::TraitUse(t) => {
3141                    arena_ast::EnumMemberKind::TraitUse(av_trait_use(arena, t))
3142                }
3143            },
3144            span: m.span,
3145        });
3146    }
3147    arena_ast::EnumDecl {
3148        name: av_ident(arena, &en.name),
3149        scalar_type: en.scalar_type.as_ref().map(|n| av_name(arena, n)),
3150        implements,
3151        body: arena_ast::EnumBody {
3152            members,
3153            span: en.body.span,
3154        },
3155        attributes: av_attrs(arena, &en.attributes),
3156        doc_comment: av_opt_comment(arena, &en.doc_comment),
3157    }
3158}