boa_ast/statement/
if.rs

1//! If statement
2
3use crate::{
4    expression::Expression,
5    statement::Statement,
6    visitor::{VisitWith, Visitor, VisitorMut},
7};
8use boa_interner::{Interner, ToIndentedString, ToInternedString};
9use core::{fmt::Write as _, ops::ControlFlow};
10
11/// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If
12/// the condition is [`falsy`][falsy], another statement can be executed.
13///
14/// Multiple `if...else` statements can be nested to create an else if clause.
15///
16/// Note that there is no elseif (in one word) keyword in JavaScript.
17///
18/// More information:
19///  - [ECMAScript reference][spec]
20///  - [MDN documentation][mdn]
21///
22/// [spec]: https://tc39.es/ecma262/#prod-IfStatement
23/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else
24/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/truthy
25/// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy
26/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29#[derive(Clone, Debug, PartialEq)]
30pub struct If {
31    condition: Expression,
32    body: Box<Statement>,
33    else_node: Option<Box<Statement>>,
34}
35
36impl If {
37    /// Gets the condition of the if statement.
38    #[inline]
39    #[must_use]
40    pub const fn cond(&self) -> &Expression {
41        &self.condition
42    }
43
44    /// Gets the body to execute if the condition is true.
45    #[inline]
46    #[must_use]
47    pub const fn body(&self) -> &Statement {
48        &self.body
49    }
50
51    /// Gets the `else` node, if it has one.
52    #[inline]
53    pub fn else_node(&self) -> Option<&Statement> {
54        self.else_node.as_ref().map(Box::as_ref)
55    }
56
57    /// Creates an `If` AST node.
58    #[inline]
59    #[must_use]
60    pub fn new(condition: Expression, body: Statement, else_node: Option<Statement>) -> Self {
61        Self {
62            condition,
63            body: body.into(),
64            else_node: else_node.map(Box::new),
65        }
66    }
67}
68
69impl ToIndentedString for If {
70    fn to_indented_string(&self, interner: &Interner, indent: usize) -> String {
71        let mut buf = format!("if ({}) ", self.cond().to_interned_string(interner));
72        match self.else_node() {
73            Some(else_e) => {
74                let _ = write!(
75                    buf,
76                    "{} else {}",
77                    self.body().to_indented_string(interner, indent),
78                    else_e.to_indented_string(interner, indent)
79                );
80            }
81            None => {
82                buf.push_str(&self.body().to_indented_string(interner, indent));
83            }
84        }
85        buf
86    }
87}
88
89impl From<If> for Statement {
90    fn from(if_stm: If) -> Self {
91        Self::If(if_stm)
92    }
93}
94
95impl VisitWith for If {
96    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
97    where
98        V: Visitor<'a>,
99    {
100        visitor.visit_expression(&self.condition)?;
101        visitor.visit_statement(&self.body)?;
102        if let Some(stmt) = &self.else_node {
103            visitor.visit_statement(stmt)?;
104        }
105        ControlFlow::Continue(())
106    }
107
108    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
109    where
110        V: VisitorMut<'a>,
111    {
112        visitor.visit_expression_mut(&mut self.condition)?;
113        visitor.visit_statement_mut(&mut self.body)?;
114        if let Some(stmt) = &mut self.else_node {
115            visitor.visit_statement_mut(stmt)?;
116        }
117        ControlFlow::Continue(())
118    }
119}