Skip to main content

php_ast/ast/
exprs.rs

1use serde::Serialize;
2
3use crate::Span;
4
5use super::{is_false, ArenaVec, Arg, Attribute, Block, ClassDecl, Param, TypeHint};
6
7#[derive(Clone, Copy, PartialEq, Eq, Hash)]
8enum NameStrInner<'arena, 'src> {
9    Src(&'src str),
10    Arena(&'arena str),
11}
12
13/// A name string from either the source buffer or the bump arena. Use [`as_str`](NameStr::as_str)
14/// or `Deref` to get the string value — the allocation origin is an internal parser detail.
15///
16/// Build one with [`NameStr::new_in`], or move one into another arena with
17/// [`fold_name_str`](crate::fold::fold_name_str). The hidden `__*` constructors
18/// and accessors exist for the parser and are not part of the stable API.
19///
20/// Using this as the payload for both `ExprKind::Variable` and `ExprKind::Identifier`
21/// gives them the same binding type, so or-patterns compile natively:
22///
23/// ```
24/// # use php_ast::ast::{ExprKind, NameStr};
25/// # fn example<'a, 'b>(kind: &ExprKind<'a, 'b>) {
26/// if let ExprKind::Variable(name) | ExprKind::Identifier(name) = kind {
27///     let _s: &str = name.as_str();
28/// }
29/// # }
30/// ```
31#[derive(Clone, Copy, PartialEq, Eq, Hash)]
32pub struct NameStr<'arena, 'src>(NameStrInner<'arena, 'src>);
33
34impl<'arena, 'src> NameStr<'arena, 'src> {
35    /// Copies `s` into `arena`.
36    pub fn new_in(arena: &'arena bumpalo::Bump, s: &str) -> Self {
37        Self(NameStrInner::Arena(arena.alloc_str(s)))
38    }
39
40    /// Borrowed directly from the source buffer.
41    #[doc(hidden)]
42    #[inline]
43    pub fn __src(s: &'src str) -> Self {
44        Self(NameStrInner::Src(s))
45    }
46
47    /// Allocated in the bump arena (e.g. a joined qualified name or a keyword).
48    #[doc(hidden)]
49    #[inline]
50    pub fn __arena(s: &'arena str) -> Self {
51        Self(NameStrInner::Arena(s))
52    }
53
54    /// Returns the arena slice if this value was arena-allocated, otherwise `None`.
55    /// Used internally to avoid re-allocating strings already in the arena.
56    #[doc(hidden)]
57    #[inline]
58    pub fn __into_arena_str(self) -> Option<&'arena str> {
59        match self.0 {
60            NameStrInner::Arena(s) => Some(s),
61            NameStrInner::Src(_) => None,
62        }
63    }
64
65    /// Returns the source slice if this value was borrowed from the source buffer, otherwise `None`.
66    /// Used by the fold infrastructure to preserve source-borrowed strings without re-allocation.
67    #[doc(hidden)]
68    #[inline]
69    pub fn __into_src_str(self) -> Option<&'src str> {
70        match self.0 {
71            NameStrInner::Src(s) => Some(s),
72            NameStrInner::Arena(_) => None,
73        }
74    }
75
76    /// The string value, regardless of where it is stored.
77    #[inline]
78    pub fn as_str(&self) -> &str {
79        match self.0 {
80            NameStrInner::Src(s) | NameStrInner::Arena(s) => s,
81        }
82    }
83}
84
85impl<'arena, 'src> std::ops::Deref for NameStr<'arena, 'src> {
86    type Target = str;
87    #[inline]
88    fn deref(&self) -> &str {
89        self.as_str()
90    }
91}
92
93impl<'arena, 'src> std::fmt::Debug for NameStr<'arena, 'src> {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        self.as_str().fmt(f)
96    }
97}
98
99impl<'arena, 'src> serde::Serialize for NameStr<'arena, 'src> {
100    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
101        self.as_str().serialize(serializer)
102    }
103}
104
105/// An expression node.
106#[derive(Debug, Serialize)]
107pub struct Expr<'arena, 'src> {
108    /// Expression kind.
109    pub kind: ExprKind<'arena, 'src>,
110    /// Source range of this node.
111    pub span: Span,
112}
113
114/// The kinds of expression.
115#[derive(Debug, Serialize)]
116#[non_exhaustive]
117pub enum ExprKind<'arena, 'src> {
118    /// Integer literal
119    Int(i64),
120
121    /// Float literal
122    Float(f64),
123
124    /// String literal, with escape sequences decoded. Borrowed from the source
125    /// when the literal has none.
126    String(NameStr<'arena, 'src>),
127
128    /// Interpolated string: `"Hello $name, you are {$age} years old"`
129    InterpolatedString(ArenaVec<'arena, StringPart<'arena, 'src>>),
130
131    /// Heredoc: `<<<EOT ... EOT`
132    Heredoc {
133        /// Heredoc/nowdoc label.
134        label: &'src str,
135        /// Literal and interpolated segments.
136        parts: ArenaVec<'arena, StringPart<'arena, 'src>>,
137    },
138
139    /// Nowdoc: `<<<'EOT' ... EOT`
140    Nowdoc {
141        /// Heredoc/nowdoc label.
142        label: &'src str,
143        /// Nowdoc contents.
144        value: &'arena str,
145    },
146
147    /// Shell execution: `` `command $var` ``
148    ShellExec(ArenaVec<'arena, StringPart<'arena, 'src>>),
149
150    /// Boolean literal
151    Bool(bool),
152
153    /// Null literal
154    Null,
155
156    /// Variable: `$name`
157    Variable(NameStr<'arena, 'src>),
158
159    /// Variable variable: `$$var`, `$$$var`, `${expr}`
160    VariableVariable(&'arena Expr<'arena, 'src>),
161
162    /// Identifier (bare name, e.g. function name in a call)
163    Identifier(NameStr<'arena, 'src>),
164
165    /// Assignment: `$x = expr` or `$x += expr`
166    Assign(AssignExpr<'arena, 'src>),
167
168    /// Binary operation: `expr op expr`
169    Binary(BinaryExpr<'arena, 'src>),
170
171    /// Unary prefix: `-expr`, `!expr`, `~expr`, `++$x`, `--$x`
172    UnaryPrefix(UnaryPrefixExpr<'arena, 'src>),
173
174    /// Unary postfix: `$x++`, `$x--`
175    UnaryPostfix(UnaryPostfixExpr<'arena, 'src>),
176
177    /// Ternary: `cond ? then : else` or short `cond ?: else`
178    Ternary(TernaryExpr<'arena, 'src>),
179
180    /// Null coalescing: `expr ?? fallback`
181    NullCoalesce(NullCoalesceExpr<'arena, 'src>),
182
183    /// Function call: `name(args)`
184    FunctionCall(FunctionCallExpr<'arena, 'src>),
185
186    /// Array literal: `[1, 2, 3]` or `['a' => 1]`
187    Array(ArenaVec<'arena, ArrayElement<'arena, 'src>>),
188
189    /// Array access: `$arr[index]`
190    ArrayAccess(ArrayAccessExpr<'arena, 'src>),
191
192    /// Print expression: `print expr`
193    Print(&'arena Expr<'arena, 'src>),
194
195    /// Parenthesized expression: `(expr)`
196    Parenthesized(&'arena Expr<'arena, 'src>),
197
198    /// Cast expression: `(int)$x`, `(string)$x`, etc.
199    Cast(CastKind, &'arena Expr<'arena, 'src>),
200
201    /// Error suppression: `@expr`
202    ErrorSuppress(&'arena Expr<'arena, 'src>),
203
204    /// Isset: `isset($a, $b)`
205    Isset(ArenaVec<'arena, Expr<'arena, 'src>>),
206
207    /// Empty: `empty($a)`
208    Empty(&'arena Expr<'arena, 'src>),
209
210    /// Include/require: `include 'file.php'`
211    Include(IncludeKind, &'arena Expr<'arena, 'src>),
212
213    /// Eval: `eval('code')`
214    Eval(&'arena Expr<'arena, 'src>),
215
216    /// Exit/die: `exit`, `exit(1)`, `die('msg')`
217    Exit(Option<&'arena Expr<'arena, 'src>>),
218
219    /// Magic constant: `__LINE__`, `__FILE__`, etc.
220    MagicConst(MagicConstKind),
221
222    /// Clone: `clone $obj`
223    Clone(&'arena Expr<'arena, 'src>),
224
225    /// Clone with property overrides: `clone($obj, ['prop' => $val])` — PHP 8.5+
226    CloneWith(&'arena Expr<'arena, 'src>, &'arena Expr<'arena, 'src>),
227
228    /// New: `new Class(args)`
229    New(NewExpr<'arena, 'src>),
230
231    /// Property access: `$obj->prop`
232    PropertyAccess(PropertyAccessExpr<'arena, 'src>),
233
234    /// Nullsafe property access: `$obj?->prop`
235    NullsafePropertyAccess(PropertyAccessExpr<'arena, 'src>),
236
237    /// Method call: `$obj->method(args)`
238    MethodCall(&'arena MethodCallExpr<'arena, 'src>),
239
240    /// Nullsafe method call: `$obj?->method(args)`
241    NullsafeMethodCall(&'arena MethodCallExpr<'arena, 'src>),
242
243    /// Static property access: `Class::$prop`
244    StaticPropertyAccess(StaticAccessExpr<'arena, 'src>),
245
246    /// Static method call: `Class::method(args)`
247    StaticMethodCall(&'arena StaticMethodCallExpr<'arena, 'src>),
248
249    /// Dynamic static method call: `Class::$method(args)`
250    StaticMethodCallDynamic(&'arena StaticMethodCallDynamicExpr<'arena, 'src>),
251
252    /// Class constant access: `Class::CONST`
253    ClassConstAccess(StaticAccessExpr<'arena, 'src>),
254
255    /// Dynamic class constant access: `Foo::{expr}`
256    ClassConstAccessDynamic {
257        /// Class expression.
258        class: &'arena Expr<'arena, 'src>,
259        /// Member expression.
260        member: &'arena Expr<'arena, 'src>,
261    },
262
263    /// Dynamic static property access: `A::$$b`, `A::${'b'}`
264    StaticPropertyAccessDynamic {
265        /// Class expression.
266        class: &'arena Expr<'arena, 'src>,
267        /// Member expression.
268        member: &'arena Expr<'arena, 'src>,
269    },
270
271    /// Closure: `function($x) use($y) { }`
272    Closure(&'arena ClosureExpr<'arena, 'src>),
273
274    /// Arrow function: `fn($x) => expr`
275    ArrowFunction(&'arena ArrowFunctionExpr<'arena, 'src>),
276
277    /// Match: `match(expr) { ... }`
278    Match(MatchExpr<'arena, 'src>),
279
280    /// Throw as expression (PHP 8)
281    Throw(&'arena Expr<'arena, 'src>),
282
283    /// Yield: `yield` / `yield $val` / `yield $key => $val`
284    Yield(YieldExpr<'arena, 'src>),
285
286    /// Anonymous class: `new class(args) extends Foo implements Bar { ... }`
287    AnonymousClass(&'arena ClassDecl<'arena, 'src>),
288
289    /// First-class callable: `strlen(...)`, `$obj->method(...)`, `Foo::bar(...)`
290    CallableCreate(CallableCreateExpr<'arena, 'src>),
291
292    /// Omitted element in destructuring: `[$a, , $c]` or `list($a, , $c)`
293    Omit,
294
295    /// Error placeholder
296    Error,
297}
298
299impl<'arena, 'src> Expr<'arena, 'src> {
300    /// Returns the name string for `Variable` and `Identifier` nodes, `None` for everything else.
301    pub fn name_str(&self) -> Option<&str> {
302        match &self.kind {
303            ExprKind::Variable(s) | ExprKind::Identifier(s) => Some(s.as_str()),
304            _ => None,
305        }
306    }
307}
308
309/// The type of a cast expression.
310#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
311#[non_exhaustive]
312pub enum CastKind {
313    /// `(int)` or `(integer)` cast.
314    Int,
315    /// `(float)`, `(double)`, or `(real)` cast.
316    Float,
317    /// `(string)` cast.
318    String,
319    /// `(bool)` or `(boolean)` cast.
320    Bool,
321    /// `(array)` cast.
322    Array,
323    /// `(object)` cast.
324    Object,
325    /// `(unset)` cast — deprecated; casts to `null`.
326    Unset,
327    /// `(void)` cast — non-standard; treated as discarding the value.
328    Void,
329}
330
331/// The include/require keyword used.
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
333#[non_exhaustive]
334pub enum IncludeKind {
335    /// `include 'file.php'` — emits a warning if the file is not found.
336    Include,
337    /// `include_once 'file.php'` — like `include`, but skipped if the file has already been included.
338    IncludeOnce,
339    /// `require 'file.php'` — fatal error if the file is not found.
340    Require,
341    /// `require_once 'file.php'` — like `require`, but skipped if the file has already been included.
342    RequireOnce,
343}
344
345/// The magic constants.
346#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
347#[non_exhaustive]
348pub enum MagicConstKind {
349    /// `__CLASS__` — name of the current class, or empty string outside a class.
350    Class,
351    /// `__DIR__` — directory of the current file.
352    Dir,
353    /// `__FILE__` — absolute path of the current file.
354    File,
355    /// `__FUNCTION__` — name of the current function or closure.
356    Function,
357    /// `__LINE__` — current line number in the source file.
358    Line,
359    /// `__METHOD__` — name of the current method including its class: `ClassName::methodName`.
360    Method,
361    /// `__NAMESPACE__` — name of the current namespace, or empty string in the global namespace.
362    Namespace,
363    /// `__TRAIT__` — name of the current trait, or empty string outside a trait.
364    Trait,
365    /// `__PROPERTY__` — name of the current property inside a property hook (PHP 8.4+).
366    Property,
367}
368
369// --- Expression sub-types ---
370
371/// An assignment expression.
372#[derive(Debug, Serialize)]
373pub struct AssignExpr<'arena, 'src> {
374    /// Assignment target.
375    pub target: &'arena Expr<'arena, 'src>,
376    /// Assignment operator.
377    pub op: AssignOp,
378    /// Assigned value.
379    pub value: &'arena Expr<'arena, 'src>,
380    /// `true` for reference assignment `=&`.
381    #[serde(skip_serializing_if = "is_false")]
382    pub by_ref: bool,
383}
384
385/// Assignment operators.
386#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
387#[non_exhaustive]
388pub enum AssignOp {
389    /// `=`
390    Assign,
391    /// `+=`
392    Plus,
393    /// `-=`
394    Minus,
395    /// `*=`
396    Mul,
397    /// `/=`
398    Div,
399    /// `%=`
400    Mod,
401    /// `**=`
402    Pow,
403    /// `.=`
404    Concat,
405    /// `&=`
406    BitwiseAnd,
407    /// `|=`
408    BitwiseOr,
409    /// `^=`
410    BitwiseXor,
411    /// `<<=`
412    ShiftLeft,
413    /// `>>=`
414    ShiftRight,
415    /// `??=`
416    Coalesce,
417}
418
419/// A binary operation.
420#[derive(Debug, Serialize)]
421pub struct BinaryExpr<'arena, 'src> {
422    /// Left operand.
423    pub left: &'arena Expr<'arena, 'src>,
424    /// Binary operator.
425    pub op: BinaryOp,
426    /// Right operand.
427    pub right: &'arena Expr<'arena, 'src>,
428}
429
430/// Binary operators.
431#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
432#[non_exhaustive]
433pub enum BinaryOp {
434    /// `+`
435    Add,
436    /// `-`
437    Sub,
438    /// `*`
439    Mul,
440    /// `/`
441    Div,
442    /// `%`
443    Mod,
444    /// `**`
445    Pow,
446    /// `.` — string concatenation.
447    Concat,
448    /// `==` — loose equality (type-coercing).
449    Equal,
450    /// `!=` or `<>` — loose inequality.
451    NotEqual,
452    /// `===` — strict equality (type and value).
453    Identical,
454    /// `!==` — strict inequality.
455    NotIdentical,
456    /// `<`
457    Less,
458    /// `>`
459    Greater,
460    /// `<=`
461    LessOrEqual,
462    /// `>=`
463    GreaterOrEqual,
464    /// `<=>` — spaceship / three-way comparison; returns -1, 0, or 1.
465    Spaceship,
466    /// `&&` — short-circuit boolean AND (higher precedence than `and`).
467    BooleanAnd,
468    /// `||` — short-circuit boolean OR (higher precedence than `or`).
469    BooleanOr,
470    /// `&` — bitwise AND.
471    BitwiseAnd,
472    /// `|` — bitwise OR.
473    BitwiseOr,
474    /// `^` — bitwise XOR.
475    BitwiseXor,
476    /// `<<` — left bit-shift.
477    ShiftLeft,
478    /// `>>` — right bit-shift.
479    ShiftRight,
480    /// `and` — boolean AND (lower precedence than `&&`).
481    LogicalAnd,
482    /// `or` — boolean OR (lower precedence than `||`).
483    LogicalOr,
484    /// `xor` — boolean XOR.
485    LogicalXor,
486    /// `instanceof` — type-check operator; `$x instanceof Foo`.
487    Instanceof,
488    /// `|>` — pipe operator (PHP 8.5+); passes the left operand as the first argument of the right callable.
489    Pipe,
490}
491
492/// A prefix unary operation.
493#[derive(Debug, Serialize)]
494pub struct UnaryPrefixExpr<'arena, 'src> {
495    /// Prefix operator.
496    pub op: UnaryPrefixOp,
497    /// Operand expression.
498    pub operand: &'arena Expr<'arena, 'src>,
499}
500
501/// Prefix unary operators.
502#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
503#[non_exhaustive]
504pub enum UnaryPrefixOp {
505    /// `-expr` — arithmetic negation.
506    Negate,
507    /// `+expr` — unary plus (no-op for numbers, promotes to numeric).
508    Plus,
509    /// `!expr` — boolean NOT.
510    BooleanNot,
511    /// `~expr` — bitwise NOT.
512    BitwiseNot,
513    /// `++$x` — pre-increment; increments then returns the new value.
514    PreIncrement,
515    /// `--$x` — pre-decrement; decrements then returns the new value.
516    PreDecrement,
517}
518
519/// A postfix unary operation.
520#[derive(Debug, Serialize)]
521pub struct UnaryPostfixExpr<'arena, 'src> {
522    /// Operand expression.
523    pub operand: &'arena Expr<'arena, 'src>,
524    /// Postfix operator.
525    pub op: UnaryPostfixOp,
526}
527
528/// Postfix unary operators.
529#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
530#[non_exhaustive]
531pub enum UnaryPostfixOp {
532    /// `$x++` — post-increment; returns the current value then increments.
533    PostIncrement,
534    /// `$x--` — post-decrement; returns the current value then decrements.
535    PostDecrement,
536}
537
538/// A ternary `?:` expression.
539#[derive(Debug, Serialize)]
540pub struct TernaryExpr<'arena, 'src> {
541    /// Condition tested.
542    pub condition: &'arena Expr<'arena, 'src>,
543    /// None for short ternary `$x ?: $y`
544    pub then_expr: Option<&'arena Expr<'arena, 'src>>,
545    /// Value when the condition is falsy.
546    pub else_expr: &'arena Expr<'arena, 'src>,
547}
548
549/// A `??` expression.
550#[derive(Debug, Serialize)]
551pub struct NullCoalesceExpr<'arena, 'src> {
552    /// Value tested for null.
553    pub left: &'arena Expr<'arena, 'src>,
554    /// Fallback when the left side is null or unset.
555    pub right: &'arena Expr<'arena, 'src>,
556}
557
558/// A function call: `name(args)`.
559#[derive(Debug, Serialize)]
560pub struct FunctionCallExpr<'arena, 'src> {
561    /// Callee expression.
562    pub name: &'arena Expr<'arena, 'src>,
563    /// Call arguments.
564    pub args: ArenaVec<'arena, Arg<'arena, 'src>>,
565}
566
567/// An element of an array literal.
568#[derive(Debug, Serialize)]
569pub struct ArrayElement<'arena, 'src> {
570    /// Key expression, if any.
571    pub key: Option<Expr<'arena, 'src>>,
572    /// Element value.
573    pub value: Expr<'arena, 'src>,
574    /// `true` for a spread element (`...$x`).
575    pub unpack: bool,
576    /// `true` for a reference element (`&$x`).
577    #[serde(skip_serializing_if = "is_false")]
578    pub by_ref: bool,
579    /// Source range of this node.
580    pub span: Span,
581}
582
583/// An array index access: `$a[i]`.
584#[derive(Debug, Serialize)]
585pub struct ArrayAccessExpr<'arena, 'src> {
586    /// Array being indexed.
587    pub array: &'arena Expr<'arena, 'src>,
588    /// Index; `None` for `$a[]`.
589    pub index: Option<&'arena Expr<'arena, 'src>>,
590}
591
592// --- OOP Expression sub-types ---
593
594/// An instantiation: `new Foo(args)`.
595#[derive(Debug, Serialize)]
596pub struct NewExpr<'arena, 'src> {
597    /// Class expression being instantiated.
598    pub class: &'arena Expr<'arena, 'src>,
599    /// Call arguments.
600    pub args: ArenaVec<'arena, Arg<'arena, 'src>>,
601}
602
603/// A property access: `$obj->prop`.
604#[derive(Debug, Serialize)]
605pub struct PropertyAccessExpr<'arena, 'src> {
606    /// Object expression.
607    pub object: &'arena Expr<'arena, 'src>,
608    /// Property name expression.
609    pub property: &'arena Expr<'arena, 'src>,
610}
611
612/// A method call: `$obj->method(args)`.
613#[derive(Debug, Serialize)]
614pub struct MethodCallExpr<'arena, 'src> {
615    /// Object expression.
616    pub object: &'arena Expr<'arena, 'src>,
617    /// Method name expression.
618    pub method: &'arena Expr<'arena, 'src>,
619    /// Call arguments.
620    pub args: ArenaVec<'arena, Arg<'arena, 'src>>,
621}
622
623/// A static member access: `Class::$prop` or `Class::CONST`.
624#[derive(Debug, Serialize)]
625pub struct StaticAccessExpr<'arena, 'src> {
626    /// Class expression or name.
627    pub class: &'arena Expr<'arena, 'src>,
628    /// Constant or property name expression.
629    pub member: &'arena Expr<'arena, 'src>,
630}
631
632/// A static method call: `Class::method(args)`.
633#[derive(Debug, Serialize)]
634pub struct StaticMethodCallExpr<'arena, 'src> {
635    /// Class expression or name.
636    pub class: &'arena Expr<'arena, 'src>,
637    /// Method name expression.
638    pub method: &'arena Expr<'arena, 'src>,
639    /// Call arguments.
640    pub args: ArenaVec<'arena, Arg<'arena, 'src>>,
641}
642
643/// A static call with a dynamic method name: `Class::$method(args)`.
644#[derive(Debug, Serialize)]
645pub struct StaticMethodCallDynamicExpr<'arena, 'src> {
646    /// Class expression or name.
647    pub class: &'arena Expr<'arena, 'src>,
648    /// Expression yielding the method name.
649    pub method: &'arena Expr<'arena, 'src>,
650    /// Call arguments.
651    pub args: ArenaVec<'arena, Arg<'arena, 'src>>,
652}
653
654/// A closure: `function() use ($x) { ... }`.
655#[derive(Debug, Serialize)]
656pub struct ClosureExpr<'arena, 'src> {
657    /// `true` for `static`.
658    pub is_static: bool,
659    /// `true` when by reference (`&`).
660    pub by_ref: bool,
661    /// Declared parameters.
662    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
663    /// Variables captured by `use (...)`.
664    pub use_vars: ArenaVec<'arena, ClosureUseVar<'src>>,
665    /// Declared return type, if any.
666    pub return_type: Option<TypeHint<'arena, 'src>>,
667    /// Closure body.
668    pub body: &'arena Block<'arena, 'src>,
669    /// `#[...]` attributes applied to this node.
670    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
671}
672
673/// A variable captured in a closure `use` list.
674#[derive(Debug, Clone, Serialize)]
675pub struct ClosureUseVar<'src> {
676    /// Captured variable name without `$`.
677    pub name: &'src str,
678    /// `true` for `use (&$x)`.
679    pub by_ref: bool,
680    /// Source range of this node.
681    pub span: Span,
682}
683
684/// An arrow function: `fn($x) => expr`.
685#[derive(Debug, Serialize)]
686pub struct ArrowFunctionExpr<'arena, 'src> {
687    /// `true` for `static`.
688    pub is_static: bool,
689    /// `true` when by reference (`&`).
690    pub by_ref: bool,
691    /// Declared parameters.
692    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
693    /// Declared return type, if any.
694    pub return_type: Option<TypeHint<'arena, 'src>>,
695    /// Single result expression.
696    pub body: &'arena Expr<'arena, 'src>,
697    /// `#[...]` attributes applied to this node.
698    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
699}
700
701/// A `match` expression.
702#[derive(Debug, Serialize)]
703pub struct MatchExpr<'arena, 'src> {
704    /// Expression being matched.
705    pub subject: &'arena Expr<'arena, 'src>,
706    /// Match arms in order.
707    pub arms: ArenaVec<'arena, MatchArm<'arena, 'src>>,
708    /// The `{` in `match (...) {`. Not serialized.
709    #[serde(skip)]
710    pub brace_span: Span,
711}
712
713/// One arm of a `match`.
714#[derive(Debug, Serialize)]
715pub struct MatchArm<'arena, 'src> {
716    /// None for `default`
717    pub conditions: Option<ArenaVec<'arena, Expr<'arena, 'src>>>,
718    /// Result expression.
719    pub body: Expr<'arena, 'src>,
720    /// Source range of this node.
721    pub span: Span,
722}
723
724/// A `yield` or `yield from` expression.
725#[derive(Debug, Serialize)]
726pub struct YieldExpr<'arena, 'src> {
727    /// Yielded key.
728    pub key: Option<&'arena Expr<'arena, 'src>>,
729    /// Yielded value.
730    pub value: Option<&'arena Expr<'arena, 'src>>,
731    /// `true` for `yield from expr` (generator delegation), `false` for plain `yield`
732    pub is_from: bool,
733}
734
735// --- First-class callable ---
736
737/// A first-class callable expression.
738#[derive(Debug, Serialize)]
739pub struct CallableCreateExpr<'arena, 'src> {
740    /// Kind of callable being created.
741    pub kind: CallableCreateKind<'arena, 'src>,
742}
743
744/// The callee forms of a first-class callable.
745#[derive(Debug, Serialize)]
746#[non_exhaustive]
747pub enum CallableCreateKind<'arena, 'src> {
748    /// `foo(...)`, `$var(...)`, `\Ns\func(...)`
749    Function(&'arena Expr<'arena, 'src>),
750    /// `$obj->method(...)`
751    Method {
752        /// Object expression.
753        object: &'arena Expr<'arena, 'src>,
754        /// Method name expression.
755        method: &'arena Expr<'arena, 'src>,
756    },
757    /// `$obj?->method(...)`
758    NullsafeMethod {
759        /// Object expression.
760        object: &'arena Expr<'arena, 'src>,
761        /// Method name expression.
762        method: &'arena Expr<'arena, 'src>,
763    },
764    /// `Foo::bar(...)`
765    StaticMethod {
766        /// Class expression.
767        class: &'arena Expr<'arena, 'src>,
768        /// Method name expression.
769        method: &'arena Expr<'arena, 'src>,
770    },
771}
772
773// --- String interpolation ---
774
775/// A segment of an interpolated string.
776#[derive(Debug, Serialize)]
777#[non_exhaustive]
778pub enum StringPart<'arena, 'src> {
779    /// A plain text segment of an interpolated string or heredoc.
780    Literal(&'arena str),
781    /// An embedded expression: `$var`, `{$expr}`, or `${var}`.
782    Expr(Expr<'arena, 'src>),
783}