Skip to main content

php_ast/ast/
decls.rs

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