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#[repr(u8)]
58#[non_exhaustive]
59pub enum Statement<'arena> {
60    OpeningTag(OpeningTag<'arena>),
61    ClosingTag(ClosingTag),
62    Inline(Inline<'arena>),
63    Namespace(Namespace<'arena>),
64    Use(Use<'arena>),
65    Class(Class<'arena>),
66    Interface(Interface<'arena>),
67    Trait(Trait<'arena>),
68    Enum(Enum<'arena>),
69    Block(Block<'arena>),
70    Constant(Constant<'arena>),
71    Function(Function<'arena>),
72    Declare(Declare<'arena>),
73    Goto(Goto<'arena>),
74    Label(Label<'arena>),
75    Try(Try<'arena>),
76    Foreach(Foreach<'arena>),
77    For(For<'arena>),
78    While(While<'arena>),
79    DoWhile(DoWhile<'arena>),
80    Continue(Continue<'arena>),
81    Break(Break<'arena>),
82    Switch(Switch<'arena>),
83    If(If<'arena>),
84    Return(Return<'arena>),
85    Expression(ExpressionStatement<'arena>),
86    Echo(Echo<'arena>),
87    EchoTag(EchoTag<'arena>),
88    Global(Global<'arena>),
89    Static(Static<'arena>),
90    HaltCompiler(HaltCompiler<'arena>),
91    Unset(Unset<'arena>),
92    Noop(Span),
93}
94
95impl Statement<'_> {
96    #[inline]
97    #[must_use]
98    pub const fn is_closing_tag(&self) -> bool {
99        matches!(self, Statement::ClosingTag(_))
100    }
101
102    #[inline]
103    #[must_use]
104    pub fn terminates_scripting(&self) -> bool {
105        match self {
106            Statement::ClosingTag(_) => true,
107            Statement::Namespace(Namespace { body: NamespaceBody::Implicit(implicit), .. }) => {
108                implicit.statements.last().map_or(implicit.terminator.is_closing_tag(), Statement::terminates_scripting)
109            }
110            Statement::Use(r#use) => r#use.terminator.is_closing_tag(),
111            Statement::Goto(goto) => goto.terminator.is_closing_tag(),
112            Statement::Declare(Declare { body: DeclareBody::Statement(b), .. }) => b.terminates_scripting(),
113            Statement::Declare(Declare { body: DeclareBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
114            Statement::For(For { body: ForBody::Statement(b), .. }) => b.terminates_scripting(),
115            Statement::For(For { body: ForBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
116            Statement::Foreach(Foreach { body: ForeachBody::Statement(b), .. }) => b.terminates_scripting(),
117            Statement::Foreach(Foreach { body: ForeachBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
118            Statement::While(While { body: WhileBody::Statement(b), .. }) => b.terminates_scripting(),
119            Statement::While(While { body: WhileBody::ColonDelimited(b), .. }) => b.terminator.is_closing_tag(),
120            Statement::DoWhile(do_while) => do_while.terminator.is_closing_tag(),
121            Statement::Continue(cont) => cont.terminator.is_closing_tag(),
122            Statement::Break(brk) => brk.terminator.is_closing_tag(),
123            Statement::If(If { body: IfBody::Statement(stmt), .. }) => match &stmt.else_clause {
124                Some(else_clause) => else_clause.statement.terminates_scripting(),
125                None => stmt
126                    .else_if_clauses
127                    .iter()
128                    .last()
129                    .map_or(stmt.statement.terminates_scripting(), |clause| clause.statement.terminates_scripting()),
130            },
131            Statement::If(If { body: IfBody::ColonDelimited(body), .. }) => body.terminator.is_closing_tag(),
132            Statement::Return(ret) => ret.terminator.is_closing_tag(),
133            Statement::Expression(expression_statement) => expression_statement.terminator.is_closing_tag(),
134            Statement::Echo(echo) => echo.terminator.is_closing_tag(),
135            Statement::EchoTag(echo) => echo.terminator.is_closing_tag(),
136            Statement::Global(global) => global.terminator.is_closing_tag(),
137            Statement::Static(r#static) => r#static.terminator.is_closing_tag(),
138            Statement::Unset(unset) => unset.terminator.is_closing_tag(),
139            Statement::HaltCompiler(_) => true,
140            _ => false,
141        }
142    }
143
144    #[inline]
145    #[must_use]
146    pub const fn is_loop(&self) -> bool {
147        matches!(self, Statement::For(_) | Statement::Foreach(_) | Statement::While(_) | Statement::DoWhile(_))
148    }
149
150    #[inline]
151    #[must_use]
152    pub const fn is_control_flow(&self) -> bool {
153        matches!(
154            self,
155            Statement::If(_)
156                | Statement::Switch(_)
157                | Statement::Try(_)
158                | Statement::Continue(_)
159                | Statement::Break(_)
160                | Statement::Goto(_)
161        )
162    }
163
164    #[inline]
165    #[must_use]
166    pub const fn is_declaration(&self) -> bool {
167        matches!(
168            self,
169            Statement::Declare(_)
170                | Statement::Namespace(_)
171                | Statement::Class(_)
172                | Statement::Interface(_)
173                | Statement::Trait(_)
174                | Statement::Enum(_)
175                | Statement::Constant(_)
176                | Statement::Function(_)
177        )
178    }
179
180    #[inline]
181    #[must_use]
182    pub const fn is_noop(&self) -> bool {
183        matches!(self, Statement::Noop(_))
184    }
185}
186
187impl HasSpan for ExpressionStatement<'_> {
188    fn span(&self) -> Span {
189        self.expression.span().join(self.terminator.span())
190    }
191}
192
193impl HasSpan for Statement<'_> {
194    fn span(&self) -> Span {
195        match self {
196            Statement::OpeningTag(statement) => statement.span(),
197            Statement::ClosingTag(statement) => statement.span(),
198            Statement::Inline(statement) => statement.span(),
199            Statement::Namespace(statement) => statement.span(),
200            Statement::Use(statement) => statement.span(),
201            Statement::Class(statement) => statement.span(),
202            Statement::Interface(statement) => statement.span(),
203            Statement::Trait(statement) => statement.span(),
204            Statement::Enum(statement) => statement.span(),
205            Statement::Block(statement) => statement.span(),
206            Statement::Constant(statement) => statement.span(),
207            Statement::Function(statement) => statement.span(),
208            Statement::Declare(statement) => statement.span(),
209            Statement::Goto(statement) => statement.span(),
210            Statement::Label(statement) => statement.span(),
211            Statement::Try(statement) => statement.span(),
212            Statement::Foreach(statement) => statement.span(),
213            Statement::For(statement) => statement.span(),
214            Statement::While(statement) => statement.span(),
215            Statement::DoWhile(statement) => statement.span(),
216            Statement::Continue(statement) => statement.span(),
217            Statement::Break(statement) => statement.span(),
218            Statement::Switch(statement) => statement.span(),
219            Statement::If(statement) => statement.span(),
220            Statement::Return(statement) => statement.span(),
221            Statement::Expression(statement) => statement.span(),
222            Statement::Echo(statement) => statement.span(),
223            Statement::EchoTag(statement) => statement.span(),
224            Statement::Global(statement) => statement.span(),
225            Statement::Static(statement) => statement.span(),
226            Statement::Unset(statement) => statement.span(),
227            Statement::HaltCompiler(statement) => statement.span(),
228            Statement::Noop(span) => *span,
229        }
230    }
231}