Skip to main content

boa_ast/statement/
mod.rs

1//! The [`Statement`] Parse Node, as defined by the [spec].
2//!
3//! ECMAScript [statements] are mainly composed of control flow operations, such as [`If`],
4//! [`WhileLoop`], and [`Break`]. However, it also contains statements such as [`VarDeclaration`],
5//! [`Block`] or [`Expression`] which are not strictly used for control flow.
6//!
7//! [spec]: https://tc39.es/ecma262/#prod-Statement
8//! [statements]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements
9
10mod block;
11mod r#if;
12mod labelled;
13mod r#return;
14mod switch;
15mod throw;
16mod r#try;
17mod with;
18
19pub mod iteration;
20
21pub use self::{
22    block::Block,
23    r#if::If,
24    iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
25    labelled::{Labelled, LabelledItem},
26    r#return::Return,
27    switch::{Case, Switch},
28    throw::Throw,
29    r#try::{Catch, ErrorHandler, Finally, Try},
30    with::With,
31};
32use core::ops::ControlFlow;
33
34use crate::visitor::{VisitWith, Visitor, VisitorMut};
35use boa_interner::{Interner, ToIndentedString, ToInternedString};
36
37use super::{declaration::VarDeclaration, expression::Expression};
38
39/// The `Statement` Parse Node.
40///
41/// See the [module level documentation][self] for more information.
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
44#[derive(Clone, Debug, PartialEq)]
45pub enum Statement {
46    /// See [`Block`].
47    Block(Block),
48
49    /// See [`VarDeclaration`]
50    Var(VarDeclaration),
51
52    /// An empty statement.
53    ///
54    /// Empty statements do nothing, just return undefined.
55    ///
56    /// More information:
57    ///  - [ECMAScript reference][spec]
58    ///  - [MDN documentation][mdn]
59    ///
60    /// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement
61    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty
62    Empty,
63
64    /// See [`Expression`].
65    Expression(Expression),
66
67    /// See [`If`].
68    If(If),
69
70    /// See [`DoWhileLoop`].
71    DoWhileLoop(DoWhileLoop),
72
73    /// See [`WhileLoop`].
74    WhileLoop(WhileLoop),
75
76    /// See [`ForLoop`].
77    ForLoop(ForLoop),
78
79    /// See [`ForInLoop`].
80    ForInLoop(ForInLoop),
81
82    /// See [`ForOfLoop`].
83    ForOfLoop(ForOfLoop),
84
85    /// See[`Switch`].
86    Switch(Switch),
87
88    /// See [`Continue`].
89    Continue(Continue),
90
91    /// See [`Break`].
92    Break(Break),
93
94    /// See [`Return`].
95    Return(Return),
96
97    /// See [`Labelled`].
98    Labelled(Labelled),
99
100    /// See [`Throw`].
101    Throw(Throw),
102
103    /// See [`Try`].
104    Try(Try),
105
106    /// See [`With`].
107    With(With),
108
109    /// A `debugger` statement.
110    ///
111    /// The debugger statement invokes any available debugging functionality.
112    ///
113    /// More information:
114    ///  - [ECMAScript reference][spec]
115    ///  - [MDN documentation][mdn]
116    ///
117    /// [spec]: https://tc39.es/ecma262/#sec-debugger-statement
118    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger
119    Debugger,
120}
121
122impl Statement {
123    /// Implements the display formatting with indentation.
124    ///
125    /// This will not prefix the value with any indentation. If you want to prefix this with proper
126    /// indents, use [`to_indented_string()`](Self::to_indented_string).
127    pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {
128        let mut s = match self {
129            Self::Block(block) => return block.to_indented_string(interner, indentation),
130            Self::Var(var) => var.to_interned_string(interner),
131            Self::Empty => return ";".to_owned(),
132            Self::Expression(expr) => expr.to_indented_string(interner, indentation),
133            Self::If(if_smt) => return if_smt.to_indented_string(interner, indentation),
134            Self::DoWhileLoop(do_while) => do_while.to_indented_string(interner, indentation),
135            Self::WhileLoop(while_loop) => {
136                return while_loop.to_indented_string(interner, indentation);
137            }
138            Self::ForLoop(for_loop) => return for_loop.to_indented_string(interner, indentation),
139            Self::ForInLoop(for_in) => return for_in.to_indented_string(interner, indentation),
140            Self::ForOfLoop(for_of) => return for_of.to_indented_string(interner, indentation),
141            Self::Switch(switch) => return switch.to_indented_string(interner, indentation),
142            Self::Continue(cont) => cont.to_interned_string(interner),
143            Self::Break(break_smt) => break_smt.to_interned_string(interner),
144            Self::Return(ret) => ret.to_interned_string(interner),
145            Self::Labelled(labelled) => return labelled.to_interned_string(interner),
146            Self::Throw(throw) => throw.to_interned_string(interner),
147            Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation),
148            Self::With(with) => return with.to_interned_string(interner),
149            Self::Debugger => "debugger".to_owned(),
150        };
151        s.push(';');
152        s
153    }
154
155    /// Abstract operation [`IsLabelledFunction`][spec].
156    ///
157    /// This recursively checks if this `Statement` is a labelled function, since adding
158    /// several labels in a function should not change the return value of the abstract operation:
159    ///
160    /// ```Javascript
161    /// l1: l2: l3: l4: function f(){ }
162    /// ```
163    ///
164    /// This should return `true` for that snippet.
165    ///
166    /// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction
167    #[inline]
168    #[must_use]
169    pub fn is_labelled_function(&self) -> bool {
170        match self {
171            Self::Labelled(stmt) => match stmt.item() {
172                LabelledItem::FunctionDeclaration(_) => true,
173                LabelledItem::Statement(stmt) => stmt.is_labelled_function(),
174            },
175            _ => false,
176        }
177    }
178}
179
180impl ToIndentedString for Statement {
181    /// Creates a string of the value of the node with the given indentation. For example, an
182    /// indent level of 2 would produce this:
183    ///
184    /// ```js
185    ///         function hello() {
186    ///             console.log("hello");
187    ///         }
188    ///         hello();
189    ///         a = 2;
190    /// ```
191    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
192        let mut buf = match *self {
193            Self::Block(_) => String::new(),
194            _ => "    ".repeat(indentation),
195        };
196
197        buf.push_str(&self.to_no_indent_string(interner, indentation));
198
199        buf
200    }
201}
202
203impl VisitWith for Statement {
204    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
205    where
206        V: Visitor<'a>,
207    {
208        match self {
209            Self::Block(b) => visitor.visit_block(b),
210            Self::Var(v) => visitor.visit_var_declaration(v),
211            Self::Empty | Self::Debugger => {
212                // do nothing; there is nothing to visit here
213                ControlFlow::Continue(())
214            }
215            Self::Expression(e) => visitor.visit_expression(e),
216            Self::If(i) => visitor.visit_if(i),
217            Self::DoWhileLoop(dw) => visitor.visit_do_while_loop(dw),
218            Self::WhileLoop(w) => visitor.visit_while_loop(w),
219            Self::ForLoop(f) => visitor.visit_for_loop(f),
220            Self::ForInLoop(fi) => visitor.visit_for_in_loop(fi),
221            Self::ForOfLoop(fo) => visitor.visit_for_of_loop(fo),
222            Self::Switch(s) => visitor.visit_switch(s),
223            Self::Continue(c) => visitor.visit_continue(c),
224            Self::Break(b) => visitor.visit_break(b),
225            Self::Return(r) => visitor.visit_return(r),
226            Self::Labelled(l) => visitor.visit_labelled(l),
227            Self::Throw(th) => visitor.visit_throw(th),
228            Self::Try(tr) => visitor.visit_try(tr),
229            Self::With(with) => visitor.visit_with(with),
230        }
231    }
232
233    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
234    where
235        V: VisitorMut<'a>,
236    {
237        match self {
238            Self::Block(b) => visitor.visit_block_mut(b),
239            Self::Var(v) => visitor.visit_var_declaration_mut(v),
240            Self::Empty | Self::Debugger => {
241                // do nothing; there is nothing to visit here
242                ControlFlow::Continue(())
243            }
244            Self::Expression(e) => visitor.visit_expression_mut(e),
245            Self::If(i) => visitor.visit_if_mut(i),
246            Self::DoWhileLoop(dw) => visitor.visit_do_while_loop_mut(dw),
247            Self::WhileLoop(w) => visitor.visit_while_loop_mut(w),
248            Self::ForLoop(f) => visitor.visit_for_loop_mut(f),
249            Self::ForInLoop(fi) => visitor.visit_for_in_loop_mut(fi),
250            Self::ForOfLoop(fo) => visitor.visit_for_of_loop_mut(fo),
251            Self::Switch(s) => visitor.visit_switch_mut(s),
252            Self::Continue(c) => visitor.visit_continue_mut(c),
253            Self::Break(b) => visitor.visit_break_mut(b),
254            Self::Return(r) => visitor.visit_return_mut(r),
255            Self::Labelled(l) => visitor.visit_labelled_mut(l),
256            Self::Throw(th) => visitor.visit_throw_mut(th),
257            Self::Try(tr) => visitor.visit_try_mut(tr),
258            Self::With(with) => visitor.visit_with_mut(with),
259        }
260    }
261}