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