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