1use std::fmt::Debug;
5
6use crate::{
7 derive_ASTNode,
8 errors::parse_lexing_error,
9 parse_bracketed,
10 property_key::PropertyKey,
11 throw_unexpected_token_with_token,
12 tokens::token_as_identifier,
13 visiting::{ImmutableVariableOrProperty, MutableVariableOrProperty},
14 ASTNode, Expression, ListItem, Marker, ParseError, ParseErrors, ParseOptions, ParseResult,
15 Span, TSXToken, Token, VisitOptions, Visitable, WithComment,
16};
17
18use derive_partial_eq_extras::PartialEqExtras;
19use get_field_by_type::GetFieldByType;
20use iterator_endiate::EndiateIteratorExt;
21use tokenizer_lib::TokenReader;
22
23#[apply(derive_ASTNode)]
24#[derive(Debug, PartialEqExtras, Clone, GetFieldByType)]
25#[partial_eq_ignore_types(Span)]
26#[get_field_by_type_target(Span)]
27pub enum VariableIdentifier {
28 Standard(String, Span),
29 #[cfg_attr(feature = "self-rust-tokenize", self_tokenize_field(0))]
31 Marker(
32 #[cfg_attr(target_family = "wasm", tsify(type = "VariableIdentifier"))] Marker<Self>,
33 Span,
34 ),
35}
36
37impl ASTNode for VariableIdentifier {
38 fn from_reader(
39 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
40 state: &mut crate::ParsingState,
41 options: &ParseOptions,
42 ) -> ParseResult<Self> {
43 let (ident, span) = token_as_identifier(reader.next().unwrap(), "variable identifier")?;
44 if ident == "let" {
45 return Err(ParseError::new(ParseErrors::ReservedIdentifier, span));
46 }
47 Ok(if options.interpolation_points && ident == crate::marker::MARKER {
48 Self::Marker(state.new_partial_point_marker(span.get_start()), span)
49 } else {
50 Self::Standard(ident, span)
51 })
52 }
53
54 fn to_string_from_buffer<T: source_map::ToString>(
55 &self,
56 buf: &mut T,
57 options: &crate::ToStringOptions,
58 _local: crate::LocalToStringInformation,
59 ) {
60 match self {
61 VariableIdentifier::Standard(name, _) => buf.push_str(name),
62 VariableIdentifier::Marker(_, _) => {
63 assert!(!options.expect_markers, "variable marker attempted to convert to string");
64 }
65 }
66 }
67
68 fn get_position(&self) -> Span {
69 *self.get()
70 }
71}
72
73impl VariableIdentifier {
74 #[must_use]
75 pub fn as_option_str(&self) -> Option<&str> {
76 match self {
77 VariableIdentifier::Standard(s, _) => Some(s.as_str()),
78 VariableIdentifier::Marker(_, _) => None,
79 }
80 }
81}
82
83#[derive(Debug, Clone, PartialEq)]
86#[apply(derive_ASTNode)]
87pub enum VariableField {
88 Name(VariableIdentifier),
90 Array {
92 members: Vec<WithComment<ArrayDestructuringField<VariableField>>>,
93 spread: Option<SpreadDestructuringField<VariableField>>,
94 position: Span,
95 },
96 Object {
98 members: Vec<WithComment<ObjectDestructuringField<VariableField>>>,
99 spread: Option<SpreadDestructuringField<VariableField>>,
100 position: Span,
101 },
102}
103
104impl ASTNode for VariableField {
105 fn from_reader(
106 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
107 state: &mut crate::ParsingState,
108 options: &ParseOptions,
109 ) -> ParseResult<Self> {
110 match reader.peek().ok_or_else(parse_lexing_error)?.0 {
111 TSXToken::OpenBrace => {
112 let Token(_, start_pos) = reader.next().unwrap();
113 let (members, spread, last_pos) =
114 parse_bracketed(reader, state, options, None, TSXToken::CloseBrace)?;
115 Ok(Self::Object { members, spread, position: start_pos.union(last_pos) })
116 }
117 TSXToken::OpenBracket => {
118 let Token(_, start_pos) = reader.next().unwrap();
119 let (members, spread, end) =
120 parse_bracketed(reader, state, options, None, TSXToken::CloseBracket)?;
121 Ok(Self::Array { members, spread, position: start_pos.union(end) })
122 }
123 _ => Ok(Self::Name(VariableIdentifier::from_reader(reader, state, options)?)),
124 }
125 }
126
127 fn to_string_from_buffer<T: source_map::ToString>(
128 &self,
129 buf: &mut T,
130 options: &crate::ToStringOptions,
131 local: crate::LocalToStringInformation,
132 ) {
133 match self {
134 Self::Name(identifier) => {
135 buf.add_mapping(&identifier.get_position().with_source(local.under));
136 identifier.to_string_from_buffer(buf, options, local);
137 }
138 Self::Array { members, spread, position: _ } => {
139 buf.push('[');
140 for (at_end, member) in members.iter().endiate() {
141 member.to_string_from_buffer(buf, options, local);
142 if !at_end {
143 buf.push(',');
144 options.push_gap_optionally(buf);
145 }
146 }
147 if let Some(ref spread) = spread {
148 if !members.is_empty() {
149 buf.push(',');
150 options.push_gap_optionally(buf);
151 }
152 buf.push_str("...");
153 spread.0.to_string_from_buffer(buf, options, local);
154 }
155 buf.push(']');
156 }
157 Self::Object { members, spread, position: _ } => {
158 buf.push('{');
159 options.push_gap_optionally(buf);
160 for (at_end, member) in members.iter().endiate() {
161 member.to_string_from_buffer(buf, options, local);
162 if !at_end {
163 buf.push(',');
164 options.push_gap_optionally(buf);
165 }
166 }
167 if let Some(ref spread) = spread {
168 if !members.is_empty() {
169 buf.push(',');
170 options.push_gap_optionally(buf);
171 }
172 buf.push_str("...");
173 spread.0.to_string_from_buffer(buf, options, local);
174 }
175 options.push_gap_optionally(buf);
176 buf.push('}');
177 }
178 }
179 }
180
181 fn get_position(&self) -> Span {
182 match self {
183 VariableField::Array { position, .. } | VariableField::Object { position, .. } => {
184 *position
185 }
186 VariableField::Name(id) => id.get_position(),
187 }
188 }
189}
190
191pub trait DestructuringFieldInto: ASTNode {
192 type TypeAnnotation: Clone + PartialEq + Debug + Sync + Send + 'static;
194
195 fn type_annotation_from_reader(
196 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
197 state: &mut crate::ParsingState,
198 options: &ParseOptions,
199 ) -> ParseResult<Self::TypeAnnotation>;
200}
201
202impl DestructuringFieldInto for VariableField {
203 type TypeAnnotation = Option<crate::TypeAnnotation>;
204
205 fn type_annotation_from_reader(
206 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
207 state: &mut crate::ParsingState,
208 options: &ParseOptions,
209 ) -> ParseResult<Self::TypeAnnotation> {
210 if let (true, Some(Token(TSXToken::Colon, _))) =
211 (options.destructuring_type_annotation, reader.peek())
212 {
213 reader.next();
214 crate::TypeAnnotation::from_reader(reader, state, options).map(Some)
215 } else {
216 Ok(None)
217 }
218 }
219}
220
221impl DestructuringFieldInto for crate::ast::LHSOfAssignment {
222 type TypeAnnotation = ();
223
224 fn type_annotation_from_reader(
225 _reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
226 _state: &mut crate::ParsingState,
227 _options: &ParseOptions,
228 ) -> ParseResult<Self::TypeAnnotation> {
229 Ok(())
230 }
231}
232
233#[derive(Debug, Clone, PartialEq, Eq)]
237#[apply(derive_ASTNode)]
238pub enum ArrayDestructuringField<T: DestructuringFieldInto> {
239 Name(T, T::TypeAnnotation, Option<Box<Expression>>),
240 Comment { content: String, is_multiline: bool, position: Span },
241 None,
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, visitable_derive::Visitable)]
246#[apply(derive_ASTNode)]
247pub struct SpreadDestructuringField<T: DestructuringFieldInto>(pub Box<T>, pub Span);
248
249impl<T: DestructuringFieldInto> ListItem for WithComment<ArrayDestructuringField<T>> {
250 const EMPTY: Option<Self> = Some(WithComment::None(ArrayDestructuringField::None));
251
252 const LAST_PREFIX: Option<TSXToken> = Some(TSXToken::Spread);
253
254 type LAST = SpreadDestructuringField<T>;
255
256 fn parse_last_item(
257 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
258 state: &mut crate::ParsingState,
259 options: &ParseOptions,
260 ) -> ParseResult<Self::LAST> {
261 let start = reader.expect_next(TSXToken::Spread)?;
262 let node = T::from_reader(reader, state, options)?;
263 let position = start.union(node.get_position());
264 Ok(SpreadDestructuringField(Box::new(node), position))
265 }
266}
267
268impl<T: DestructuringFieldInto> ASTNode for ArrayDestructuringField<T> {
269 fn from_reader(
270 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
271 state: &mut crate::ParsingState,
272 options: &ParseOptions,
273 ) -> ParseResult<Self> {
274 let Token(token, _start) = reader.peek().ok_or_else(parse_lexing_error)?;
275 if matches!(token, TSXToken::Comma | TSXToken::CloseBracket) {
276 Ok(Self::None)
277 } else {
278 let name = T::from_reader(reader, state, options)?;
279 let annotation = T::type_annotation_from_reader(reader, state, options)?;
280 let default_value = reader
281 .conditional_next(|t| matches!(t, TSXToken::Assign))
282 .is_some()
283 .then(|| ASTNode::from_reader(reader, state, options).map(Box::new))
284 .transpose()?;
285
286 Ok(Self::Name(name, annotation, default_value))
293 }
294 }
295
296 fn to_string_from_buffer<U: source_map::ToString>(
297 &self,
298 buf: &mut U,
299 options: &crate::ToStringOptions,
300 local: crate::LocalToStringInformation,
301 ) {
302 match self {
303 Self::Name(name, _annotation, default_value) => {
304 name.to_string_from_buffer(buf, options, local);
305 if let Some(default_value) = default_value {
306 options.push_gap_optionally(buf);
307 buf.push('=');
308 options.push_gap_optionally(buf);
309 default_value.to_string_from_buffer(buf, options, local);
310 }
311 }
312 Self::Comment { content, is_multiline: _is_multiline, position: _ } => {
313 if options.should_add_comment(content) {
314 buf.push_str("/*");
315 buf.push_str(content);
316 buf.push_str("*/");
317 }
318 }
319 Self::None => {}
320 }
321 }
322
323 fn get_position(&self) -> Span {
324 match self {
325 ArrayDestructuringField::Comment { position, .. } => *position,
326 ArrayDestructuringField::Name(vf, ..) => vf.get_position(),
328 ArrayDestructuringField::None => source_map::Nullable::NULL,
329 }
330 }
331}
332
333#[apply(derive_ASTNode)]
337#[derive(Debug, Clone, PartialEqExtras, get_field_by_type::GetFieldByType)]
338#[get_field_by_type_target(Span)]
339#[partial_eq_ignore_types(Span)]
340pub enum ObjectDestructuringField<T: DestructuringFieldInto> {
341 Name(VariableIdentifier, T::TypeAnnotation, Option<Box<Expression>>, Span),
343 Map {
345 from: PropertyKey<crate::property_key::AlwaysPublic>,
346 annotation: T::TypeAnnotation,
347 name: WithComment<T>,
348 default_value: Option<Box<Expression>>,
349 position: Span,
350 },
351}
352
353impl<T: DestructuringFieldInto> ListItem for WithComment<ObjectDestructuringField<T>> {
354 const LAST_PREFIX: Option<TSXToken> = Some(TSXToken::Spread);
355
356 type LAST = SpreadDestructuringField<T>;
357
358 fn parse_last_item(
359 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
360 state: &mut crate::ParsingState,
361 options: &ParseOptions,
362 ) -> ParseResult<Self::LAST> {
363 let start = reader.expect_next(TSXToken::Spread)?;
364 let node = T::from_reader(reader, state, options)?;
365 let position = start.union(node.get_position());
366 Ok(SpreadDestructuringField(Box::new(node), position))
367 }
368}
369
370impl<T: DestructuringFieldInto> ASTNode for ObjectDestructuringField<T> {
371 fn from_reader(
372 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
373 state: &mut crate::ParsingState,
374 options: &ParseOptions,
375 ) -> ParseResult<Self> {
376 let key = PropertyKey::from_reader(reader, state, options)?;
377 if reader.peek().is_some_and(|Token(t, _)| is_destructuring_into_marker(t, options)) {
378 reader.next();
379 let name = WithComment::<T>::from_reader(reader, state, options)?;
380 let annotation = T::type_annotation_from_reader(reader, state, options)?;
381
382 let default_value = reader
383 .conditional_next(|t| matches!(t, TSXToken::Assign))
384 .is_some()
385 .then(|| Expression::from_reader(reader, state, options).map(Box::new))
386 .transpose()?;
387
388 let position = if let Some(ref dv) = default_value {
389 key.get_position().union(dv.get_position())
390 } else {
391 key.get_position()
392 };
393
394 Ok(Self::Map { from: key, annotation, name, default_value, position })
395 } else if let PropertyKey::Identifier(name, key_pos, _) = key {
396 let default_value = reader
397 .conditional_next(|t| matches!(t, TSXToken::Assign))
398 .is_some()
399 .then(|| Expression::from_reader(reader, state, options).map(Box::new))
400 .transpose()?;
401
402 let standard = VariableIdentifier::Standard(name, key_pos);
403 let annotation = T::type_annotation_from_reader(reader, state, options)?;
404 let position = if let Some(ref dv) = default_value {
405 key_pos.union(dv.get_position())
406 } else {
407 key_pos
408 };
409
410 Ok(Self::Name(standard, annotation, default_value, position))
411 } else {
412 let token = reader.next().ok_or_else(parse_lexing_error)?;
413 throw_unexpected_token_with_token(token, &[TSXToken::Colon])
414 }
415 }
416
417 fn to_string_from_buffer<U: source_map::ToString>(
418 &self,
419 buf: &mut U,
420 options: &crate::ToStringOptions,
421 local: crate::LocalToStringInformation,
422 ) {
423 match self {
424 Self::Name(name, _annotation, default_value, ..) => {
425 name.to_string_from_buffer(buf, options, local);
426 if let Some(default_value) = default_value {
427 options.push_gap_optionally(buf);
428 buf.push('=');
429 options.push_gap_optionally(buf);
430 default_value.to_string_from_buffer(buf, options, local);
431 }
432 }
433 Self::Map { from, annotation: _, name: variable_name, default_value, .. } => {
434 from.to_string_from_buffer(buf, options, local);
435 buf.push(':');
436 options.push_gap_optionally(buf);
437 variable_name.to_string_from_buffer(buf, options, local);
438 if let Some(default_value) = default_value {
439 options.push_gap_optionally(buf);
440 buf.push('=');
441 options.push_gap_optionally(buf);
442 default_value.to_string_from_buffer(buf, options, local);
443 }
444 }
445 }
446 }
447
448 fn get_position(&self) -> Span {
449 *self.get()
450 }
451}
452
453impl Visitable for VariableField {
455 fn visit<TData>(
456 &self,
457 visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
458 data: &mut TData,
459 options: &VisitOptions,
460 chain: &mut temporary_annex::Annex<crate::visiting::Chain>,
461 ) {
462 match self {
463 VariableField::Name(id) => {
464 if let VariableIdentifier::Standard(name, pos) = id {
465 let item = ImmutableVariableOrProperty::VariableFieldName(name, pos);
466 visitors.visit_variable(&item, data, chain);
467 }
468 }
469 VariableField::Array { members, spread: _, .. } => {
470 members.iter().for_each(|f| f.visit(visitors, data, options, chain));
471 }
472 VariableField::Object { members, spread: _, .. } => {
473 members.iter().for_each(|f| f.visit(visitors, data, options, chain));
474 }
475 }
476 }
477
478 fn visit_mut<TData>(
479 &mut self,
480 visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
481 data: &mut TData,
482 options: &VisitOptions,
483 chain: &mut temporary_annex::Annex<crate::visiting::Chain>,
484 ) {
485 match self {
486 VariableField::Name(identifier) => {
487 if let VariableIdentifier::Standard(name, _span) = identifier {
488 visitors.visit_variable_mut(
489 &mut MutableVariableOrProperty::VariableFieldName(name),
490 data,
491 chain,
492 );
493 }
494 }
495 VariableField::Array { members, spread: _, .. } => {
496 members.iter_mut().for_each(|f| f.visit_mut(visitors, data, options, chain));
497 }
498 VariableField::Object { members, spread: _, .. } => {
499 members.iter_mut().for_each(|f| f.visit_mut(visitors, data, options, chain));
500 }
501 }
502 }
503}
504
505impl Visitable for WithComment<ArrayDestructuringField<VariableField>> {
506 fn visit<TData>(
507 &self,
508 visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
509 data: &mut TData,
510 options: &VisitOptions,
511 chain: &mut temporary_annex::Annex<crate::Chain>,
512 ) {
513 let field = self.get_ast_ref();
514 let array_destructuring_member =
515 ImmutableVariableOrProperty::ArrayDestructuringMember(field);
516 visitors.visit_variable(&array_destructuring_member, data, chain);
517 match field {
518 ArrayDestructuringField::Comment { .. } | ArrayDestructuringField::None => {}
520 ArrayDestructuringField::Name(variable_field, _, expression) => {
521 variable_field.visit(visitors, data, options, chain);
522 expression.visit(visitors, data, options, chain);
523 }
524 }
525 }
526
527 fn visit_mut<TData>(
528 &mut self,
529 visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
530 data: &mut TData,
531 options: &VisitOptions,
532 chain: &mut temporary_annex::Annex<crate::Chain>,
533 ) {
534 let mut array_destructuring_member =
535 MutableVariableOrProperty::ArrayDestructuringMember(self.get_ast_mut());
536 visitors.visit_variable_mut(&mut array_destructuring_member, data, chain);
537 match self.get_ast_mut() {
538 ArrayDestructuringField::Comment { .. } | ArrayDestructuringField::None => {}
539 ArrayDestructuringField::Name(variable_field, _, default_value) => {
540 variable_field.visit_mut(visitors, data, options, chain);
541 default_value.visit_mut(visitors, data, options, chain);
542 }
543 }
544 }
545}
546
547impl Visitable for WithComment<ArrayDestructuringField<crate::ast::LHSOfAssignment>> {
548 fn visit<TData>(
549 &self,
550 _visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
551 _data: &mut TData,
552 _options: &VisitOptions,
553 _chain: &mut temporary_annex::Annex<crate::Chain>,
554 ) {
555 todo!()
556 }
557
558 fn visit_mut<TData>(
559 &mut self,
560 _visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
561 _data: &mut TData,
562 _options: &VisitOptions,
563 _chain: &mut temporary_annex::Annex<crate::Chain>,
564 ) {
565 todo!()
566 }
567}
568
569impl Visitable for WithComment<ObjectDestructuringField<VariableField>> {
570 fn visit<TData>(
571 &self,
572 visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
573 data: &mut TData,
574 options: &VisitOptions,
575 chain: &mut temporary_annex::Annex<crate::Chain>,
576 ) {
577 visitors.visit_variable(
578 &ImmutableVariableOrProperty::ObjectDestructuringMember(self),
579 data,
580 chain,
581 );
582 match self.get_ast_ref() {
583 ObjectDestructuringField::Name(_name, _, default_value, _) => {
584 default_value.visit(visitors, data, options, chain);
585 }
586 ObjectDestructuringField::Map {
587 name: variable_name,
588 annotation: _,
589 default_value,
590 ..
591 } => {
592 variable_name.visit(visitors, data, options, chain);
593 default_value.visit(visitors, data, options, chain);
594 }
595 }
596 }
597
598 fn visit_mut<TData>(
599 &mut self,
600 visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
601 data: &mut TData,
602 options: &VisitOptions,
603 chain: &mut temporary_annex::Annex<crate::Chain>,
604 ) {
605 visitors.visit_variable_mut(
606 &mut MutableVariableOrProperty::ObjectDestructuringMember(self),
607 data,
608 chain,
609 );
610 match self.get_ast_mut() {
611 ObjectDestructuringField::Name(_id, _, default_value, _) => {
612 default_value.visit_mut(visitors, data, options, chain);
613 }
614 ObjectDestructuringField::Map {
615 name: variable_name,
616 annotation: _,
617 default_value,
618 ..
619 } => {
620 variable_name.visit_mut(visitors, data, options, chain);
621 default_value.visit_mut(visitors, data, options, chain);
622 }
623 }
624 }
625}
626impl Visitable for WithComment<ObjectDestructuringField<crate::ast::LHSOfAssignment>> {
627 fn visit<TData>(
628 &self,
629 _visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
630 _data: &mut TData,
631 _options: &VisitOptions,
632 _chain: &mut temporary_annex::Annex<crate::Chain>,
633 ) {
634 todo!()
635 }
636
637 fn visit_mut<TData>(
638 &mut self,
639 _visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
640 _data: &mut TData,
641 _options: &VisitOptions,
642 _chain: &mut temporary_annex::Annex<crate::Chain>,
643 ) {
644 todo!()
645 }
646}
647
648#[cfg(not(feature = "extras"))]
649fn is_destructuring_into_marker(t: &TSXToken, _options: &ParseOptions) -> bool {
650 matches!(t, TSXToken::Colon)
651}
652
653#[cfg(feature = "extras")]
654fn is_destructuring_into_marker(t: &TSXToken, options: &ParseOptions) -> bool {
655 if options.destructuring_type_annotation {
656 matches!(t, TSXToken::Keyword(crate::TSXKeyword::As))
657 } else {
658 matches!(t, TSXToken::Colon)
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665 use crate::{assert_matches_ast, span};
666
667 #[test]
668 fn name() {
669 assert_matches_ast!(
670 "x",
671 VariableField::Name(VariableIdentifier::Standard(
672 Deref @ "x",
673 Span { start: 0, end: 1, .. },
674 ))
675 );
676 }
677
678 #[test]
679 fn array() {
680 assert_matches_ast!(
681 "[x, y, z]",
682 VariableField::Array {
683 members: Deref @ [WithComment::None(ArrayDestructuringField::Name(
684 VariableField::Name(VariableIdentifier::Standard(Deref @ "x", span!(1, 2))),
685 None,
686 None,
687 )), WithComment::None(ArrayDestructuringField::Name(
688 VariableField::Name(VariableIdentifier::Standard(Deref @ "y", span!(4, 5))),
689 None,
690 None,
691 )), WithComment::None(ArrayDestructuringField::Name(
692 VariableField::Name(VariableIdentifier::Standard(Deref @ "z", span!(7, 8))),
693 None,
694 None,
695 ))],
696 spread: _,
697 position: _
698 }
699 );
700
701 assert_matches_ast!(
702 "[x,,z]",
703 VariableField::Array {
704 members:
705 Deref @ [WithComment::None(ArrayDestructuringField::Name(
706 VariableField::Name(VariableIdentifier::Standard(Deref @ "x", span!(1, 2))),
707 None,
708 None,
709 )), WithComment::None(ArrayDestructuringField::None), WithComment::None(ArrayDestructuringField::Name(
710 VariableField::Name(VariableIdentifier::Standard(Deref @ "z", span!(4, 5))),
711 None,
712 None,
713 ))],
714 spread: None,
715 position: span!(0, 6),
716 }
717 );
718 }
719
720 #[test]
721 fn object() {
722 assert_matches_ast!(
723 "{x, y, z}",
724 VariableField::Object {
725 members: Deref @ [WithComment::None(ObjectDestructuringField::Name(
726 VariableIdentifier::Standard(Deref @ "x", span!(1, 2)),
727 None,
728 None,
729 span!(1, 2),
730 )), WithComment::None(ObjectDestructuringField::Name(
731 VariableIdentifier::Standard(Deref @ "y", span!(4, 5)),
732 None,
733 None,
734 span!(4, 5),
735 )), WithComment::None(ObjectDestructuringField::Name(
736 VariableIdentifier::Standard(Deref @ "z", span!(7, 8)),
737 None,
738 None,
739 span!(7, 8),
740 ))],
741 spread: None,
742 position: span!(0, 9),
743 }
744 );
745 }
746
747 #[test]
748 fn name_with_default() {
749 assert_matches_ast!(
750 "{ x = 2 }",
751 VariableField::Object {
752 members:
753 Deref @ [WithComment::None(ObjectDestructuringField::Name(
754 VariableIdentifier::Standard(Deref @ "x", span!(2, 3)),
755 None,
756 Some(
757 Deref @ Expression::NumberLiteral(
758 crate::number::NumberRepresentation::Number { .. },
759 span!(6, 7),
760 ),
761 ),
762 span!(2, 7),
763 ))],
764 spread: None,
765 position: span!(0, 9),
766 }
767 );
768 }
769
770 #[test]
771 fn array_spread() {
772 assert_matches_ast!(
773 "[x, ...y]",
774 VariableField::Array {
775 members:Deref @ [WithComment::None(ArrayDestructuringField::Name(
776 VariableField::Name(VariableIdentifier::Standard(Deref @ "x", span!(1, 2))),
777 None,
778 None,
779 ))],
780 spread: Some(SpreadDestructuringField( Deref @ VariableField::Name(VariableIdentifier::Standard(Deref @ "y", span!(7, 8))), span!(4, 8))),
781 position: span!(0, 9)
782 }
783 );
784 }
785}