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))]
11pub struct FunctionDecl<'arena, 'src> {
12    /// Function name.
13    pub name: Ident<'src>,
14    /// Declared parameters.
15    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
16    /// Function body.
17    pub body: &'arena Block<'arena, 'src>,
18    /// Declared return type, if any.
19    pub return_type: Option<TypeHint<'arena, 'src>>,
20    /// `true` when by reference (`&`).
21    pub by_ref: bool,
22    /// `#[...]` attributes applied to this node.
23    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
24    /// The immediately preceding `/** */` doc-block, if any.
25    ///
26    /// When present, this comment is **removed** from
27    /// `ParseResult::comments` — the
28    /// two collections are disjoint. All other comment forms (line, hash,
29    /// block) remain in `ParseResult::comments` regardless of position.
30    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
31    pub doc_comment: Option<Comment<'src>>,
32}
33
34/// A function, method, closure or hook parameter.
35#[derive(Debug)]
36#[cfg_attr(feature = "serde", derive(Serialize))]
37#[non_exhaustive]
38pub struct Param<'arena, 'src> {
39    /// Parameter name without `$`.
40    pub name: Ident<'src>,
41    /// Declared parameter type.
42    pub type_hint: Option<TypeHint<'arena, 'src>>,
43    /// Default value expression.
44    pub default: Option<Expr<'arena, 'src>>,
45    /// `true` when by reference (`&`).
46    pub by_ref: bool,
47    /// `true` for a variadic parameter (`...$x`).
48    pub variadic: bool,
49    /// `true` for a `readonly` promoted property.
50    pub is_readonly: bool,
51    /// `true` for a `final` promoted property.
52    pub is_final: bool,
53    /// Visibility of a promoted constructor property.
54    pub visibility: Option<Visibility>,
55    /// Asymmetric write visibility, e.g. `private(set)` (PHP 8.4+).
56    pub set_visibility: Option<Visibility>,
57    /// `#[...]` attributes applied to this node.
58    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
59    /// Property hooks on a promoted property (PHP 8.4+).
60    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "ArenaVec::is_empty"))]
61    pub hooks: ArenaVec<'arena, PropertyHook<'arena, 'src>>,
62    /// A doc-block attached directly to this parameter (PHP 8.6 recognizes
63    /// this for reflection purposes), either immediately before it or
64    /// trailing it up to the comma. Parsed at every version since a bare
65    /// comment is legal PHP regardless of target version.
66    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
67    pub doc_comment: Option<Comment<'src>>,
68    /// Source range of this node.
69    pub span: Span,
70}
71
72impl<'arena, 'src> Param<'arena, 'src> {
73    /// A parameter with `name` and `span` set and every other field empty
74    /// (`None`, `false`, no attributes or hooks). The fields are public, so set
75    /// the rest by assignment; `Param` is `#[non_exhaustive]` and cannot be
76    /// built with a struct literal outside this crate.
77    pub fn new(arena: &'arena bumpalo::Bump, name: Ident<'src>, span: Span) -> Self {
78        Self {
79            name,
80            type_hint: None,
81            default: None,
82            by_ref: false,
83            variadic: false,
84            is_readonly: false,
85            is_final: false,
86            visibility: None,
87            set_visibility: None,
88            attributes: ArenaVec::new_in(arena),
89            hooks: ArenaVec::new_in(arena),
90            doc_comment: None,
91            span,
92        }
93    }
94}
95
96/// Member visibility modifier.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98#[cfg_attr(feature = "serde", derive(Serialize))]
99#[non_exhaustive]
100pub enum Visibility {
101    /// `public` — accessible from anywhere.
102    Public,
103    /// `protected` — accessible within the class and its subclasses.
104    Protected,
105    /// `private` — accessible only within the declaring class.
106    Private,
107}
108
109/// The braced member list of a class-like declaration.
110#[derive(Debug)]
111#[cfg_attr(feature = "serde", derive(Serialize))]
112pub struct ClassBody<'arena, 'src> {
113    /// Class members in source order.
114    pub members: ArenaVec<'arena, ClassMember<'arena, 'src>>,
115    /// Span covering `{` to `}` of the body.
116    #[cfg_attr(feature = "serde", serde(skip))]
117    pub span: Span,
118}
119
120/// A class declaration, or the body of an anonymous class.
121#[derive(Debug)]
122#[cfg_attr(feature = "serde", derive(Serialize))]
123pub struct ClassDecl<'arena, 'src> {
124    /// Class name; `None` for anonymous classes.
125    pub name: Option<Ident<'src>>,
126    /// `abstract`, `final` and `readonly` modifiers.
127    pub modifiers: ClassModifiers,
128    /// Parent class.
129    pub extends: Option<Name<'arena, 'src>>,
130    /// Implemented interfaces.
131    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
132    /// Class body, flattened on serialization.
133    #[cfg_attr(feature = "serde", serde(flatten))]
134    pub body: ClassBody<'arena, 'src>,
135    /// `#[...]` attributes applied to this node.
136    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
137    /// Preceding `/** */` doc-block, if any.
138    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
139    pub doc_comment: Option<Comment<'src>>,
140}
141
142/// Modifiers preceding `class`.
143#[derive(Debug, Clone, Default)]
144#[cfg_attr(feature = "serde", derive(Serialize))]
145#[non_exhaustive]
146pub struct ClassModifiers {
147    /// `true` for `abstract class`.
148    pub is_abstract: bool,
149    /// `true` for `final class`.
150    pub is_final: bool,
151    /// `true` for `readonly class`.
152    pub is_readonly: bool,
153}
154
155/// A member of a class, interface or trait body.
156#[derive(Debug)]
157#[cfg_attr(feature = "serde", derive(Serialize))]
158pub struct ClassMember<'arena, 'src> {
159    /// Kind of member.
160    pub kind: ClassMemberKind<'arena, 'src>,
161    /// Source range of this node.
162    pub span: Span,
163}
164
165/// The kinds of class-like member.
166#[derive(Debug)]
167#[cfg_attr(feature = "serde", derive(Serialize))]
168#[non_exhaustive]
169pub enum ClassMemberKind<'arena, 'src> {
170    /// Property declaration.
171    Property(PropertyDecl<'arena, 'src>),
172    /// Method declaration.
173    Method(MethodDecl<'arena, 'src>),
174    /// Class constant declaration.
175    ClassConst(ClassConstDecl<'arena, 'src>),
176    /// Trait `use` statement.
177    TraitUse(TraitUseDecl<'arena, 'src>),
178}
179
180/// A property declaration: `public int $x = 0;`.
181#[derive(Debug)]
182#[cfg_attr(feature = "serde", derive(Serialize))]
183pub struct PropertyDecl<'arena, 'src> {
184    /// Property name without `$`.
185    pub name: Ident<'src>,
186    /// Declared visibility, if any.
187    pub visibility: Option<Visibility>,
188    /// Asymmetric write visibility, e.g. `private(set)` (PHP 8.4+).
189    pub set_visibility: Option<Visibility>,
190    /// `true` for `static`.
191    pub is_static: bool,
192    /// `true` for `readonly`.
193    pub is_readonly: bool,
194    /// Declared type, if any.
195    pub type_hint: Option<TypeHint<'arena, 'src>>,
196    /// Default value expression.
197    pub default: Option<Expr<'arena, 'src>>,
198    /// `#[...]` attributes applied to this node.
199    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
200    /// Property hooks (PHP 8.4+).
201    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "ArenaVec::is_empty"))]
202    pub hooks: ArenaVec<'arena, PropertyHook<'arena, 'src>>,
203    /// Preceding `/** */` doc-block, if any.
204    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
205    pub doc_comment: Option<Comment<'src>>,
206}
207
208/// Which accessor a property hook implements.
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
210#[cfg_attr(feature = "serde", derive(Serialize))]
211#[non_exhaustive]
212pub enum PropertyHookKind {
213    /// `get` hook — called when the property is read.
214    Get,
215    /// `set` hook — called when the property is written; receives the incoming value as `$value`.
216    Set,
217}
218
219/// The body form of a property hook.
220#[derive(Debug)]
221#[cfg_attr(feature = "serde", derive(Serialize))]
222#[non_exhaustive]
223pub enum PropertyHookBody<'arena, 'src> {
224    /// `{ stmts }` — a full statement block.
225    Block(&'arena Block<'arena, 'src>),
226    /// `=> expr` — short-form expression body.
227    Expression(Expr<'arena, 'src>),
228    /// No body — the hook is declared abstract (on an abstract class or interface).
229    Abstract,
230}
231
232/// A property hook: `get { ... }` or `set => ...` (PHP 8.4+).
233#[derive(Debug)]
234#[cfg_attr(feature = "serde", derive(Serialize))]
235pub struct PropertyHook<'arena, 'src> {
236    /// Whether this is a `get` or `set` hook.
237    pub kind: PropertyHookKind,
238    /// Hook body.
239    pub body: PropertyHookBody<'arena, 'src>,
240    /// `true` for a `final` hook.
241    pub is_final: bool,
242    /// `true` for `&get`.
243    pub by_ref: bool,
244    /// Explicit parameters, e.g. `set(string $v)`.
245    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
246    /// `#[...]` attributes applied to this node.
247    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
248    /// Source range of this node.
249    pub span: Span,
250}
251
252/// A method declaration inside a class-like body.
253#[derive(Debug)]
254#[cfg_attr(feature = "serde", derive(Serialize))]
255#[non_exhaustive]
256pub struct MethodDecl<'arena, 'src> {
257    /// Method name.
258    pub name: Ident<'src>,
259    /// Declared visibility, if any.
260    pub visibility: Option<Visibility>,
261    /// `true` for `static`.
262    pub is_static: bool,
263    /// `true` for `abstract`.
264    pub is_abstract: bool,
265    /// `true` for `final`.
266    pub is_final: bool,
267    /// `true` when by reference (`&`).
268    pub by_ref: bool,
269    /// Declared parameters.
270    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
271    /// Declared return type, if any.
272    pub return_type: Option<TypeHint<'arena, 'src>>,
273    /// `None` for an abstract/interface method.
274    pub body: Option<&'arena Block<'arena, 'src>>,
275    /// `#[...]` attributes applied to this node.
276    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
277    /// Preceding `/** */` doc-block, if any.
278    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
279    pub doc_comment: Option<Comment<'src>>,
280}
281
282impl<'arena, 'src> MethodDecl<'arena, 'src> {
283    /// A method with `name` set and every other field empty (no modifiers,
284    /// parameters, return type, body or attributes). Set the rest by
285    /// assignment; see [`Param::new`].
286    pub fn new(arena: &'arena bumpalo::Bump, name: Ident<'src>) -> Self {
287        Self {
288            name,
289            visibility: None,
290            is_static: false,
291            is_abstract: false,
292            is_final: false,
293            by_ref: false,
294            params: ArenaVec::new_in(arena),
295            return_type: None,
296            body: None,
297            attributes: ArenaVec::new_in(arena),
298            doc_comment: None,
299        }
300    }
301}
302
303/// A class constant: `const X = 1;`.
304#[derive(Debug)]
305#[cfg_attr(feature = "serde", derive(Serialize))]
306pub struct ClassConstDecl<'arena, 'src> {
307    /// Constant name.
308    pub name: Ident<'src>,
309    /// Declared visibility, if any.
310    pub visibility: Option<Visibility>,
311    /// `true` for `final`.
312    pub is_final: bool,
313    /// Typed class constant type (PHP 8.3+).
314    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
315    pub type_hint: Option<&'arena TypeHint<'arena, 'src>>,
316    /// Constant value expression.
317    pub value: Expr<'arena, 'src>,
318    /// `#[...]` attributes applied to this node.
319    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
320    /// Preceding `/** */` doc-block, if any.
321    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
322    pub doc_comment: Option<Comment<'src>>,
323}
324
325/// A `use` of traits inside a class-like body.
326#[derive(Debug)]
327#[cfg_attr(feature = "serde", derive(Serialize))]
328pub struct TraitUseDecl<'arena, 'src> {
329    /// Traits being used.
330    pub traits: ArenaVec<'arena, Name<'arena, 'src>>,
331    /// `insteadof` and `as` rules from the adaptation block.
332    pub adaptations: ArenaVec<'arena, TraitAdaptation<'arena, 'src>>,
333    /// The `{` that opens the adaptations block; `None` when there are no
334    /// adaptations. Not serialized.
335    #[cfg_attr(feature = "serde", serde(skip))]
336    pub adaptations_brace_span: Option<Span>,
337    /// Preceding `/** */` doc-block, if any.
338    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
339    pub doc_comment: Option<Comment<'src>>,
340}
341
342/// A single rule in a trait `use` adaptation block.
343#[derive(Debug)]
344#[cfg_attr(feature = "serde", derive(Serialize))]
345pub struct TraitAdaptation<'arena, 'src> {
346    /// Kind of adaptation rule.
347    pub kind: TraitAdaptationKind<'arena, 'src>,
348    /// Source range of this node.
349    pub span: Span,
350}
351
352/// The kinds of trait adaptation rule.
353#[derive(Debug)]
354#[cfg_attr(feature = "serde", derive(Serialize))]
355#[non_exhaustive]
356pub enum TraitAdaptationKind<'arena, 'src> {
357    /// `A::foo insteadof B, C;`
358    Precedence {
359        /// Trait qualifying the method, if written.
360        trait_name: Name<'arena, 'src>,
361        /// Method being adapted.
362        method: Name<'arena, 'src>,
363        /// Traits whose method is excluded.
364        insteadof: ArenaVec<'arena, Name<'arena, 'src>>,
365    },
366    /// `foo as bar;` or `A::foo as protected bar;` or `foo as protected;`
367    Alias {
368        /// Trait qualifying the method, if written.
369        trait_name: Option<Name<'arena, 'src>>,
370        /// Method being adapted.
371        method: Name<'arena, 'src>,
372        /// New visibility, if changed.
373        new_modifier: Option<Visibility>,
374        /// Alias name, if given.
375        new_name: Option<Name<'arena, 'src>>,
376    },
377}
378
379/// An interface declaration.
380#[derive(Debug)]
381#[cfg_attr(feature = "serde", derive(Serialize))]
382pub struct InterfaceDecl<'arena, 'src> {
383    /// Interface name.
384    pub name: Ident<'src>,
385    /// Extended interfaces.
386    pub extends: ArenaVec<'arena, Name<'arena, 'src>>,
387    /// Interface body, flattened on serialization.
388    #[cfg_attr(feature = "serde", serde(flatten))]
389    pub body: ClassBody<'arena, 'src>,
390    /// `#[...]` attributes applied to this node.
391    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
392    /// Preceding `/** */` doc-block, if any.
393    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
394    pub doc_comment: Option<Comment<'src>>,
395}
396
397/// A trait declaration.
398#[derive(Debug)]
399#[cfg_attr(feature = "serde", derive(Serialize))]
400pub struct TraitDecl<'arena, 'src> {
401    /// Trait name.
402    pub name: Ident<'src>,
403    /// Trait body, flattened on serialization.
404    #[cfg_attr(feature = "serde", serde(flatten))]
405    pub body: ClassBody<'arena, 'src>,
406    /// `#[...]` attributes applied to this node.
407    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
408    /// Preceding `/** */` doc-block, if any.
409    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
410    pub doc_comment: Option<Comment<'src>>,
411}
412
413/// The braced member list of an enum.
414#[derive(Debug)]
415#[cfg_attr(feature = "serde", derive(Serialize))]
416pub struct EnumBody<'arena, 'src> {
417    /// Enum members in source order.
418    pub members: ArenaVec<'arena, EnumMember<'arena, 'src>>,
419    /// Span covering `{` to `}` of the body.
420    #[cfg_attr(feature = "serde", serde(skip))]
421    pub span: Span,
422}
423
424/// An enum declaration (PHP 8.1+).
425#[derive(Debug)]
426#[cfg_attr(feature = "serde", derive(Serialize))]
427pub struct EnumDecl<'arena, 'src> {
428    /// Enum name.
429    pub name: Ident<'src>,
430    /// Backing type of a backed enum.
431    pub scalar_type: Option<Name<'arena, 'src>>,
432    /// Implemented interfaces.
433    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
434    /// Enum body, flattened on serialization.
435    #[cfg_attr(feature = "serde", serde(flatten))]
436    pub body: EnumBody<'arena, 'src>,
437    /// `#[...]` attributes applied to this node.
438    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
439    /// Preceding `/** */` doc-block, if any.
440    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
441    pub doc_comment: Option<Comment<'src>>,
442}
443
444/// A member of an enum body.
445#[derive(Debug)]
446#[cfg_attr(feature = "serde", derive(Serialize))]
447pub struct EnumMember<'arena, 'src> {
448    /// Kind of member.
449    pub kind: EnumMemberKind<'arena, 'src>,
450    /// Source range of this node.
451    pub span: Span,
452}
453
454/// The kinds of enum member.
455#[derive(Debug)]
456#[cfg_attr(feature = "serde", derive(Serialize))]
457#[non_exhaustive]
458pub enum EnumMemberKind<'arena, 'src> {
459    /// An enum case: `case Foo;` or `case Foo = 'foo';` (backed enum).
460    Case(EnumCase<'arena, 'src>),
461    /// A method defined inside the enum body.
462    Method(MethodDecl<'arena, 'src>),
463    /// A constant defined inside the enum body: `const X = 1;`.
464    ClassConst(ClassConstDecl<'arena, 'src>),
465    /// A trait use inside the enum body: `use SomeTrait;`.
466    TraitUse(TraitUseDecl<'arena, 'src>),
467}
468
469/// A `case` inside an enum.
470#[derive(Debug)]
471#[cfg_attr(feature = "serde", derive(Serialize))]
472pub struct EnumCase<'arena, 'src> {
473    /// Case name.
474    pub name: Ident<'src>,
475    /// Backing value of a backed case.
476    pub value: Option<Expr<'arena, 'src>>,
477    /// `#[...]` attributes applied to this node.
478    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
479    /// Preceding `/** */` doc-block, if any.
480    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
481    pub doc_comment: Option<Comment<'src>>,
482}