Skip to main content

boa_ast/expression/operator/assign/
mod.rs

1#![allow(clippy::doc_link_with_quotes)]
2
3//! Assignment expression nodes, as defined by the [spec].
4//!
5//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right
6//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple
7//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=`
8//! only allow ["simple"][simple] left hand side expressions as an assignment target.
9//!
10//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
11//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators
12//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression
13//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype
14
15mod op;
16
17use core::ops::ControlFlow;
18pub use op::*;
19
20use boa_interner::{Interner, Sym, ToInternedString};
21
22use crate::{
23    Span, Spanned,
24    expression::{Expression, access::PropertyAccess, identifier::Identifier},
25    pattern::Pattern,
26    visitor::{VisitWith, Visitor, VisitorMut},
27};
28
29/// An assignment operator expression.
30///
31/// See the [module level documentation][self] for more information.
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
34#[derive(Clone, Debug, PartialEq)]
35pub struct Assign {
36    op: AssignOp,
37    lhs: Box<AssignTarget>,
38    rhs: Box<Expression>,
39}
40
41impl Assign {
42    /// Creates an `Assign` AST Expression.
43    #[inline]
44    #[must_use]
45    pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self {
46        Self {
47            op,
48            lhs: Box::new(lhs),
49            rhs: Box::new(rhs),
50        }
51    }
52
53    /// Gets the operator of the assignment operation.
54    #[inline]
55    #[must_use]
56    pub const fn op(&self) -> AssignOp {
57        self.op
58    }
59
60    /// Gets the left hand side of the assignment operation.
61    #[inline]
62    #[must_use]
63    pub const fn lhs(&self) -> &AssignTarget {
64        &self.lhs
65    }
66
67    /// Gets the right hand side of the assignment operation.
68    #[inline]
69    #[must_use]
70    pub const fn rhs(&self) -> &Expression {
71        &self.rhs
72    }
73}
74
75impl Spanned for Assign {
76    #[inline]
77    fn span(&self) -> Span {
78        Span::new(self.lhs.span().start(), self.rhs.span().end())
79    }
80}
81
82impl ToInternedString for Assign {
83    #[inline]
84    fn to_interned_string(&self, interner: &Interner) -> String {
85        format!(
86            "{} {} {}",
87            self.lhs.to_interned_string(interner),
88            self.op,
89            self.rhs.to_interned_string(interner)
90        )
91    }
92}
93
94impl From<Assign> for Expression {
95    #[inline]
96    fn from(op: Assign) -> Self {
97        Self::Assign(op)
98    }
99}
100
101impl VisitWith for Assign {
102    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
103    where
104        V: Visitor<'a>,
105    {
106        visitor.visit_assign_target(&self.lhs)?;
107        visitor.visit_expression(&self.rhs)
108    }
109
110    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
111    where
112        V: VisitorMut<'a>,
113    {
114        visitor.visit_assign_target_mut(&mut self.lhs)?;
115        visitor.visit_expression_mut(&mut self.rhs)
116    }
117}
118
119/// The valid left-hand-side expressions of an assignment operator. Also called
120/// [`LeftHandSideExpression`][spec] in the spec.
121///
122/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
125#[derive(Clone, Debug, PartialEq)]
126pub enum AssignTarget {
127    /// A simple identifier, such as `a`.
128    Identifier(Identifier),
129    /// A property access, such as `a.prop`.
130    Access(PropertyAccess),
131    /// A pattern assignment, such as `{a, b, ...c}`.
132    Pattern(Pattern),
133}
134
135impl AssignTarget {
136    /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].
137    /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.
138    #[must_use]
139    pub fn from_expression(expression: &Expression, strict: bool) -> Option<Self> {
140        match expression {
141            Expression::ObjectLiteral(object) => {
142                let pattern = object.to_pattern(strict)?;
143                Some(Self::Pattern(pattern.into()))
144            }
145            Expression::ArrayLiteral(array) => {
146                let pattern = array.to_pattern(strict)?;
147                Some(Self::Pattern(pattern.into()))
148            }
149            e => Self::from_expression_simple(e, strict),
150        }
151    }
152
153    /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].
154    /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.
155    ///
156    /// The `AssignmentTargetType` of the expression must be `simple`.
157    #[must_use]
158    pub fn from_expression_simple(expression: &Expression, strict: bool) -> Option<Self> {
159        match expression {
160            Expression::Identifier(id)
161                if strict && (id.sym() == Sym::EVAL || id.sym() == Sym::ARGUMENTS) =>
162            {
163                None
164            }
165            Expression::Identifier(id) => Some(Self::Identifier(*id)),
166            Expression::PropertyAccess(access) => Some(Self::Access(access.clone())),
167            Expression::Parenthesized(p) => Self::from_expression_simple(p.expression(), strict),
168            _ => None,
169        }
170    }
171}
172
173impl Spanned for AssignTarget {
174    #[inline]
175    fn span(&self) -> Span {
176        match self {
177            AssignTarget::Identifier(identifier) => identifier.span(),
178            AssignTarget::Access(property_access) => property_access.span(),
179            AssignTarget::Pattern(pattern) => pattern.span(),
180        }
181    }
182}
183
184impl ToInternedString for AssignTarget {
185    #[inline]
186    fn to_interned_string(&self, interner: &Interner) -> String {
187        match self {
188            Self::Identifier(id) => id.to_interned_string(interner),
189            Self::Access(access) => access.to_interned_string(interner),
190            Self::Pattern(pattern) => pattern.to_interned_string(interner),
191        }
192    }
193}
194
195impl From<Identifier> for AssignTarget {
196    #[inline]
197    fn from(target: Identifier) -> Self {
198        Self::Identifier(target)
199    }
200}
201
202impl VisitWith for AssignTarget {
203    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
204    where
205        V: Visitor<'a>,
206    {
207        match self {
208            Self::Identifier(id) => visitor.visit_identifier(id),
209            Self::Access(pa) => visitor.visit_property_access(pa),
210            Self::Pattern(pat) => visitor.visit_pattern(pat),
211        }
212    }
213
214    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
215    where
216        V: VisitorMut<'a>,
217    {
218        match self {
219            Self::Identifier(id) => visitor.visit_identifier_mut(id),
220            Self::Access(pa) => visitor.visit_property_access_mut(pa),
221            Self::Pattern(pat) => visitor.visit_pattern_mut(pat),
222        }
223    }
224}