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