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}