Skip to main content

php_ast/ast/
stmts.rs

1use serde::Serialize;
2
3use crate::Span;
4
5use super::{
6    ArenaVec, Attribute, ClassDecl, Comment, EnumDecl, Expr, FunctionDecl, Ident, InterfaceDecl,
7    Name, TraitDecl,
8};
9
10fn is_false(b: &bool) -> bool {
11    !b
12}
13
14#[derive(Debug, Serialize)]
15pub struct Stmt<'arena, 'src> {
16    pub kind: StmtKind<'arena, 'src>,
17    pub span: Span,
18}
19
20/// A brace-delimited statement block. Used both as a standalone block
21/// statement ([`StmtKind::Block`]) and as the body of constructs that are
22/// *always* braced (functions, methods, closures, `try`/`catch`/`finally`,
23/// braced namespaces, property-hook blocks) — those positions hold a
24/// `&Block` so the "it's a block" invariant is enforced by the type.
25#[derive(Debug, Serialize)]
26#[serde(transparent)]
27pub struct Block<'arena, 'src> {
28    pub stmts: ArenaVec<'arena, Stmt<'arena, 'src>>,
29    /// Span covering `{`..`}` (or the keyword-delimited region for the
30    /// alternative-syntax blocks reachable only via [`StmtKind::Block`]).
31    #[serde(skip)]
32    pub span: Span,
33}
34
35#[derive(Debug, Serialize)]
36pub enum StmtKind<'arena, 'src> {
37    /// Expression statement (e.g. `foo();`)
38    Expression(&'arena Expr<'arena, 'src>),
39
40    /// Echo statement: `echo expr1, expr2;`
41    Echo(ArenaVec<'arena, Expr<'arena, 'src>>),
42
43    /// Return statement: `return expr;`
44    Return(Option<&'arena Expr<'arena, 'src>>),
45
46    /// Block statement: `{ stmts }`
47    Block(&'arena Block<'arena, 'src>),
48
49    /// If statement
50    If(&'arena IfStmt<'arena, 'src>),
51
52    /// While loop
53    While(&'arena WhileStmt<'arena, 'src>),
54
55    /// For loop
56    For(&'arena ForStmt<'arena, 'src>),
57
58    /// Foreach loop
59    Foreach(&'arena ForeachStmt<'arena, 'src>),
60
61    /// Do-while loop
62    DoWhile(&'arena DoWhileStmt<'arena, 'src>),
63
64    /// Function declaration
65    Function(&'arena FunctionDecl<'arena, 'src>),
66
67    /// Break statement
68    Break(Option<&'arena Expr<'arena, 'src>>),
69
70    /// Continue statement
71    Continue(Option<&'arena Expr<'arena, 'src>>),
72
73    /// Switch statement
74    Switch(&'arena SwitchStmt<'arena, 'src>),
75
76    /// Goto statement
77    Goto(Ident<'src>),
78
79    /// Label statement
80    Label(&'arena str),
81
82    /// Declare statement
83    Declare(&'arena DeclareStmt<'arena, 'src>),
84
85    /// Unset statement
86    Unset(ArenaVec<'arena, Expr<'arena, 'src>>),
87
88    /// Throw statement (also can be expression in PHP 8)
89    Throw(&'arena Expr<'arena, 'src>),
90
91    /// Try/catch/finally
92    TryCatch(&'arena TryCatchStmt<'arena, 'src>),
93
94    /// Global declaration
95    Global(ArenaVec<'arena, Expr<'arena, 'src>>),
96
97    /// Class declaration
98    Class(&'arena ClassDecl<'arena, 'src>),
99
100    /// Interface declaration
101    Interface(&'arena InterfaceDecl<'arena, 'src>),
102
103    /// Trait declaration
104    Trait(&'arena TraitDecl<'arena, 'src>),
105
106    /// Enum declaration
107    Enum(&'arena EnumDecl<'arena, 'src>),
108
109    /// Namespace declaration
110    Namespace(&'arena NamespaceDecl<'arena, 'src>),
111
112    /// Use declaration
113    Use(&'arena UseDecl<'arena, 'src>),
114
115    /// Top-level constant: `const FOO = expr;`
116    Const(ArenaVec<'arena, ConstItem<'arena, 'src>>),
117
118    /// Static variable declaration: `static $x = 1;`
119    StaticVar(ArenaVec<'arena, StaticVar<'arena, 'src>>),
120
121    /// __halt_compiler(); with remaining data
122    HaltCompiler(&'src str),
123
124    /// Nop (empty statement `;`)
125    Nop,
126
127    /// Inline HTML
128    InlineHtml(&'src str),
129
130    /// Error placeholder — parser always produces a tree
131    Error,
132}
133
134#[derive(Debug, Serialize)]
135pub struct IfStmt<'arena, 'src> {
136    pub condition: Expr<'arena, 'src>,
137    pub then_branch: &'arena Stmt<'arena, 'src>,
138    pub elseif_branches: ArenaVec<'arena, ElseIfBranch<'arena, 'src>>,
139    pub else_branch: Option<&'arena Stmt<'arena, 'src>>,
140    /// Start byte offset of the `else` keyword; `None` when there is no else branch.
141    #[serde(skip)]
142    pub else_kw_start: Option<u32>,
143    #[serde(default, skip_serializing_if = "is_false")]
144    pub uses_alternative: bool,
145}
146
147#[derive(Debug, Serialize)]
148pub struct ElseIfBranch<'arena, 'src> {
149    pub condition: Expr<'arena, 'src>,
150    pub body: Stmt<'arena, 'src>,
151    pub span: Span,
152}
153
154#[derive(Debug, Serialize)]
155pub struct WhileStmt<'arena, 'src> {
156    pub condition: Expr<'arena, 'src>,
157    pub body: &'arena Stmt<'arena, 'src>,
158    #[serde(default, skip_serializing_if = "is_false")]
159    pub uses_alternative: bool,
160}
161
162#[derive(Debug, Serialize)]
163pub struct ForStmt<'arena, 'src> {
164    pub init: ArenaVec<'arena, Expr<'arena, 'src>>,
165    pub condition: ArenaVec<'arena, Expr<'arena, 'src>>,
166    pub update: ArenaVec<'arena, Expr<'arena, 'src>>,
167    pub body: &'arena Stmt<'arena, 'src>,
168    #[serde(default, skip_serializing_if = "is_false")]
169    pub uses_alternative: bool,
170}
171
172#[derive(Debug, Serialize)]
173pub struct ForeachStmt<'arena, 'src> {
174    pub expr: Expr<'arena, 'src>,
175    pub key: Option<Expr<'arena, 'src>>,
176    pub value: Expr<'arena, 'src>,
177    pub body: &'arena Stmt<'arena, 'src>,
178    #[serde(default, skip_serializing_if = "is_false")]
179    pub uses_alternative: bool,
180}
181
182#[derive(Debug, Serialize)]
183pub struct DoWhileStmt<'arena, 'src> {
184    pub body: &'arena Stmt<'arena, 'src>,
185    pub condition: Expr<'arena, 'src>,
186}
187
188#[derive(Debug, Serialize)]
189pub struct SwitchBody<'arena, 'src> {
190    pub cases: ArenaVec<'arena, SwitchCase<'arena, 'src>>,
191    #[serde(skip)]
192    pub span: Span,
193}
194
195#[derive(Debug, Serialize)]
196pub struct SwitchStmt<'arena, 'src> {
197    pub expr: Expr<'arena, 'src>,
198    #[serde(flatten)]
199    pub body: SwitchBody<'arena, 'src>,
200    #[serde(default, skip_serializing_if = "is_false")]
201    pub uses_alternative: bool,
202}
203
204#[derive(Debug, Serialize)]
205pub struct SwitchCase<'arena, 'src> {
206    pub value: Option<Expr<'arena, 'src>>,
207    pub body: ArenaVec<'arena, Stmt<'arena, 'src>>,
208    pub span: Span,
209}
210
211#[derive(Debug, Serialize)]
212pub struct TryCatchStmt<'arena, 'src> {
213    pub body: &'arena Block<'arena, 'src>,
214    pub catches: ArenaVec<'arena, CatchClause<'arena, 'src>>,
215    pub finally: Option<&'arena Block<'arena, 'src>>,
216    /// Start byte offset of the `finally` keyword; `None` when there is no finally clause.
217    #[serde(skip)]
218    pub finally_kw_start: Option<u32>,
219}
220
221#[derive(Debug, Serialize)]
222pub struct CatchClause<'arena, 'src> {
223    pub types: ArenaVec<'arena, Name<'arena, 'src>>,
224    pub var: Option<&'src str>,
225    pub body: &'arena Block<'arena, 'src>,
226    pub span: Span,
227}
228
229#[derive(Debug, Serialize)]
230pub struct NamespaceDecl<'arena, 'src> {
231    pub name: Option<Name<'arena, 'src>>,
232    pub body: NamespaceBody<'arena, 'src>,
233}
234
235#[derive(Debug, Serialize)]
236pub enum NamespaceBody<'arena, 'src> {
237    /// `namespace Foo { … }` — braced form; the statements are scoped to this namespace.
238    Braced(&'arena Block<'arena, 'src>),
239    /// `namespace Foo;` — simple form; all subsequent statements until the next `namespace` or EOF are in scope.
240    Simple,
241}
242
243#[derive(Debug, Serialize)]
244pub struct DeclareStmt<'arena, 'src> {
245    pub directives: ArenaVec<'arena, (&'src str, Expr<'arena, 'src>)>,
246    pub body: Option<&'arena Stmt<'arena, 'src>>,
247    #[serde(default, skip_serializing_if = "is_false")]
248    pub uses_alternative: bool,
249}
250
251#[derive(Debug, Serialize)]
252pub struct UseDecl<'arena, 'src> {
253    pub kind: UseKind,
254    pub uses: ArenaVec<'arena, UseItem<'arena, 'src>>,
255}
256
257#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
258pub enum UseKind {
259    /// `use Foo\Bar` — imports a class, interface, trait, or enum.
260    Normal,
261    /// `use function Foo\bar` — imports a function.
262    Function,
263    /// `use const Foo\BAR` — imports a constant.
264    Const,
265}
266
267#[derive(Debug, Serialize)]
268pub struct UseItem<'arena, 'src> {
269    pub name: Name<'arena, 'src>,
270    pub alias: Option<&'src str>,
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub kind: Option<UseKind>,
273    pub span: Span,
274}
275
276#[derive(Debug, Serialize)]
277pub struct ConstItem<'arena, 'src> {
278    pub name: Ident<'src>,
279    pub value: Expr<'arena, 'src>,
280    pub attributes: ArenaVec<'arena, Attribute<'arena, 'src>>,
281    pub span: Span,
282    #[serde(skip_serializing_if = "Option::is_none")]
283    pub doc_comment: Option<Comment<'src>>,
284}
285
286#[derive(Debug, Serialize)]
287pub struct StaticVar<'arena, 'src> {
288    pub name: Ident<'src>,
289    pub default: Option<Expr<'arena, 'src>>,
290    pub span: Span,
291}