Skip to main content

mago_syntax/ast/ast/
statement.rs

1use serde::Serialize;
2use strum::Display;
3
4use mago_span::HasSpan;
5use mago_span::Span;
6
7use crate::ast::ast::block::Block;
8use crate::ast::ast::class_like::Class;
9use crate::ast::ast::class_like::Enum;
10use crate::ast::ast::class_like::Interface;
11use crate::ast::ast::class_like::Trait;
12use crate::ast::ast::constant::Constant;
13use crate::ast::ast::control_flow::r#if::If;
14use crate::ast::ast::control_flow::switch::Switch;
15use crate::ast::ast::declare::Declare;
16use crate::ast::ast::echo::Echo;
17use crate::ast::ast::echo::EchoTag;
18use crate::ast::ast::expression::Expression;
19use crate::ast::ast::function_like::function::Function;
20use crate::ast::ast::global::Global;
21use crate::ast::ast::goto::Goto;
22use crate::ast::ast::goto::Label;
23use crate::ast::ast::halt_compiler::HaltCompiler;
24use crate::ast::ast::inline::Inline;
25use crate::ast::ast::r#loop::Break;
26use crate::ast::ast::r#loop::Continue;
27use crate::ast::ast::r#loop::do_while::DoWhile;
28use crate::ast::ast::r#loop::r#for::For;
29use crate::ast::ast::r#loop::foreach::Foreach;
30use crate::ast::ast::r#loop::r#while::While;
31use crate::ast::ast::namespace::Namespace;
32use crate::ast::ast::r#return::Return;
33use crate::ast::ast::r#static::Static;
34use crate::ast::ast::tag::ClosingTag;
35use crate::ast::ast::tag::OpeningTag;
36use crate::ast::ast::terminator::Terminator;
37use crate::ast::ast::r#try::Try;
38use crate::ast::ast::unset::Unset;
39use crate::ast::ast::r#use::Use;
40
41use super::DeclareBody;
42use super::ForBody;
43use super::ForeachBody;
44use super::IfBody;
45use super::NamespaceBody;
46use super::WhileBody;
47
48#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
49pub struct ExpressionStatement<'arena> {
50    pub expression: &'arena Expression<'arena>,
51    pub terminator: Terminator<'arena>,
52}
53
54/// Represents a PHP statement.
55#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord, Display)]
56#[serde(tag = "type", content = "value")]
57#[non_exhaustive]
58pub enum Statement<'arena> {
59    OpeningTag(OpeningTag<'arena>),
60    ClosingTag(ClosingTag),
61    Inline(Inline<'arena>),
62    Namespace(Namespace<'arena>),
63    Use(Use<'arena>),
64    Class(Class<'arena>),
65    Interface(Interface<'arena>),
66    Trait(Trait<'arena>),
67    Enum(Enum<'arena>),
68    Block(Block<'arena>),
69    Constant(Constant<'arena>),
70    Function(Function<'arena>),
71    Declare(Declare<'arena>),
72    Goto(Goto<'arena>),
73    Label(Label<'arena>),
74    Try(Try<'arena>),
75    Foreach(Foreach<'arena>),
76    For(For<'arena>),
77    While(While<'arena>),
78    DoWhile(DoWhile<'arena>),
79    Continue(Continue<'arena>),
80    Break(Break<'arena>),
81    Switch(Switch<'arena>),
82    If(If<'arena>),
83    Return(Return<'arena>),
84    Expression(ExpressionStatement<'arena>),
85    Echo(Echo<'arena>),
86    EchoTag(EchoTag<'arena>),
87    Global(Global<'arena>),
88    Static(Static<'arena>),
89    HaltCompiler(HaltCompiler<'arena>),
90    Unset(Unset<'arena>),
91    Noop(Span),
92}
93
94impl Statement<'_> {
95    #[inline]
96    #[must_use]
97    pub const fn is_closing_tag(&self) -> bool {
98        matches!(self, Statement::ClosingTag(_))
99    }
100
101    #[inline]
102    #[must_use]
103    pub fn terminates_scripting(&self) -> bool {
104        match self {
105            Statement::ClosingTag(_) => true,
106            Statement::Namespace(Namespace { body: NamespaceBody::Implicit(implicit), .. }) => {
107                implicit.statements.last().map_or(implicit.terminator.is_closing_tag(), Statement::terminates_scripting)
108            }
109            Statement::Use(r#use) => r#use.terminator.is_closing_tag(),
110            Statement::Goto(goto) => goto.terminator.is_closing_tag(),
111            Statement::Declare(Declare { body: DeclareBody::Statement(b), .. }) => b.terminates_scripting(),
112            Statement::Declare(Declare { body: DeclareBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
113            Statement::For(For { body: ForBody::Statement(b), .. }) => b.terminates_scripting(),
114            Statement::For(For { body: ForBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
115            Statement::Foreach(Foreach { body: ForeachBody::Statement(b), .. }) => b.terminates_scripting(),
116            Statement::Foreach(Foreach { body: ForeachBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
117            Statement::While(While { body: WhileBody::Statement(b), .. }) => b.terminates_scripting(),
118            Statement::While(While { body: WhileBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
119            Statement::DoWhile(do_while) => do_while.terminator.is_closing_tag(),
120            Statement::Continue(cont) => cont.terminator.is_closing_tag(),
121            Statement::Break(brk) => brk.terminator.is_closing_tag(),
122            Statement::If(If { body: IfBody::Statement(stmt), .. }) => match &stmt.else_clause {
123                Some(else_clause) => else_clause.statement.terminates_scripting(),
124                None => stmt
125                    .else_if_clauses
126                    .iter()
127                    .last()
128                    .map_or(stmt.statement.terminates_scripting(), |clause| clause.statement.terminates_scripting()),
129            },
130            Statement::If(If { body: IfBody::ColonDelimited(body), .. }) => body.terminator.is_closing_tag(),
131            Statement::Return(ret) => ret.terminator.is_closing_tag(),
132            Statement::Expression(expression_statement) => expression_statement.terminator.is_closing_tag(),
133            Statement::Echo(echo) => echo.terminator.is_closing_tag(),
134            Statement::EchoTag(echo) => echo.terminator.is_closing_tag(),
135            Statement::Global(global) => global.terminator.is_closing_tag(),
136            Statement::Static(r#static) => r#static.terminator.is_closing_tag(),
137            Statement::Unset(unset) => unset.terminator.is_closing_tag(),
138            Statement::HaltCompiler(_) => true,
139            _ => false,
140        }
141    }
142
143    #[inline]
144    #[must_use]
145    pub const fn is_loop(&self) -> bool {
146        matches!(self, Statement::For(_) | Statement::Foreach(_) | Statement::While(_) | Statement::DoWhile(_))
147    }
148
149    #[inline]
150    #[must_use]
151    pub const fn is_control_flow(&self) -> bool {
152        matches!(
153            self,
154            Statement::If(_)
155                | Statement::Switch(_)
156                | Statement::Try(_)
157                | Statement::Continue(_)
158                | Statement::Break(_)
159                | Statement::Goto(_)
160        )
161    }
162
163    #[inline]
164    #[must_use]
165    pub const fn is_declaration(&self) -> bool {
166        matches!(
167            self,
168            Statement::Declare(_)
169                | Statement::Namespace(_)
170                | Statement::Class(_)
171                | Statement::Interface(_)
172                | Statement::Trait(_)
173                | Statement::Enum(_)
174                | Statement::Constant(_)
175                | Statement::Function(_)
176        )
177    }
178
179    #[inline]
180    #[must_use]
181    pub const fn is_noop(&self) -> bool {
182        matches!(self, Statement::Noop(_))
183    }
184}
185
186impl HasSpan for ExpressionStatement<'_> {
187    fn span(&self) -> Span {
188        self.expression.span().join(self.terminator.span())
189    }
190}
191
192impl HasSpan for Statement<'_> {
193    fn span(&self) -> Span {
194        match self {
195            Statement::OpeningTag(statement) => statement.span(),
196            Statement::ClosingTag(statement) => statement.span(),
197            Statement::Inline(statement) => statement.span(),
198            Statement::Namespace(statement) => statement.span(),
199            Statement::Use(statement) => statement.span(),
200            Statement::Class(statement) => statement.span(),
201            Statement::Interface(statement) => statement.span(),
202            Statement::Trait(statement) => statement.span(),
203            Statement::Enum(statement) => statement.span(),
204            Statement::Block(statement) => statement.span(),
205            Statement::Constant(statement) => statement.span(),
206            Statement::Function(statement) => statement.span(),
207            Statement::Declare(statement) => statement.span(),
208            Statement::Goto(statement) => statement.span(),
209            Statement::Label(statement) => statement.span(),
210            Statement::Try(statement) => statement.span(),
211            Statement::Foreach(statement) => statement.span(),
212            Statement::For(statement) => statement.span(),
213            Statement::While(statement) => statement.span(),
214            Statement::DoWhile(statement) => statement.span(),
215            Statement::Continue(statement) => statement.span(),
216            Statement::Break(statement) => statement.span(),
217            Statement::Switch(statement) => statement.span(),
218            Statement::If(statement) => statement.span(),
219            Statement::Return(statement) => statement.span(),
220            Statement::Expression(statement) => statement.span(),
221            Statement::Echo(statement) => statement.span(),
222            Statement::EchoTag(statement) => statement.span(),
223            Statement::Global(statement) => statement.span(),
224            Statement::Static(statement) => statement.span(),
225            Statement::Unset(statement) => statement.span(),
226            Statement::HaltCompiler(statement) => statement.span(),
227            Statement::Noop(span) => *span,
228        }
229    }
230}