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