1use crate::{
2 ast::{
3 object_literal::{ObjectLiteral, ObjectLiteralMember},
4 FunctionArgument,
5 },
6 derive_ASTNode, ParseErrors, PropertyKey, PropertyReference, SpreadDestructuringField,
7 TSXToken,
8};
9use derive_partial_eq_extras::PartialEqExtras;
10use get_field_by_type::GetFieldByType;
11use iterator_endiate::EndiateIteratorExt;
12use source_map::Span;
13use tokenizer_lib::TokenReader;
14use visitable_derive::Visitable;
15
16use crate::{
17 ASTNode, ArrayDestructuringField, Expression, ObjectDestructuringField, ParseError,
18 ParseResult, WithComment,
19};
20
21use super::MultipleExpression;
22
23#[apply(derive_ASTNode)]
24#[derive(Debug, Clone, PartialEqExtras, Visitable, get_field_by_type::GetFieldByType)]
25#[get_field_by_type_target(Span)]
26#[partial_eq_ignore_types(Span)]
27pub enum VariableOrPropertyAccess {
28 Variable(String, Span),
29 PropertyAccess {
30 parent: Box<Expression>,
31 property: PropertyReference,
32 position: Span,
33 },
34 Index {
36 indexee: Box<Expression>,
37 indexer: Box<MultipleExpression>,
38 position: Span,
39 },
40}
41
42impl ASTNode for VariableOrPropertyAccess {
43 fn get_position(&self) -> Span {
44 *self.get()
45 }
46
47 fn from_reader(
48 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
49 state: &mut crate::ParsingState,
50 options: &crate::ParseOptions,
51 ) -> ParseResult<Self> {
52 Expression::from_reader(reader, state, options)?.try_into()
53 }
54
55 fn to_string_from_buffer<T: source_map::ToString>(
56 &self,
57 buf: &mut T,
58 options: &crate::ToStringOptions,
59 local: crate::LocalToStringInformation,
60 ) {
61 match self {
62 VariableOrPropertyAccess::Variable(name, ..) => {
63 buf.push_str(name);
64 }
65 VariableOrPropertyAccess::PropertyAccess { parent, property, .. } => {
66 if let Expression::NumberLiteral(..)
67 | Expression::ObjectLiteral(..)
68 | Expression::ArrowFunction(..) = parent.get_non_parenthesized()
69 {
70 buf.push('(');
71 parent.to_string_from_buffer(buf, options, local);
72 buf.push(')');
73 } else {
74 parent.to_string_from_buffer(buf, options, local);
75 }
76 buf.push('.');
77 if let PropertyReference::Standard { property, is_private } = property {
78 if *is_private {
79 buf.push('#');
80 }
81 buf.push_str(property);
82 } else if !options.expect_markers {
83 panic!("found marker");
84 }
85 }
86 VariableOrPropertyAccess::Index { indexee, indexer, .. } => {
87 indexee.to_string_from_buffer(buf, options, local);
88 buf.push('[');
89 indexer.to_string_from_buffer(buf, options, local);
90 buf.push(']');
91 }
92 }
93 }
94}
95
96impl VariableOrPropertyAccess {
97 pub(crate) fn from_reader_with_precedence(
98 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
99 state: &mut crate::ParsingState,
100 options: &crate::ParseOptions,
101 return_precedence: u8,
102 ) -> ParseResult<Self> {
103 Expression::from_reader_with_precedence(reader, state, options, return_precedence, None)?
104 .try_into()
105 }
106}
107
108impl TryFrom<Expression> for VariableOrPropertyAccess {
109 type Error = ParseError;
110
111 fn try_from(expression: Expression) -> Result<Self, Self::Error> {
112 match expression {
113 Expression::VariableReference(name, position) => Ok(Self::Variable(name, position)),
114 Expression::PropertyAccess { parent, position, property, is_optional } => {
115 if is_optional {
116 Err(ParseError::new(crate::ParseErrors::InvalidLHSAssignment, position))
118 } else {
119 Ok(Self::PropertyAccess { parent, position, property })
120 }
121 }
122 Expression::Index { indexer, position, indexee, is_optional: false } => {
123 Ok(Self::Index { indexer, position, indexee })
124 }
125 Expression::ParenthesizedExpression(inner, _) => {
127 if let MultipleExpression::Single(expression) = *inner {
128 TryFrom::try_from(expression)
129 } else {
130 Err(ParseError::new(
131 crate::ParseErrors::InvalidLHSAssignment,
132 inner.get_position(),
133 ))
134 }
135 }
136 expression => Err(ParseError::new(
137 crate::ParseErrors::InvalidLHSAssignment,
138 expression.get_position(),
139 )),
140 }
141 }
142}
143
144impl From<VariableOrPropertyAccess> for Expression {
145 fn from(this: VariableOrPropertyAccess) -> Self {
146 match this {
147 VariableOrPropertyAccess::Variable(variable, position) => {
148 Expression::VariableReference(variable, position)
149 }
150 VariableOrPropertyAccess::Index { indexee, indexer, position } => {
151 Expression::Index { indexee, indexer, position, is_optional: false }
152 }
153 VariableOrPropertyAccess::PropertyAccess { parent, position, property } => {
154 Expression::PropertyAccess { parent, position, property, is_optional: false }
155 }
156 }
157 }
158}
159
160impl VariableOrPropertyAccess {
161 #[must_use]
162 pub fn get_parent(&self) -> Option<&Expression> {
163 match self {
164 VariableOrPropertyAccess::Variable(..) => None,
165 VariableOrPropertyAccess::PropertyAccess { parent, .. }
166 | VariableOrPropertyAccess::Index { indexee: parent, .. } => Some(parent),
167 }
168 }
169
170 pub fn get_parent_mut(&mut self) -> Option<&mut Expression> {
171 match self {
172 VariableOrPropertyAccess::Variable(..) => None,
173 VariableOrPropertyAccess::PropertyAccess { parent, .. }
174 | VariableOrPropertyAccess::Index { indexee: parent, .. } => Some(parent),
175 }
176 }
177}
178
179#[apply(derive_ASTNode)]
183#[derive(PartialEqExtras, Debug, Clone, Visitable, derive_enum_from_into::EnumFrom)]
184#[partial_eq_ignore_types(Span)]
185pub enum LHSOfAssignment {
186 VariableOrPropertyAccess(VariableOrPropertyAccess),
187 ArrayDestructuring {
188 #[visit_skip_field]
189 members: Vec<WithComment<ArrayDestructuringField<LHSOfAssignment>>>,
190 spread: Option<SpreadDestructuringField<LHSOfAssignment>>,
191 position: Span,
192 },
193 ObjectDestructuring {
194 #[visit_skip_field]
195 members: Vec<WithComment<ObjectDestructuringField<LHSOfAssignment>>>,
196 spread: Option<SpreadDestructuringField<LHSOfAssignment>>,
197 position: Span,
198 },
199}
200
201impl ASTNode for LHSOfAssignment {
202 fn get_position(&self) -> Span {
203 match self {
204 LHSOfAssignment::ObjectDestructuring { position, .. }
205 | LHSOfAssignment::ArrayDestructuring { position, .. } => *position,
206 LHSOfAssignment::VariableOrPropertyAccess(var_prop_access) => {
207 var_prop_access.get_position()
208 }
209 }
210 }
211
212 fn from_reader(
213 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
214 state: &mut crate::ParsingState,
215 options: &crate::ParseOptions,
216 ) -> ParseResult<Self> {
217 Expression::from_reader(reader, state, options).and_then(TryInto::try_into)
218 }
219
220 fn to_string_from_buffer<T: source_map::ToString>(
221 &self,
222 buf: &mut T,
223 options: &crate::ToStringOptions,
224 local: crate::LocalToStringInformation,
225 ) {
226 match self {
227 LHSOfAssignment::ObjectDestructuring { members, spread, position: _ } => {
228 buf.push('{');
229 options.push_gap_optionally(buf);
230 for (at_end, member) in members.iter().endiate() {
231 member.to_string_from_buffer(buf, options, local);
232 if !at_end {
233 buf.push(',');
234 options.push_gap_optionally(buf);
235 }
236 }
237 if let Some(ref spread) = spread {
238 if !members.is_empty() {
239 buf.push(',');
240 options.push_gap_optionally(buf);
241 }
242 buf.push_str("...");
243 spread.0.to_string_from_buffer(buf, options, local);
244 }
245 options.push_gap_optionally(buf);
246 buf.push('}');
247 }
248 LHSOfAssignment::ArrayDestructuring { members, spread, position: _ } => {
249 buf.push('[');
250 for (at_end, member) in members.iter().endiate() {
251 member.to_string_from_buffer(buf, options, local);
252 if !at_end {
253 buf.push(',');
254 options.push_gap_optionally(buf);
255 }
256 }
257 if let Some(ref spread) = spread {
258 if !members.is_empty() {
259 buf.push(',');
260 options.push_gap_optionally(buf);
261 }
262 buf.push_str("...");
263 spread.0.to_string_from_buffer(buf, options, local);
264 }
265 buf.push(']');
266 }
267 LHSOfAssignment::VariableOrPropertyAccess(variable_or_property_access) => {
268 variable_or_property_access.to_string_from_buffer(buf, options, local);
269 }
270 }
271 }
272}
273
274impl TryFrom<Expression> for LHSOfAssignment {
275 type Error = ParseError;
276
277 fn try_from(value: Expression) -> Result<Self, Self::Error> {
278 match value {
279 Expression::ArrayLiteral(members, position) => {
280 let mut new_members = Vec::with_capacity(members.len());
281 let mut iter = members.into_iter();
282 for member in iter.by_ref() {
283 let new_member = match member.0 {
284 Some(FunctionArgument::Comment { content, is_multiline: _, position }) => {
285 WithComment::PrefixComment(
286 content,
287 ArrayDestructuringField::None,
288 position,
289 )
290 }
291 Some(FunctionArgument::Spread(expression, span)) => {
292 return if let Some(next) = iter.next() {
293 Err(ParseError::new(
294 ParseErrors::CannotHaveRegularMemberAfterSpread,
295 next.get_position(),
296 ))
297 } else {
298 let inner: LHSOfAssignment = expression.try_into()?;
299 Ok(Self::ArrayDestructuring {
300 members: new_members,
301 spread: Some(SpreadDestructuringField(Box::new(inner), span)),
302 position,
303 })
304 }
305 }
306 Some(FunctionArgument::Standard(expression)) => {
307 WithComment::None(match expression {
308 Expression::Assignment { lhs, rhs, position: _ } => {
309 ArrayDestructuringField::Name(lhs, (), Some(rhs))
310 }
311 expression => {
312 ArrayDestructuringField::Name(expression.try_into()?, (), None)
313 }
314 })
315 }
316 None => WithComment::None(ArrayDestructuringField::None),
317 };
318 new_members.push(new_member);
319 }
320 Ok(Self::ArrayDestructuring { members: new_members, spread: None, position })
321 }
322 Expression::ObjectLiteral(ObjectLiteral { members, position }) => {
323 let mut new_members = Vec::with_capacity(members.len());
324 let mut iter = members.into_iter();
325 for member in iter.by_ref() {
326 let new_member: ObjectDestructuringField<LHSOfAssignment> = match member {
327 ObjectLiteralMember::Spread(expression, span) => {
328 return if let Some(next) = iter.next() {
329 Err(ParseError::new(
330 ParseErrors::CannotHaveRegularMemberAfterSpread,
331 next.get_position(),
332 ))
333 } else {
334 let inner: LHSOfAssignment = expression.try_into()?;
335 Ok(Self::ObjectDestructuring {
336 members: new_members,
337 spread: Some(SpreadDestructuringField(Box::new(inner), span)),
338 position,
339 })
340 }
341 }
342 ObjectLiteralMember::Shorthand(name, pos) => {
343 ObjectDestructuringField::Name(
344 crate::VariableIdentifier::Standard(name, pos),
345 (),
346 None,
347 pos,
348 )
349 }
350 ObjectLiteralMember::Property { assignment, key, position, value } => {
351 if assignment {
352 if let PropertyKey::Identifier(name, pos, _) = key.get_ast() {
353 ObjectDestructuringField::Name(
354 crate::VariableIdentifier::Standard(name, pos),
355 (),
356 Some(Box::new(value)),
357 pos,
358 )
359 } else {
360 return Err(ParseError::new(
361 crate::ParseErrors::InvalidLHSAssignment,
362 position,
363 ));
364 }
365 } else {
366 let (name, default_value) =
367 if let Expression::Assignment { lhs, rhs, position: _ } = value
368 {
369 (lhs, Some(rhs))
370 } else {
371 (value.try_into()?, None)
372 };
373
374 ObjectDestructuringField::Map {
375 from: key.get_ast(),
376 annotation: (),
377 name: WithComment::None(name),
378 default_value,
379 position,
380 }
381 }
382 }
383 ObjectLiteralMember::Method(_) => {
384 return Err(ParseError::new(
385 crate::ParseErrors::InvalidLHSAssignment,
386 position,
387 ))
388 }
389 ObjectLiteralMember::Comment(..) => {
390 continue;
391 }
392 };
393 new_members.push(WithComment::None(new_member));
394 }
395 Ok(Self::ObjectDestructuring { members: new_members, spread: None, position })
396 }
397 expression => VariableOrPropertyAccess::try_from(expression)
398 .map(LHSOfAssignment::VariableOrPropertyAccess),
399 }
400 }
401}