Skip to main content

php_ast/ast/
exprs.rs

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