Skip to main content

php_ast/ast/
decls.rs

1#[cfg(feature = "serde")]
2use serde::Serialize;
3
4use crate::Span;
5
6use super::{ArenaVec, Attribute, Block, Comment, Expr, Ident, Name, TypeHint};
7
8/// A named function declaration: `function foo() {}`.
9#[derive(Debug)]
10#[cfg_attr(feature = "serde", derive(Serialize))]
11#[non_exhaustive]
12pub struct FunctionDecl<'arena, 'src> {
13    /// Function name.
14    pub name: Ident<'src>,
15    /// Declared parameters.
16    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
17    /// Function body.
18    pub body: &'arena Block<'arena, 'src>,
19    /// Declared return type, if any.
20    pub return_type: Option<TypeHint<'arena, 'src>>,
21    /// `true` when by reference (`&`).
22    pub by_ref: bool,
23    /// `#[...]` attributes applied to this node.
24    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
25    /// The immediately preceding `/** */` doc-block, if any.
26    ///
27    /// When present, this comment is **removed** from
28    /// `ParseResult::comments` — the
29    /// two collections are disjoint. All other comment forms (line, hash,
30    /// block) remain in `ParseResult::comments` regardless of position.
31    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
32    pub doc_comment: Option<Comment<'src>>,
33}
34
35impl<'arena, 'src> FunctionDecl<'arena, 'src> {
36    /// Builds a `FunctionDecl` from its identifying fields with every other field empty. Set
37    /// the rest by assignment, as with `Param::new`.
38    pub fn new(
39        arena: &'arena bumpalo::Bump,
40        name: Ident<'src>,
41        body: &'arena Block<'arena, 'src>,
42    ) -> Self {
43        Self {
44            name,
45            params: ArenaVec::new_in(arena),
46            body,
47            return_type: None,
48            by_ref: false,
49            attributes: ArenaVec::new_in(arena),
50            doc_comment: None,
51        }
52    }
53}
54
55/// A function, method, closure or hook parameter.
56#[derive(Debug)]
57#[cfg_attr(feature = "serde", derive(Serialize))]
58#[non_exhaustive]
59pub struct Param<'arena, 'src> {
60    /// Parameter name without `$`.
61    pub name: Ident<'src>,
62    /// Declared parameter type.
63    pub type_hint: Option<TypeHint<'arena, 'src>>,
64    /// Default value expression.
65    pub default: Option<Expr<'arena, 'src>>,
66    /// `true` when by reference (`&`).
67    pub by_ref: bool,
68    /// `true` for a variadic parameter (`...$x`).
69    pub variadic: bool,
70    /// Visibility, `static`, `final`, `abstract` and `readonly` modifiers.
71    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Modifiers::is_empty"))]
72    pub modifiers: Modifiers,
73    /// `#[...]` attributes applied to this node.
74    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
75    /// Property hooks on a promoted property (PHP 8.4+).
76    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "ArenaVec::is_empty"))]
77    pub hooks: ArenaVec<'arena, PropertyHook<'arena, 'src>>,
78    /// A doc-block attached directly to this parameter (PHP 8.6 recognizes
79    /// this for reflection purposes), either immediately before it or
80    /// trailing it up to the comma. Parsed at every version since a bare
81    /// comment is legal PHP regardless of target version.
82    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
83    pub doc_comment: Option<Comment<'src>>,
84    /// Source range of this node.
85    pub span: Span,
86}
87
88impl<'arena, 'src> Param<'arena, 'src> {
89    /// A parameter with `name` and `span` set and every other field empty
90    /// (`None`, `false`, no attributes or hooks). The fields are public, so set
91    /// the rest by assignment; `Param` is `#[non_exhaustive]` and cannot be
92    /// built with a struct literal outside this crate.
93    pub fn new(arena: &'arena bumpalo::Bump, name: Ident<'src>, span: Span) -> Self {
94        Self {
95            name,
96            type_hint: None,
97            default: None,
98            by_ref: false,
99            variadic: false,
100            modifiers: Modifiers::new(),
101            attributes: ArenaVec::new_in(arena),
102            hooks: ArenaVec::new_in(arena),
103            doc_comment: None,
104            span,
105        }
106    }
107}
108
109/// The modifiers of a member or promoted parameter. Which flags are valid depends on the node
110/// that carries them.
111#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
112#[cfg_attr(feature = "serde", derive(Serialize))]
113#[non_exhaustive]
114pub struct Modifiers {
115    /// Declared visibility, if any.
116    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
117    pub visibility: Option<Visibility>,
118    /// Asymmetric write visibility, e.g. `private(set)` (PHP 8.4+).
119    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
120    pub set_visibility: Option<Visibility>,
121    /// `true` for `static`.
122    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "super::is_false"))]
123    pub is_static: bool,
124    /// `true` for `abstract`.
125    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "super::is_false"))]
126    pub is_abstract: bool,
127    /// `true` for `final`.
128    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "super::is_false"))]
129    pub is_final: bool,
130    /// `true` for `readonly`.
131    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "super::is_false"))]
132    pub is_readonly: bool,
133}
134
135impl Modifiers {
136    /// No modifiers. The fields are public, so set the ones that apply by assignment;
137    /// `Modifiers` is `#[non_exhaustive]` and cannot be built with a struct literal outside
138    /// this crate.
139    pub const fn new() -> Self {
140        Self {
141            visibility: None,
142            set_visibility: None,
143            is_static: false,
144            is_abstract: false,
145            is_final: false,
146            is_readonly: false,
147        }
148    }
149
150    /// Whether no modifier is set.
151    pub fn is_empty(&self) -> bool {
152        *self == Self::new()
153    }
154}
155
156/// Member visibility modifier.
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158#[cfg_attr(feature = "serde", derive(Serialize))]
159#[non_exhaustive]
160pub enum Visibility {
161    /// `public` — accessible from anywhere.
162    Public,
163    /// `protected` — accessible within the class and its subclasses.
164    Protected,
165    /// `private` — accessible only within the declaring class.
166    Private,
167}
168
169/// The braced member list of a class-like declaration.
170#[derive(Debug)]
171#[cfg_attr(feature = "serde", derive(Serialize))]
172pub struct ClassBody<'arena, 'src> {
173    /// Class members in source order.
174    pub members: ArenaVec<'arena, ClassMember<'arena, 'src>>,
175    /// Span covering `{` to `}` of the body.
176    #[cfg_attr(feature = "serde", serde(skip))]
177    pub span: Span,
178}
179
180/// A class declaration, or the body of an anonymous class.
181#[derive(Debug)]
182#[cfg_attr(feature = "serde", derive(Serialize))]
183#[non_exhaustive]
184pub struct ClassDecl<'arena, 'src> {
185    /// Class name; `None` for anonymous classes.
186    pub name: Option<Ident<'src>>,
187    /// `abstract`, `final` and `readonly` modifiers.
188    pub modifiers: ClassModifiers,
189    /// Parent class.
190    pub extends: Option<Name<'arena, 'src>>,
191    /// Implemented interfaces.
192    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
193    /// Class body, flattened on serialization.
194    #[cfg_attr(feature = "serde", serde(flatten))]
195    pub body: ClassBody<'arena, 'src>,
196    /// `#[...]` attributes applied to this node.
197    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
198    /// Preceding `/** */` doc-block, if any.
199    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
200    pub doc_comment: Option<Comment<'src>>,
201}
202
203impl<'arena, 'src> ClassDecl<'arena, 'src> {
204    /// Builds a `ClassDecl` from its identifying fields with every other field empty. Set
205    /// the rest by assignment, as with `Param::new`.
206    pub fn new(
207        arena: &'arena bumpalo::Bump,
208        name: Option<Ident<'src>>,
209        body: ClassBody<'arena, 'src>,
210    ) -> Self {
211        Self {
212            name,
213            modifiers: ClassModifiers::default(),
214            extends: None,
215            implements: ArenaVec::new_in(arena),
216            body,
217            attributes: ArenaVec::new_in(arena),
218            doc_comment: None,
219        }
220    }
221}
222
223/// Modifiers preceding `class`.
224#[derive(Debug, Clone, Default)]
225#[cfg_attr(feature = "serde", derive(Serialize))]
226#[non_exhaustive]
227pub struct ClassModifiers {
228    /// `true` for `abstract class`.
229    pub is_abstract: bool,
230    /// `true` for `final class`.
231    pub is_final: bool,
232    /// `true` for `readonly class`.
233    pub is_readonly: bool,
234}
235
236/// A member of a class, interface or trait body.
237#[derive(Debug)]
238#[cfg_attr(feature = "serde", derive(Serialize))]
239pub struct ClassMember<'arena, 'src> {
240    /// Kind of member.
241    pub kind: ClassMemberKind<'arena, 'src>,
242    /// Source range of this node.
243    pub span: Span,
244}
245
246/// The kinds of class-like member.
247#[derive(Debug)]
248#[cfg_attr(feature = "serde", derive(Serialize))]
249#[non_exhaustive]
250pub enum ClassMemberKind<'arena, 'src> {
251    /// Property declaration.
252    Property(PropertyDecl<'arena, 'src>),
253    /// Method declaration.
254    Method(MethodDecl<'arena, 'src>),
255    /// Class constant declaration.
256    ClassConst(ClassConstDecl<'arena, 'src>),
257    /// Trait `use` statement.
258    TraitUse(TraitUseDecl<'arena, 'src>),
259}
260
261/// A property declaration: `public int $x = 0;`.
262#[derive(Debug)]
263#[cfg_attr(feature = "serde", derive(Serialize))]
264#[non_exhaustive]
265pub struct PropertyDecl<'arena, 'src> {
266    /// Property name without `$`.
267    pub name: Ident<'src>,
268    /// Visibility, `static`, `final`, `abstract` and `readonly` modifiers.
269    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Modifiers::is_empty"))]
270    pub modifiers: Modifiers,
271    /// Declared type, if any.
272    pub type_hint: Option<TypeHint<'arena, 'src>>,
273    /// Default value expression.
274    pub default: Option<Expr<'arena, 'src>>,
275    /// `#[...]` attributes applied to this node.
276    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
277    /// Property hooks (PHP 8.4+).
278    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "ArenaVec::is_empty"))]
279    pub hooks: ArenaVec<'arena, PropertyHook<'arena, 'src>>,
280    /// Preceding `/** */` doc-block, if any.
281    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
282    pub doc_comment: Option<Comment<'src>>,
283}
284
285impl<'arena, 'src> PropertyDecl<'arena, 'src> {
286    /// Builds a `PropertyDecl` from its identifying fields with every other field empty. Set
287    /// the rest by assignment, as with `Param::new`.
288    pub fn new(arena: &'arena bumpalo::Bump, name: Ident<'src>) -> Self {
289        Self {
290            name,
291            modifiers: Modifiers::new(),
292            type_hint: None,
293            default: None,
294            attributes: ArenaVec::new_in(arena),
295            hooks: ArenaVec::new_in(arena),
296            doc_comment: None,
297        }
298    }
299}
300
301/// Which accessor a property hook implements.
302#[derive(Debug, Clone, Copy, PartialEq, Eq)]
303#[cfg_attr(feature = "serde", derive(Serialize))]
304#[non_exhaustive]
305pub enum PropertyHookKind {
306    /// `get` hook — called when the property is read.
307    Get,
308    /// `set` hook — called when the property is written; receives the incoming value as `$value`.
309    Set,
310}
311
312/// The body form of a property hook.
313#[derive(Debug)]
314#[cfg_attr(feature = "serde", derive(Serialize))]
315#[non_exhaustive]
316pub enum PropertyHookBody<'arena, 'src> {
317    /// `{ stmts }` — a full statement block.
318    Block(&'arena Block<'arena, 'src>),
319    /// `=> expr` — short-form expression body.
320    Expression(Expr<'arena, 'src>),
321    /// No body — the hook is declared abstract (on an abstract class or interface).
322    Abstract,
323}
324
325/// A property hook: `get { ... }` or `set => ...` (PHP 8.4+).
326#[derive(Debug)]
327#[cfg_attr(feature = "serde", derive(Serialize))]
328#[non_exhaustive]
329pub struct PropertyHook<'arena, 'src> {
330    /// Whether this is a `get` or `set` hook.
331    pub kind: PropertyHookKind,
332    /// Hook body.
333    pub body: PropertyHookBody<'arena, 'src>,
334    /// Visibility, `static`, `final`, `abstract` and `readonly` modifiers.
335    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Modifiers::is_empty"))]
336    pub modifiers: Modifiers,
337    /// `true` for `&get`.
338    pub by_ref: bool,
339    /// Explicit parameters, e.g. `set(string $v)`.
340    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
341    /// `#[...]` attributes applied to this node.
342    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
343    /// Source range of this node.
344    pub span: Span,
345}
346
347impl<'arena, 'src> PropertyHook<'arena, 'src> {
348    /// Builds a `PropertyHook` from its identifying fields with every other field empty. Set
349    /// the rest by assignment, as with `Param::new`.
350    pub fn new(
351        arena: &'arena bumpalo::Bump,
352        kind: PropertyHookKind,
353        body: PropertyHookBody<'arena, 'src>,
354        span: Span,
355    ) -> Self {
356        Self {
357            kind,
358            body,
359            modifiers: Modifiers::new(),
360            by_ref: false,
361            params: ArenaVec::new_in(arena),
362            attributes: ArenaVec::new_in(arena),
363            span,
364        }
365    }
366}
367
368/// A method declaration inside a class-like body.
369#[derive(Debug)]
370#[cfg_attr(feature = "serde", derive(Serialize))]
371#[non_exhaustive]
372pub struct MethodDecl<'arena, 'src> {
373    /// Method name.
374    pub name: Ident<'src>,
375    /// Visibility, `static`, `final`, `abstract` and `readonly` modifiers.
376    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Modifiers::is_empty"))]
377    pub modifiers: Modifiers,
378    /// `true` when by reference (`&`).
379    pub by_ref: bool,
380    /// Declared parameters.
381    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
382    /// Declared return type, if any.
383    pub return_type: Option<TypeHint<'arena, 'src>>,
384    /// `None` for an abstract/interface method.
385    pub body: Option<&'arena Block<'arena, 'src>>,
386    /// `#[...]` attributes applied to this node.
387    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
388    /// Preceding `/** */` doc-block, if any.
389    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
390    pub doc_comment: Option<Comment<'src>>,
391}
392
393impl<'arena, 'src> MethodDecl<'arena, 'src> {
394    /// A method with `name` set and every other field empty (no modifiers,
395    /// parameters, return type, body or attributes). Set the rest by
396    /// assignment; see [`Param::new`].
397    pub fn new(arena: &'arena bumpalo::Bump, name: Ident<'src>) -> Self {
398        Self {
399            name,
400            modifiers: Modifiers::new(),
401            by_ref: false,
402            params: ArenaVec::new_in(arena),
403            return_type: None,
404            body: None,
405            attributes: ArenaVec::new_in(arena),
406            doc_comment: None,
407        }
408    }
409}
410
411/// A class constant: `const X = 1;`.
412#[derive(Debug)]
413#[cfg_attr(feature = "serde", derive(Serialize))]
414#[non_exhaustive]
415pub struct ClassConstDecl<'arena, 'src> {
416    /// Constant name.
417    pub name: Ident<'src>,
418    /// Visibility, `static`, `final`, `abstract` and `readonly` modifiers.
419    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Modifiers::is_empty"))]
420    pub modifiers: Modifiers,
421    /// Typed class constant type (PHP 8.3+).
422    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
423    pub type_hint: Option<&'arena TypeHint<'arena, 'src>>,
424    /// Constant value expression.
425    pub value: Expr<'arena, 'src>,
426    /// `#[...]` attributes applied to this node.
427    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
428    /// Preceding `/** */` doc-block, if any.
429    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
430    pub doc_comment: Option<Comment<'src>>,
431}
432
433impl<'arena, 'src> ClassConstDecl<'arena, 'src> {
434    /// Builds a `ClassConstDecl` from its identifying fields with every other field empty. Set
435    /// the rest by assignment, as with `Param::new`.
436    pub fn new(arena: &'arena bumpalo::Bump, name: Ident<'src>, value: Expr<'arena, 'src>) -> Self {
437        Self {
438            name,
439            modifiers: Modifiers::new(),
440            type_hint: None,
441            value,
442            attributes: ArenaVec::new_in(arena),
443            doc_comment: None,
444        }
445    }
446}
447
448/// A `use` of traits inside a class-like body.
449#[derive(Debug)]
450#[cfg_attr(feature = "serde", derive(Serialize))]
451#[non_exhaustive]
452pub struct TraitUseDecl<'arena, 'src> {
453    /// Traits being used.
454    pub traits: ArenaVec<'arena, Name<'arena, 'src>>,
455    /// `insteadof` and `as` rules from the adaptation block.
456    pub adaptations: ArenaVec<'arena, TraitAdaptation<'arena, 'src>>,
457    /// The `{` that opens the adaptations block; `None` when there are no
458    /// adaptations. Printer layout detail, outside semver; not serialized.
459    #[cfg_attr(feature = "serde", serde(skip))]
460    #[doc(hidden)]
461    pub adaptations_brace_span: Option<Span>,
462    /// Preceding `/** */` doc-block, if any.
463    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
464    pub doc_comment: Option<Comment<'src>>,
465}
466
467impl<'arena, 'src> TraitUseDecl<'arena, 'src> {
468    /// Builds a `TraitUseDecl` from its identifying fields with every other field empty. Set
469    /// the rest by assignment, as with `Param::new`.
470    pub fn new(arena: &'arena bumpalo::Bump, traits: ArenaVec<'arena, Name<'arena, 'src>>) -> Self {
471        Self {
472            traits,
473            adaptations: ArenaVec::new_in(arena),
474            adaptations_brace_span: None,
475            doc_comment: None,
476        }
477    }
478}
479
480/// A single rule in a trait `use` adaptation block.
481#[derive(Debug)]
482#[cfg_attr(feature = "serde", derive(Serialize))]
483pub struct TraitAdaptation<'arena, 'src> {
484    /// Kind of adaptation rule.
485    pub kind: TraitAdaptationKind<'arena, 'src>,
486    /// Source range of this node.
487    pub span: Span,
488}
489
490/// The kinds of trait adaptation rule.
491#[derive(Debug)]
492#[cfg_attr(feature = "serde", derive(Serialize))]
493#[non_exhaustive]
494pub enum TraitAdaptationKind<'arena, 'src> {
495    /// `A::foo insteadof B, C;`
496    Precedence {
497        /// Trait qualifying the method, if written.
498        trait_name: Name<'arena, 'src>,
499        /// Method being adapted.
500        method: Name<'arena, 'src>,
501        /// Traits whose method is excluded.
502        insteadof: ArenaVec<'arena, Name<'arena, 'src>>,
503    },
504    /// `foo as bar;` or `A::foo as protected bar;` or `foo as protected;`
505    Alias {
506        /// Trait qualifying the method, if written.
507        trait_name: Option<Name<'arena, 'src>>,
508        /// Method being adapted.
509        method: Name<'arena, 'src>,
510        /// New visibility, if changed.
511        new_modifier: Option<Visibility>,
512        /// `true` for `foo as final bar;` (PHP 8.3+).
513        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "super::is_false"))]
514        is_final: bool,
515        /// Alias name, if given.
516        new_name: Option<Name<'arena, 'src>>,
517    },
518}
519
520/// An interface declaration.
521#[derive(Debug)]
522#[cfg_attr(feature = "serde", derive(Serialize))]
523#[non_exhaustive]
524pub struct InterfaceDecl<'arena, 'src> {
525    /// Interface name.
526    pub name: Ident<'src>,
527    /// Extended interfaces.
528    pub extends: ArenaVec<'arena, Name<'arena, 'src>>,
529    /// Interface body, flattened on serialization.
530    #[cfg_attr(feature = "serde", serde(flatten))]
531    pub body: ClassBody<'arena, 'src>,
532    /// `#[...]` attributes applied to this node.
533    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
534    /// Preceding `/** */` doc-block, if any.
535    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
536    pub doc_comment: Option<Comment<'src>>,
537}
538
539impl<'arena, 'src> InterfaceDecl<'arena, 'src> {
540    /// Builds a `InterfaceDecl` from its identifying fields with every other field empty. Set
541    /// the rest by assignment, as with `Param::new`.
542    pub fn new(
543        arena: &'arena bumpalo::Bump,
544        name: Ident<'src>,
545        body: ClassBody<'arena, 'src>,
546    ) -> Self {
547        Self {
548            name,
549            extends: ArenaVec::new_in(arena),
550            body,
551            attributes: ArenaVec::new_in(arena),
552            doc_comment: None,
553        }
554    }
555}
556
557/// A trait declaration.
558#[derive(Debug)]
559#[cfg_attr(feature = "serde", derive(Serialize))]
560#[non_exhaustive]
561pub struct TraitDecl<'arena, 'src> {
562    /// Trait name.
563    pub name: Ident<'src>,
564    /// Trait body, flattened on serialization.
565    #[cfg_attr(feature = "serde", serde(flatten))]
566    pub body: ClassBody<'arena, 'src>,
567    /// `#[...]` attributes applied to this node.
568    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
569    /// Preceding `/** */` doc-block, if any.
570    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
571    pub doc_comment: Option<Comment<'src>>,
572}
573
574impl<'arena, 'src> TraitDecl<'arena, 'src> {
575    /// Builds a `TraitDecl` from its identifying fields with every other field empty. Set
576    /// the rest by assignment, as with `Param::new`.
577    pub fn new(
578        arena: &'arena bumpalo::Bump,
579        name: Ident<'src>,
580        body: ClassBody<'arena, 'src>,
581    ) -> Self {
582        Self {
583            name,
584            body,
585            attributes: ArenaVec::new_in(arena),
586            doc_comment: None,
587        }
588    }
589}
590
591/// The braced member list of an enum.
592#[derive(Debug)]
593#[cfg_attr(feature = "serde", derive(Serialize))]
594pub struct EnumBody<'arena, 'src> {
595    /// Enum members in source order.
596    pub members: ArenaVec<'arena, EnumMember<'arena, 'src>>,
597    /// Span covering `{` to `}` of the body.
598    #[cfg_attr(feature = "serde", serde(skip))]
599    pub span: Span,
600}
601
602/// An enum declaration (PHP 8.1+).
603#[derive(Debug)]
604#[cfg_attr(feature = "serde", derive(Serialize))]
605#[non_exhaustive]
606pub struct EnumDecl<'arena, 'src> {
607    /// Enum name.
608    pub name: Ident<'src>,
609    /// Backing type of a backed enum.
610    pub scalar_type: Option<Name<'arena, 'src>>,
611    /// Implemented interfaces.
612    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
613    /// Enum body, flattened on serialization.
614    #[cfg_attr(feature = "serde", serde(flatten))]
615    pub body: EnumBody<'arena, 'src>,
616    /// `#[...]` attributes applied to this node.
617    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
618    /// Preceding `/** */` doc-block, if any.
619    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
620    pub doc_comment: Option<Comment<'src>>,
621}
622
623impl<'arena, 'src> EnumDecl<'arena, 'src> {
624    /// Builds a `EnumDecl` from its identifying fields with every other field empty. Set
625    /// the rest by assignment, as with `Param::new`.
626    pub fn new(
627        arena: &'arena bumpalo::Bump,
628        name: Ident<'src>,
629        body: EnumBody<'arena, 'src>,
630    ) -> Self {
631        Self {
632            name,
633            scalar_type: None,
634            implements: ArenaVec::new_in(arena),
635            body,
636            attributes: ArenaVec::new_in(arena),
637            doc_comment: None,
638        }
639    }
640}
641
642/// A member of an enum body.
643#[derive(Debug)]
644#[cfg_attr(feature = "serde", derive(Serialize))]
645pub struct EnumMember<'arena, 'src> {
646    /// Kind of member.
647    pub kind: EnumMemberKind<'arena, 'src>,
648    /// Source range of this node.
649    pub span: Span,
650}
651
652/// The kinds of enum member.
653#[derive(Debug)]
654#[cfg_attr(feature = "serde", derive(Serialize))]
655#[non_exhaustive]
656pub enum EnumMemberKind<'arena, 'src> {
657    /// An enum case: `case Foo;` or `case Foo = 'foo';` (backed enum).
658    Case(EnumCase<'arena, 'src>),
659    /// A method defined inside the enum body.
660    Method(MethodDecl<'arena, 'src>),
661    /// A constant defined inside the enum body: `const X = 1;`.
662    ClassConst(ClassConstDecl<'arena, 'src>),
663    /// A trait use inside the enum body: `use SomeTrait;`.
664    TraitUse(TraitUseDecl<'arena, 'src>),
665}
666
667/// A `case` inside an enum.
668#[derive(Debug)]
669#[cfg_attr(feature = "serde", derive(Serialize))]
670#[non_exhaustive]
671pub struct EnumCase<'arena, 'src> {
672    /// Case name.
673    pub name: Ident<'src>,
674    /// Backing value of a backed case.
675    pub value: Option<Expr<'arena, 'src>>,
676    /// `#[...]` attributes applied to this node.
677    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
678    /// Preceding `/** */` doc-block, if any.
679    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
680    pub doc_comment: Option<Comment<'src>>,
681}
682
683impl<'arena, 'src> EnumCase<'arena, 'src> {
684    /// Builds a `EnumCase` from its identifying fields with every other field empty. Set
685    /// the rest by assignment, as with `Param::new`.
686    pub fn new(arena: &'arena bumpalo::Bump, name: Ident<'src>) -> Self {
687        Self {
688            name,
689            value: None,
690            attributes: ArenaVec::new_in(arena),
691            doc_comment: None,
692        }
693    }
694}