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