Skip to main content

boa_ast/function/
arrow_function.rs

1use super::{FormalParameterList, FunctionBody};
2use crate::operations::{ContainsSymbol, contains};
3use crate::scope::FunctionScopes;
4use crate::visitor::{VisitWith, Visitor, VisitorMut};
5use crate::{LinearSpan, LinearSpanIgnoreEq, Span, Spanned};
6use crate::{
7    expression::{Expression, Identifier},
8    join_nodes,
9};
10use boa_interner::{Interner, ToIndentedString};
11use core::{fmt::Write as _, ops::ControlFlow};
12
13/// An arrow function expression, as defined by the [spec].
14///
15/// An [arrow function][mdn] expression is a syntactically compact alternative to a regular function
16/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as
17/// constructors. Arrow functions cannot be used as constructors and will throw an error when
18/// used with new.
19///
20/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
21/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24#[derive(Clone, Debug, PartialEq)]
25pub struct ArrowFunction {
26    pub(crate) name: Option<Identifier>,
27    pub(crate) parameters: FormalParameterList,
28    pub(crate) body: FunctionBody,
29    pub(crate) contains_direct_eval: bool,
30
31    #[cfg_attr(feature = "serde", serde(skip))]
32    pub(crate) scopes: FunctionScopes,
33    linear_span: LinearSpanIgnoreEq,
34    span: Span,
35}
36
37impl ArrowFunction {
38    /// Creates a new `ArrowFunctionDecl` AST Expression.
39    #[inline]
40    #[must_use]
41    pub fn new(
42        name: Option<Identifier>,
43        parameters: FormalParameterList,
44        body: FunctionBody,
45        linear_span: LinearSpan,
46        span: Span,
47    ) -> Self {
48        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
49            || contains(&body, ContainsSymbol::DirectEval);
50        Self {
51            name,
52            parameters,
53            body,
54            contains_direct_eval,
55            scopes: FunctionScopes::default(),
56            linear_span: linear_span.into(),
57            span,
58        }
59    }
60
61    /// Gets the name of the arrow function.
62    #[inline]
63    #[must_use]
64    pub const fn name(&self) -> Option<Identifier> {
65        self.name
66    }
67
68    /// Sets the name of the arrow function.
69    #[inline]
70    pub fn set_name(&mut self, name: Option<Identifier>) {
71        self.name = name;
72    }
73
74    /// Gets the list of parameters of the arrow function.
75    #[inline]
76    #[must_use]
77    pub const fn parameters(&self) -> &FormalParameterList {
78        &self.parameters
79    }
80
81    /// Gets the body of the arrow function.
82    #[inline]
83    #[must_use]
84    pub const fn body(&self) -> &FunctionBody {
85        &self.body
86    }
87
88    /// Returns the scopes of the arrow function.
89    #[inline]
90    #[must_use]
91    pub const fn scopes(&self) -> &FunctionScopes {
92        &self.scopes
93    }
94
95    /// Gets linear span of the function declaration.
96    #[inline]
97    #[must_use]
98    pub const fn linear_span(&self) -> LinearSpan {
99        self.linear_span.0
100    }
101
102    /// Returns `true` if the arrow function contains a direct call to `eval`.
103    #[inline]
104    #[must_use]
105    pub const fn contains_direct_eval(&self) -> bool {
106        self.contains_direct_eval
107    }
108}
109
110impl Spanned for ArrowFunction {
111    #[inline]
112    fn span(&self) -> Span {
113        self.span
114    }
115}
116
117impl ToIndentedString for ArrowFunction {
118    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
119        let mut buf = format!("({}", join_nodes(interner, self.parameters.as_ref()));
120        if self.body().statements().is_empty() {
121            buf.push_str(") => {}");
122        } else {
123            let _ = write!(
124                buf,
125                ") => {{\n{}{}}}",
126                self.body.to_indented_string(interner, indentation + 1),
127                "    ".repeat(indentation)
128            );
129        }
130        buf
131    }
132}
133
134impl From<ArrowFunction> for Expression {
135    fn from(decl: ArrowFunction) -> Self {
136        Self::ArrowFunction(decl)
137    }
138}
139
140impl VisitWith for ArrowFunction {
141    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
142    where
143        V: Visitor<'a>,
144    {
145        if let Some(ident) = &self.name {
146            visitor.visit_identifier(ident)?;
147        }
148        visitor.visit_formal_parameter_list(&self.parameters)?;
149        visitor.visit_function_body(&self.body)
150    }
151
152    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
153    where
154        V: VisitorMut<'a>,
155    {
156        if let Some(ident) = &mut self.name {
157            visitor.visit_identifier_mut(ident)?;
158        }
159        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
160        visitor.visit_function_body_mut(&mut self.body)
161    }
162}