Skip to main content

php_ast/ast/
decls.rs

1use serde::Serialize;
2
3use crate::Span;
4
5use super::{ArenaVec, Attribute, Comment, Expr, Ident, Name, Stmt, TypeHint};
6
7#[derive(Debug, Serialize)]
8pub struct FunctionDecl<'arena, 'src> {
9    pub name: Ident<'src>,
10    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
11    pub body: ArenaVec<'arena, Stmt<'arena, 'src>>,
12    pub return_type: Option<TypeHint<'arena, 'src>>,
13    pub by_ref: bool,
14    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
15    /// The immediately preceding `/** */` doc-block, if any.
16    ///
17    /// When present, this comment is **removed** from
18    /// [`ParseResult::comments`](php_rs_parser::ParseResult::comments) — the
19    /// two collections are disjoint. All other comment forms (line, hash,
20    /// block) remain in `ParseResult::comments` regardless of position.
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub doc_comment: Option<Comment<'src>>,
23}
24
25#[derive(Debug, Serialize)]
26pub struct Param<'arena, 'src> {
27    pub name: Ident<'src>,
28    pub type_hint: Option<TypeHint<'arena, 'src>>,
29    pub default: Option<Expr<'arena, 'src>>,
30    pub by_ref: bool,
31    pub variadic: bool,
32    pub is_readonly: bool,
33    pub is_final: bool,
34    pub visibility: Option<Visibility>,
35    pub set_visibility: Option<Visibility>,
36    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
37    #[serde(skip_serializing_if = "ArenaVec::is_empty")]
38    pub hooks: ArenaVec<'arena, PropertyHook<'arena, 'src>>,
39    pub span: Span,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
43pub enum Visibility {
44    /// `public` — accessible from anywhere.
45    Public,
46    /// `protected` — accessible within the class and its subclasses.
47    Protected,
48    /// `private` — accessible only within the declaring class.
49    Private,
50}
51
52#[derive(Debug, Serialize)]
53pub struct ClassDecl<'arena, 'src> {
54    pub name: Option<Ident<'src>>,
55    pub modifiers: ClassModifiers,
56    pub extends: Option<Name<'arena, 'src>>,
57    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
58    pub members: ArenaVec<'arena, ClassMember<'arena, 'src>>,
59    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub doc_comment: Option<Comment<'src>>,
62}
63
64#[derive(Debug, Clone, Serialize, Default)]
65pub struct ClassModifiers {
66    pub is_abstract: bool,
67    pub is_final: bool,
68    pub is_readonly: bool,
69}
70
71#[derive(Debug, Serialize)]
72pub struct ClassMember<'arena, 'src> {
73    pub kind: ClassMemberKind<'arena, 'src>,
74    pub span: Span,
75}
76
77#[derive(Debug, Serialize)]
78pub enum ClassMemberKind<'arena, 'src> {
79    Property(PropertyDecl<'arena, 'src>),
80    Method(MethodDecl<'arena, 'src>),
81    ClassConst(ClassConstDecl<'arena, 'src>),
82    TraitUse(TraitUseDecl<'arena, 'src>),
83}
84
85#[derive(Debug, Serialize)]
86pub struct PropertyDecl<'arena, 'src> {
87    pub name: Ident<'src>,
88    pub visibility: Option<Visibility>,
89    pub set_visibility: Option<Visibility>,
90    pub is_static: bool,
91    pub is_readonly: bool,
92    pub type_hint: Option<TypeHint<'arena, 'src>>,
93    pub default: Option<Expr<'arena, 'src>>,
94    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
95    #[serde(skip_serializing_if = "ArenaVec::is_empty")]
96    pub hooks: ArenaVec<'arena, PropertyHook<'arena, 'src>>,
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub doc_comment: Option<Comment<'src>>,
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
102pub enum PropertyHookKind {
103    /// `get` hook — called when the property is read.
104    Get,
105    /// `set` hook — called when the property is written; receives the incoming value as `$value`.
106    Set,
107}
108
109#[derive(Debug, Serialize)]
110pub enum PropertyHookBody<'arena, 'src> {
111    /// `{ stmts }` — a full statement block.
112    Block(ArenaVec<'arena, Stmt<'arena, 'src>>),
113    /// `=> expr` — short-form expression body.
114    Expression(Expr<'arena, 'src>),
115    /// No body — the hook is declared abstract (on an abstract class or interface).
116    Abstract,
117}
118
119#[derive(Debug, Serialize)]
120pub struct PropertyHook<'arena, 'src> {
121    pub kind: PropertyHookKind,
122    pub body: PropertyHookBody<'arena, 'src>,
123    pub is_final: bool,
124    pub by_ref: bool,
125    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
126    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
127    pub span: Span,
128}
129
130#[derive(Debug, Serialize)]
131pub struct MethodDecl<'arena, 'src> {
132    pub name: Ident<'src>,
133    pub visibility: Option<Visibility>,
134    pub is_static: bool,
135    pub is_abstract: bool,
136    pub is_final: bool,
137    pub by_ref: bool,
138    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
139    pub return_type: Option<TypeHint<'arena, 'src>>,
140    pub body: Option<ArenaVec<'arena, Stmt<'arena, 'src>>>,
141    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
142    #[serde(skip_serializing_if = "Option::is_none")]
143    pub doc_comment: Option<Comment<'src>>,
144}
145
146#[derive(Debug, Serialize)]
147pub struct ClassConstDecl<'arena, 'src> {
148    pub name: Ident<'src>,
149    pub visibility: Option<Visibility>,
150    pub is_final: bool,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub type_hint: Option<&'arena TypeHint<'arena, 'src>>,
153    pub value: Expr<'arena, 'src>,
154    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub doc_comment: Option<Comment<'src>>,
157}
158
159#[derive(Debug, Serialize)]
160pub struct TraitUseDecl<'arena, 'src> {
161    pub traits: ArenaVec<'arena, Name<'arena, 'src>>,
162    pub adaptations: ArenaVec<'arena, TraitAdaptation<'arena, 'src>>,
163}
164
165#[derive(Debug, Serialize)]
166pub struct TraitAdaptation<'arena, 'src> {
167    pub kind: TraitAdaptationKind<'arena, 'src>,
168    pub span: Span,
169}
170
171#[derive(Debug, Serialize)]
172pub enum TraitAdaptationKind<'arena, 'src> {
173    /// `A::foo insteadof B, C;`
174    Precedence {
175        trait_name: Name<'arena, 'src>,
176        method: Name<'arena, 'src>,
177        insteadof: ArenaVec<'arena, Name<'arena, 'src>>,
178    },
179    /// `foo as bar;` or `A::foo as protected bar;` or `foo as protected;`
180    Alias {
181        trait_name: Option<Name<'arena, 'src>>,
182        method: Name<'arena, 'src>,
183        new_modifier: Option<Visibility>,
184        new_name: Option<Name<'arena, 'src>>,
185    },
186}
187
188#[derive(Debug, Serialize)]
189pub struct InterfaceDecl<'arena, 'src> {
190    pub name: Ident<'src>,
191    pub extends: ArenaVec<'arena, Name<'arena, 'src>>,
192    pub members: ArenaVec<'arena, ClassMember<'arena, 'src>>,
193    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub doc_comment: Option<Comment<'src>>,
196}
197
198#[derive(Debug, Serialize)]
199pub struct TraitDecl<'arena, 'src> {
200    pub name: Ident<'src>,
201    pub members: ArenaVec<'arena, ClassMember<'arena, 'src>>,
202    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub doc_comment: Option<Comment<'src>>,
205}
206
207#[derive(Debug, Serialize)]
208pub struct EnumDecl<'arena, 'src> {
209    pub name: Ident<'src>,
210    pub scalar_type: Option<Name<'arena, 'src>>,
211    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
212    pub members: ArenaVec<'arena, EnumMember<'arena, 'src>>,
213    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub doc_comment: Option<Comment<'src>>,
216}
217
218#[derive(Debug, Serialize)]
219pub struct EnumMember<'arena, 'src> {
220    pub kind: EnumMemberKind<'arena, 'src>,
221    pub span: Span,
222}
223
224#[derive(Debug, Serialize)]
225pub enum EnumMemberKind<'arena, 'src> {
226    /// An enum case: `case Foo;` or `case Foo = 'foo';` (backed enum).
227    Case(EnumCase<'arena, 'src>),
228    /// A method defined inside the enum body.
229    Method(MethodDecl<'arena, 'src>),
230    /// A constant defined inside the enum body: `const X = 1;`.
231    ClassConst(ClassConstDecl<'arena, 'src>),
232    /// A trait use inside the enum body: `use SomeTrait;`.
233    TraitUse(TraitUseDecl<'arena, 'src>),
234}
235
236#[derive(Debug, Serialize)]
237pub struct EnumCase<'arena, 'src> {
238    pub name: Ident<'src>,
239    pub value: Option<Expr<'arena, 'src>>,
240    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub doc_comment: Option<Comment<'src>>,
243}