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#[derive(Debug, Serialize)]
8pub struct FunctionDecl<'arena, 'src> {
9    pub name: Ident<'src>,
10    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
11    pub body: &'arena Block<'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 ClassBody<'arena, 'src> {
54    pub members: ArenaVec<'arena, ClassMember<'arena, 'src>>,
55    /// Span covering `{` to `}` of the body.
56    #[serde(skip)]
57    pub span: Span,
58}
59
60#[derive(Debug, Serialize)]
61pub struct ClassDecl<'arena, 'src> {
62    pub name: Option<Ident<'src>>,
63    pub modifiers: ClassModifiers,
64    pub extends: Option<Name<'arena, 'src>>,
65    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
66    #[serde(flatten)]
67    pub body: ClassBody<'arena, 'src>,
68    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub doc_comment: Option<Comment<'src>>,
71}
72
73#[derive(Debug, Clone, Serialize, Default)]
74pub struct ClassModifiers {
75    pub is_abstract: bool,
76    pub is_final: bool,
77    pub is_readonly: bool,
78}
79
80#[derive(Debug, Serialize)]
81pub struct ClassMember<'arena, 'src> {
82    pub kind: ClassMemberKind<'arena, 'src>,
83    pub span: Span,
84}
85
86#[derive(Debug, Serialize)]
87pub enum ClassMemberKind<'arena, 'src> {
88    Property(PropertyDecl<'arena, 'src>),
89    Method(MethodDecl<'arena, 'src>),
90    ClassConst(ClassConstDecl<'arena, 'src>),
91    TraitUse(TraitUseDecl<'arena, 'src>),
92}
93
94#[derive(Debug, Serialize)]
95pub struct PropertyDecl<'arena, 'src> {
96    pub name: Ident<'src>,
97    pub visibility: Option<Visibility>,
98    pub set_visibility: Option<Visibility>,
99    pub is_static: bool,
100    pub is_readonly: bool,
101    pub type_hint: Option<TypeHint<'arena, 'src>>,
102    pub default: Option<Expr<'arena, 'src>>,
103    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
104    #[serde(skip_serializing_if = "ArenaVec::is_empty")]
105    pub hooks: ArenaVec<'arena, PropertyHook<'arena, 'src>>,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub doc_comment: Option<Comment<'src>>,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
111pub enum PropertyHookKind {
112    /// `get` hook — called when the property is read.
113    Get,
114    /// `set` hook — called when the property is written; receives the incoming value as `$value`.
115    Set,
116}
117
118#[derive(Debug, Serialize)]
119pub enum PropertyHookBody<'arena, 'src> {
120    /// `{ stmts }` — a full statement block.
121    Block(&'arena Block<'arena, 'src>),
122    /// `=> expr` — short-form expression body.
123    Expression(Expr<'arena, 'src>),
124    /// No body — the hook is declared abstract (on an abstract class or interface).
125    Abstract,
126}
127
128#[derive(Debug, Serialize)]
129pub struct PropertyHook<'arena, 'src> {
130    pub kind: PropertyHookKind,
131    pub body: PropertyHookBody<'arena, 'src>,
132    pub is_final: bool,
133    pub by_ref: bool,
134    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
135    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
136    pub span: Span,
137}
138
139#[derive(Debug, Serialize)]
140pub struct MethodDecl<'arena, 'src> {
141    pub name: Ident<'src>,
142    pub visibility: Option<Visibility>,
143    pub is_static: bool,
144    pub is_abstract: bool,
145    pub is_final: bool,
146    pub by_ref: bool,
147    pub params: ArenaVec<'arena, Param<'arena, 'src>>,
148    pub return_type: Option<TypeHint<'arena, 'src>>,
149    /// `None` for an abstract/interface method.
150    pub body: Option<&'arena Block<'arena, 'src>>,
151    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub doc_comment: Option<Comment<'src>>,
154}
155
156#[derive(Debug, Serialize)]
157pub struct ClassConstDecl<'arena, 'src> {
158    pub name: Ident<'src>,
159    pub visibility: Option<Visibility>,
160    pub is_final: bool,
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub type_hint: Option<&'arena TypeHint<'arena, 'src>>,
163    pub value: Expr<'arena, 'src>,
164    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub doc_comment: Option<Comment<'src>>,
167}
168
169#[derive(Debug, Serialize)]
170pub struct TraitUseDecl<'arena, 'src> {
171    pub traits: ArenaVec<'arena, Name<'arena, 'src>>,
172    pub adaptations: ArenaVec<'arena, TraitAdaptation<'arena, 'src>>,
173    /// Start byte offset of the `{` that opens the adaptations block; `None` when there are no adaptations.
174    #[serde(skip)]
175    pub adaptations_brace_start: Option<u32>,
176}
177
178#[derive(Debug, Serialize)]
179pub struct TraitAdaptation<'arena, 'src> {
180    pub kind: TraitAdaptationKind<'arena, 'src>,
181    pub span: Span,
182}
183
184#[derive(Debug, Serialize)]
185pub enum TraitAdaptationKind<'arena, 'src> {
186    /// `A::foo insteadof B, C;`
187    Precedence {
188        trait_name: Name<'arena, 'src>,
189        method: Name<'arena, 'src>,
190        insteadof: ArenaVec<'arena, Name<'arena, 'src>>,
191    },
192    /// `foo as bar;` or `A::foo as protected bar;` or `foo as protected;`
193    Alias {
194        trait_name: Option<Name<'arena, 'src>>,
195        method: Name<'arena, 'src>,
196        new_modifier: Option<Visibility>,
197        new_name: Option<Name<'arena, 'src>>,
198    },
199}
200
201#[derive(Debug, Serialize)]
202pub struct InterfaceDecl<'arena, 'src> {
203    pub name: Ident<'src>,
204    pub extends: ArenaVec<'arena, Name<'arena, 'src>>,
205    #[serde(flatten)]
206    pub body: ClassBody<'arena, 'src>,
207    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub doc_comment: Option<Comment<'src>>,
210}
211
212#[derive(Debug, Serialize)]
213pub struct TraitDecl<'arena, 'src> {
214    pub name: Ident<'src>,
215    #[serde(flatten)]
216    pub body: ClassBody<'arena, 'src>,
217    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub doc_comment: Option<Comment<'src>>,
220}
221
222#[derive(Debug, Serialize)]
223pub struct EnumBody<'arena, 'src> {
224    pub members: ArenaVec<'arena, EnumMember<'arena, 'src>>,
225    /// Span covering `{` to `}` of the body.
226    #[serde(skip)]
227    pub span: Span,
228}
229
230#[derive(Debug, Serialize)]
231pub struct EnumDecl<'arena, 'src> {
232    pub name: Ident<'src>,
233    pub scalar_type: Option<Name<'arena, 'src>>,
234    pub implements: ArenaVec<'arena, Name<'arena, 'src>>,
235    #[serde(flatten)]
236    pub body: EnumBody<'arena, 'src>,
237    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub doc_comment: Option<Comment<'src>>,
240}
241
242#[derive(Debug, Serialize)]
243pub struct EnumMember<'arena, 'src> {
244    pub kind: EnumMemberKind<'arena, 'src>,
245    pub span: Span,
246}
247
248#[derive(Debug, Serialize)]
249pub enum EnumMemberKind<'arena, 'src> {
250    /// An enum case: `case Foo;` or `case Foo = 'foo';` (backed enum).
251    Case(EnumCase<'arena, 'src>),
252    /// A method defined inside the enum body.
253    Method(MethodDecl<'arena, 'src>),
254    /// A constant defined inside the enum body: `const X = 1;`.
255    ClassConst(ClassConstDecl<'arena, 'src>),
256    /// A trait use inside the enum body: `use SomeTrait;`.
257    TraitUse(TraitUseDecl<'arena, 'src>),
258}
259
260#[derive(Debug, Serialize)]
261pub struct EnumCase<'arena, 'src> {
262    pub name: Ident<'src>,
263    pub value: Option<Expr<'arena, 'src>>,
264    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub doc_comment: Option<Comment<'src>>,
267}