1use std::{
2 iter,
3 mem::{self, replace},
4};
5
6use swc_common::{util::take::Take, Mark, Spanned, DUMMY_SP};
7use swc_ecma_ast::*;
8use swc_ecma_compat_common::impl_visit_mut_fn;
9use swc_ecma_transforms_base::{helper, helper_expr, perf::Check};
10use swc_ecma_transforms_macros::fast_path;
11use swc_ecma_utils::{
12 alias_ident_for, alias_if_required, find_pat_ids, is_literal, private_ident, quote_ident,
13 var::VarCollector, ExprFactory, StmtLike,
14};
15use swc_ecma_visit::{
16 noop_visit_mut_type, noop_visit_type, Visit, VisitMut, VisitMutWith, VisitWith,
17};
18use swc_trace_macro::swc_trace;
19
20use super::object_rest_spread::Config;
21
22#[derive(Default)]
23pub(super) struct ObjectRest {
24 pub vars: Vec<VarDeclarator>,
26 pub mutable_vars: Vec<VarDeclarator>,
28 pub exprs: Vec<Box<Expr>>,
30 pub config: Config,
31}
32
33macro_rules! impl_for_for_stmt {
34 ($name:ident, $T:tt) => {
35 fn $name(&mut self, for_stmt: &mut $T) {
36 if !contains_rest(for_stmt) {
37 return;
38 }
39
40 let stmt;
41
42 let left = match &mut for_stmt.left {
43 ForHead::VarDecl(var_decl) => {
44 let ref_ident = private_ident!("_ref");
45
46 let mut decls = var_decl
48 .decls
49 .take()
50 .into_iter()
51 .map(|decl| VarDeclarator {
52 name: decl.name,
53 init: Some(Box::new(Expr::Ident(ref_ident.clone()))),
54 ..decl
55 })
56 .collect::<Vec<_>>();
57
58 decls.append(&mut self.vars.take());
60
61 stmt = Some(Stmt::Decl(
62 VarDecl {
63 span: DUMMY_SP,
64 kind: VarDeclKind::Let,
65 decls,
66 ..Default::default()
67 }
68 .into(),
69 ));
70
71 VarDecl {
72 decls: vec![VarDeclarator {
73 span: DUMMY_SP,
74 name: ref_ident.into(),
75 init: None,
76 definite: false,
77 }],
78 ..*var_decl.take()
79 }
80 .into()
81 }
82 ForHead::Pat(pat) => {
83 let var_ident = private_ident!("_ref");
84 let pat = pat.take();
85
86 stmt = Some(
88 var_ident
89 .clone()
90 .make_assign_to(op!("="), pat.try_into().unwrap())
91 .into_stmt(),
92 );
93
94 VarDecl {
96 span: DUMMY_SP,
97 kind: VarDeclKind::Var,
98 decls: vec![VarDeclarator {
99 span: DUMMY_SP,
100 name: var_ident.into(),
101 init: None,
102 definite: false,
103 }],
104 ..Default::default()
105 }
106 .into()
107 }
108
109 ForHead::UsingDecl(..) => {
110 unreachable!("using declaration must be removed by previous pass")
111 }
112
113 #[cfg(swc_ast_unknown)]
114 _ => panic!("unable to access unknown nodes"),
115 };
116 for_stmt.left = left;
117
118 for_stmt.body = Box::new(Stmt::Block(match &mut *for_stmt.body {
119 Stmt::Block(BlockStmt { span, stmts, ctxt }) => BlockStmt {
120 span: *span,
121 stmts: stmt.into_iter().chain(stmts.take()).collect(),
122 ctxt: *ctxt,
123 },
124 body => BlockStmt {
125 span: DUMMY_SP,
126 stmts: stmt.into_iter().chain(iter::once(body.take())).collect(),
127 ..Default::default()
128 },
129 }));
130
131 for_stmt.right.visit_mut_with(self);
132 for_stmt.body.visit_mut_with(self);
133 }
134 };
135}
136
137#[derive(Default)]
138struct RestVisitor {
139 found: bool,
140}
141
142#[swc_trace]
143impl Visit for RestVisitor {
144 noop_visit_type!(fail);
145
146 fn visit_object_pat_prop(&mut self, prop: &ObjectPatProp) {
147 match *prop {
148 ObjectPatProp::Rest(..) => self.found = true,
149 _ => prop.visit_children_with(self),
150 }
151 }
152}
153
154impl Check for RestVisitor {
155 fn should_handle(&self) -> bool {
156 self.found
157 }
158}
159
160fn contains_rest<N>(node: &N) -> bool
161where
162 N: VisitWith<RestVisitor>,
163{
164 let mut v = RestVisitor { found: false };
165 node.visit_with(&mut v);
166 v.found
167}
168
169#[swc_trace]
170#[fast_path(RestVisitor)]
171impl VisitMut for ObjectRest {
172 noop_visit_mut_type!(fail);
173
174 impl_for_for_stmt!(visit_mut_for_in_stmt, ForInStmt);
175
176 impl_for_for_stmt!(visit_mut_for_of_stmt, ForOfStmt);
177
178 impl_visit_mut_fn!();
179
180 fn visit_mut_expr(&mut self, expr: &mut Expr) {
182 if !contains_rest(expr) {
184 return;
185 }
186
187 expr.visit_mut_children_with(self);
188
189 if let Expr::Assign(AssignExpr {
190 span,
191 left: AssignTarget::Pat(pat),
192 op: op!("="),
193 right,
194 }) = expr
195 {
196 let mut var_ident = alias_ident_for(right, "_tmp");
197 var_ident.ctxt = var_ident.ctxt.apply_mark(Mark::new());
198
199 self.mutable_vars.push(VarDeclarator {
201 span: DUMMY_SP,
202 name: var_ident.clone().into(),
203 init: None,
204 definite: false,
205 });
206 self.exprs.push(
208 AssignExpr {
209 span: DUMMY_SP,
210 left: var_ident.clone().into(),
211 op: op!("="),
212 right: right.take(),
213 }
214 .into(),
215 );
216 let pat = self.fold_rest(
217 &mut 0,
218 pat.take().into(),
219 var_ident.clone().into(),
220 true,
221 true,
222 );
223
224 match pat {
225 Pat::Object(ObjectPat { ref props, .. }) if props.is_empty() => {}
226 _ => self.exprs.push(
227 AssignExpr {
228 span: *span,
229 left: pat.try_into().unwrap(),
230 op: op!("="),
231 right: Box::new(var_ident.clone().into()),
232 }
233 .into(),
234 ),
235 }
236 self.exprs.push(Box::new(var_ident.into()));
237 *expr = SeqExpr {
238 span: DUMMY_SP,
239 exprs: mem::take(&mut self.exprs),
240 }
241 .into();
242 };
243 }
244
245 fn visit_mut_module_decl(&mut self, decl: &mut ModuleDecl) {
247 if !contains_rest(decl) {
248 return;
250 }
251
252 match decl {
253 ModuleDecl::ExportDecl(ExportDecl {
254 span,
255 decl: Decl::Var(var_decl),
256 ..
257 }) if var_decl.decls.iter().any(|v| v.name.is_object()) => {
258 let specifiers = {
259 let mut found: Vec<Ident> = Vec::new();
260 let mut finder = VarCollector { to: &mut found };
261 var_decl.visit_with(&mut finder);
262 found
263 .into_iter()
264 .map(|ident| ExportNamedSpecifier {
265 span: DUMMY_SP,
266 orig: ident.into(),
267 exported: None,
268 is_type_only: false,
269 })
270 .map(ExportSpecifier::Named)
271 .collect()
272 };
273
274 let export = NamedExport {
275 span: *span,
276 specifiers,
277 src: None,
278 type_only: false,
279 with: None,
280 };
281
282 var_decl.visit_mut_with(self);
283 self.vars.append(&mut var_decl.decls);
284
285 *decl = export.into();
286 }
287 _ => {
288 decl.visit_mut_children_with(self);
289 }
290 };
291 }
292
293 fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
294 self.visit_mut_stmt_like(n);
295 }
296
297 fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
298 self.visit_mut_stmt_like(n);
299 }
300
301 fn visit_mut_var_declarators(&mut self, decls: &mut Vec<VarDeclarator>) {
302 if !contains_rest(decls) {
304 return;
305 }
306
307 for mut decl in decls.drain(..) {
308 if !contains_rest(&decl) {
310 self.vars.push(decl);
312 continue;
313 }
314
315 decl.visit_mut_children_with(self);
316
317 let (var_ident, _) = match decl.name {
324 Pat::Ident(ref i) => (Ident::from(i), false),
325
326 _ => match decl.init {
327 Some(ref e) => alias_if_required(e, "ref"),
328 _ => (private_ident!("_ref"), true),
329 },
330 };
331
332 let has_init = decl.init.is_some();
333 if let Some(init) = decl.init {
334 match decl.name {
335 Pat::Object(ObjectPat { props, .. })
337 if props.len() == 1 && matches!(props[0], ObjectPatProp::Rest(..)) =>
338 {
339 let prop = match props.into_iter().next().unwrap() {
340 ObjectPatProp::Rest(r) => r,
341 _ => unreachable!(),
342 };
343
344 self.vars.push(VarDeclarator {
345 span: prop.span(),
346 name: *prop.arg,
347 init: Some(
348 CallExpr {
349 span: DUMMY_SP,
350 callee: helper!(extends),
351 args: vec![
352 ObjectLit {
353 span: DUMMY_SP,
354 props: Vec::new(),
355 }
356 .as_arg(),
357 helper_expr!(object_destructuring_empty)
358 .as_call(DUMMY_SP, vec![init.as_arg()])
359 .as_arg(),
360 ],
361 ..Default::default()
362 }
363 .into(),
364 ),
365 definite: false,
366 });
367 continue;
368 }
369 _ => {}
370 }
371
372 match *init {
373 Expr::Ident(..) => {}
375 _ => {
376 self.push_var_if_not_empty(VarDeclarator {
378 span: DUMMY_SP,
379 name: var_ident.clone().into(),
380 init: Some(init),
381 definite: false,
382 });
383 }
384 }
385 }
386
387 let mut index = self.vars.len();
388 let mut pat =
389 self.fold_rest(&mut index, decl.name, var_ident.clone().into(), false, true);
390 match pat {
391 Pat::Object(ObjectPat { ref props, .. }) if props.is_empty() => {}
393
394 _ => {
395 pat.visit_mut_with(&mut PatSimplifier);
402
403 self.insert_var_if_not_empty(
404 index,
405 VarDeclarator {
406 name: pat,
407 init: if has_init {
409 Some(var_ident.clone().into())
410 } else {
411 None
412 },
413 ..decl
414 },
415 )
416 }
417 }
418 }
419
420 *decls = mem::take(&mut self.vars);
421 }
422}
423
424#[swc_trace]
425impl ObjectRest {
426 fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
427 where
428 T: StmtLike + VisitWith<RestVisitor> + VisitMutWith<ObjectRest>,
429 Vec<T>: VisitMutWith<Self> + VisitWith<RestVisitor>,
430 {
431 if !contains_rest(stmts) {
432 return;
433 }
434
435 let mut buf = Vec::with_capacity(stmts.len());
436
437 for mut stmt in stmts.drain(..) {
438 let mut folder = ObjectRest {
439 config: self.config,
440 ..Default::default()
441 };
442 stmt.visit_mut_with(&mut folder);
443
444 if !folder.mutable_vars.is_empty() {
447 buf.push(T::from(
448 VarDecl {
449 span: DUMMY_SP,
450 kind: VarDeclKind::Var,
451 decls: folder.mutable_vars,
452 ..Default::default()
453 }
454 .into(),
455 ));
456 }
457
458 if !folder.vars.is_empty() {
459 buf.push(T::from(
460 VarDecl {
461 span: DUMMY_SP,
462 kind: VarDeclKind::Var,
463 decls: folder.vars,
464 ..Default::default()
465 }
466 .into(),
467 ));
468 }
469
470 buf.push(stmt);
471
472 buf.extend(folder.exprs.into_iter().map(|v| v.into_stmt()).map(T::from));
473 }
474
475 *stmts = buf;
476 }
477}
478
479impl ObjectRest {
480 fn insert_var_if_not_empty(&mut self, idx: usize, mut decl: VarDeclarator) {
481 if let Some(e1) = decl.init {
482 if let Expr::Ident(ref i1) = *e1 {
483 if let Pat::Ident(ref i2) = decl.name {
484 if i1.ctxt == i2.ctxt && i1.sym == i2.sym {
485 return;
486 }
487 }
488 }
489 decl.init = Some(e1);
490 }
491
492 if let Pat::Object(..) | Pat::Array(..) = decl.name {
493 let ids: Vec<Id> = find_pat_ids(&decl.name);
494 if ids.is_empty() {
495 return;
496 }
497 }
498 self.vars.insert(idx, decl)
499 }
500
501 fn push_var_if_not_empty(&mut self, mut decl: VarDeclarator) {
502 if let Some(e1) = decl.init {
503 if let Expr::Ident(ref i1) = *e1 {
504 if let Pat::Ident(ref i2) = decl.name {
505 if i1.sym == i2.sym && i1.ctxt == i2.ctxt {
506 return;
507 }
508 }
509 }
510 decl.init = Some(e1);
511 }
512
513 if let Pat::Object(ObjectPat { ref props, .. }) = decl.name {
514 if props.is_empty() {
515 return;
516 }
517 }
518 self.vars.push(decl)
519 }
520
521 fn visit_mut_fn_like(
522 &mut self,
523 params: &mut Vec<Param>,
524 body: &mut BlockStmt,
525 ) -> (Vec<Param>, BlockStmt) {
526 if !contains_rest(params) {
527 return (params.take(), body.take());
529 }
530
531 let prev_state = replace(
532 self,
533 Self {
534 config: self.config,
535 ..Default::default()
536 },
537 );
538
539 let params = params
540 .drain(..)
541 .map(|mut param| {
542 let var_ident = private_ident!(param.span(), "_param");
543 let mut index = self.vars.len();
544 param.pat =
545 self.fold_rest(&mut index, param.pat, var_ident.clone().into(), false, true);
546
547 match param.pat {
548 Pat::Rest(..) | Pat::Ident(..) => param,
549 Pat::Assign(AssignPat { ref left, .. })
550 if left.is_ident() || left.is_rest() || left.is_array() =>
551 {
552 param
553 }
554 Pat::Assign(n) => {
555 let AssignPat {
556 span, left, right, ..
557 } = n;
558 self.insert_var_if_not_empty(
559 index,
560 VarDeclarator {
561 span,
562 name: *left,
563 init: Some(var_ident.clone().into()),
564 definite: false,
565 },
566 );
567 Param {
568 span: DUMMY_SP,
569 decorators: Default::default(),
570 pat: AssignPat {
571 span,
572 left: var_ident.into(),
573 right,
574 }
575 .into(),
576 }
577 }
578 _ => {
579 self.insert_var_if_not_empty(
581 index,
582 VarDeclarator {
583 span: DUMMY_SP,
584 name: param.pat,
585 init: Some(var_ident.clone().into()),
586 definite: false,
587 },
588 );
589 Param {
590 span: DUMMY_SP,
591 decorators: Default::default(),
592 pat: var_ident.into(),
593 }
594 }
595 }
596 })
597 .collect();
598
599 let ret = (
600 params,
601 BlockStmt {
602 stmts: if self.vars.is_empty() {
603 None
604 } else {
605 Some(
606 VarDecl {
607 span: DUMMY_SP,
608 kind: VarDeclKind::Var,
609 decls: mem::take(&mut self.vars),
610 ..Default::default()
611 }
612 .into(),
613 )
614 }
615 .into_iter()
616 .chain(body.stmts.take())
617 .collect(),
618 ..body.take()
619 },
620 );
621
622 *self = prev_state;
623
624 ret
625 }
626
627 fn fold_rest(
628 &mut self,
629 index: &mut usize,
630 pat: Pat,
631 obj: Box<Expr>,
632 use_expr_for_assign: bool,
633 use_member_for_array: bool,
634 ) -> Pat {
635 if pat.is_ident() {
645 }
647
648 let ObjectPat {
649 span,
650 props,
651 type_ann,
652 ..
653 } = match pat {
654 Pat::Object(pat) => pat,
655 Pat::Assign(n) => {
656 let AssignPat {
657 span, left, right, ..
658 } = n;
659 let left = Box::new(self.fold_rest(
660 index,
661 *left,
662 obj,
663 use_expr_for_assign,
664 use_member_for_array,
665 ));
666 return AssignPat { span, left, right }.into();
667 }
668 Pat::Array(n) => {
669 let ArrayPat { span, elems, .. } = n;
670 let elems = elems
671 .into_iter()
672 .enumerate()
673 .map(|(i, elem)| {
674 elem.map(|elem| {
675 self.fold_rest(
676 index,
677 elem,
678 if use_member_for_array {
679 obj.clone().computed_member(i as f64).into()
680 } else {
681 obj.clone()
682 },
683 use_expr_for_assign,
684 use_member_for_array,
685 )
686 })
687 })
688 .collect();
689
690 return ArrayPat { span, elems, ..n }.into();
691 }
692 _ => return pat,
693 };
694
695 let mut props: Vec<ObjectPatProp> = props
696 .into_iter()
697 .map(|prop| match prop {
698 ObjectPatProp::Rest(n) => {
699 let RestPat {
700 arg, dot3_token, ..
701 } = n;
702
703 let pat = self.fold_rest(
704 index,
705 *arg,
706 obj.clone(),
708 use_expr_for_assign,
709 true,
710 );
711 ObjectPatProp::Rest(RestPat {
712 dot3_token,
713 arg: Box::new(pat),
714 ..n
715 })
716 }
717 ObjectPatProp::KeyValue(KeyValuePatProp { key, value }) => {
718 let (key, prop) = match key {
719 PropName::Ident(ref ident) => {
720 let ident = ident.clone();
721 (key, MemberProp::Ident(ident))
722 }
723 PropName::Str(Str {
724 ref value, span, ..
725 }) => {
726 let value = value.clone();
727 (
728 key,
729 MemberProp::Computed(ComputedPropName {
730 span,
731 expr: Lit::Str(Str {
732 span,
733 raw: None,
734 value: value.clone(),
735 })
736 .into(),
737 }),
738 )
739 }
740 PropName::Num(Number { span, value, .. }) => (
741 key,
742 MemberProp::Computed(ComputedPropName {
743 span,
744 expr: Lit::Str(Str {
745 span,
746 raw: None,
747
748 value: format!("{value}").into(),
749 })
750 .into(),
751 }),
752 ),
753 PropName::BigInt(BigInt {
754 span, ref value, ..
755 }) => {
756 let value = value.clone();
757 (
758 key,
759 MemberProp::Computed(ComputedPropName {
760 span,
761 expr: Lit::Str(Str {
762 span,
763 raw: None,
764 value: format!("{value}").into(),
765 })
766 .into(),
767 }),
768 )
769 }
770 PropName::Computed(ref c) if is_literal(&c.expr) => {
771 let c = c.clone();
772 (key, MemberProp::Computed(c))
773 }
774 PropName::Computed(c) => {
775 let (ident, aliased) = alias_if_required(&c.expr, "key");
776 if aliased {
777 *index += 1;
778 self.vars.push(VarDeclarator {
779 span: DUMMY_SP,
780 name: ident.clone().into(),
781 init: Some(c.expr),
782 definite: false,
783 });
784 }
785
786 (
787 PropName::Computed(ComputedPropName {
788 span: c.span,
789 expr: ident.clone().into(),
790 }),
791 MemberProp::Computed(ComputedPropName {
792 span: c.span,
793 expr: ident.into(),
794 }),
795 )
796 }
797 #[cfg(swc_ast_unknown)]
798 _ => panic!("unable to access unknown nodes"),
799 };
800
801 let value = Box::new(
802 self.fold_rest(
803 index,
804 *value,
805 Box::new(
806 MemberExpr {
807 span: DUMMY_SP,
808 obj: obj.clone(),
809 prop,
810 }
811 .into(),
812 ),
813 use_expr_for_assign,
814 true,
815 ),
816 );
817 ObjectPatProp::KeyValue(KeyValuePatProp { key, value })
818 }
819 _ => prop,
820 })
821 .collect();
822
823 match props.last() {
824 Some(ObjectPatProp::Rest(..)) => {}
825 _ => {
826 return ObjectPat {
827 span,
828 props,
829 optional: false,
830 type_ann,
831 }
832 .into();
833 }
834 }
835 let last = match props.pop() {
836 Some(ObjectPatProp::Rest(rest)) => rest,
837 _ => unreachable!(),
838 };
839
840 let excluded_props = excluded_props(&props);
841
842 if use_expr_for_assign {
843 self.exprs.push(
845 AssignExpr {
846 span: DUMMY_SP,
847 left: last.arg.try_into().unwrap(),
848 op: op!("="),
849 right: Box::new(object_without_properties(
850 obj,
851 excluded_props,
852 self.config.no_symbol,
853 )),
854 }
855 .into(),
856 );
857 } else {
858 self.push_var_if_not_empty(VarDeclarator {
860 span: DUMMY_SP,
861 name: *last.arg,
862 init: Some(Box::new(object_without_properties(
863 obj,
864 excluded_props,
865 self.config.no_symbol,
866 ))),
867 definite: false,
868 });
869 }
870
871 ObjectPat {
872 props,
873 span,
874 type_ann,
875 optional: false,
876 }
877 .into()
878 }
879}
880
881#[tracing::instrument(level = "debug", skip_all)]
882fn object_without_properties(
883 obj: Box<Expr>,
884 excluded_props: Vec<Option<ExprOrSpread>>,
885 no_symbol: bool,
886) -> Expr {
887 if excluded_props.is_empty() {
888 return CallExpr {
889 span: DUMMY_SP,
890 callee: helper!(extends),
891 args: vec![
892 ObjectLit {
893 span: DUMMY_SP,
894 props: Vec::new(),
895 }
896 .as_arg(),
897 helper_expr!(object_destructuring_empty)
898 .as_call(DUMMY_SP, vec![obj.as_arg()])
899 .as_arg(),
900 ],
901 ..Default::default()
902 }
903 .into();
904 }
905
906 let excluded_props = excluded_props
907 .into_iter()
908 .map(|v| {
909 v.map(|v| match *v.expr {
910 Expr::Lit(Lit::Num(Number { span, value, .. })) => ExprOrSpread {
911 expr: Lit::Str(Str {
912 span,
913 raw: None,
914 value: value.to_string().into(),
915 })
916 .into(),
917 ..v
918 },
919 _ => v,
920 })
921 })
922 .collect();
923
924 CallExpr {
925 span: DUMMY_SP,
926 callee: if no_symbol {
927 helper!(object_without_properties_loose)
928 } else {
929 helper!(object_without_properties)
930 },
931 args: vec![
932 obj.as_arg(),
933 if is_literal(&excluded_props) {
934 ArrayLit {
935 span: DUMMY_SP,
936 elems: excluded_props,
937 }
938 .as_arg()
939 } else {
940 CallExpr {
941 span: DUMMY_SP,
942 callee: ArrayLit {
943 span: DUMMY_SP,
944 elems: excluded_props,
945 }
946 .make_member(quote_ident!("map"))
947 .as_callee(),
948 args: vec![helper_expr!(to_property_key).as_arg()],
949 ..Default::default()
950 }
951 .as_arg()
952 },
953 ],
954 ..Default::default()
955 }
956 .into()
957}
958
959#[tracing::instrument(level = "debug", skip_all)]
960fn excluded_props(props: &[ObjectPatProp]) -> Vec<Option<ExprOrSpread>> {
961 props
962 .iter()
963 .map(|prop| match prop {
964 ObjectPatProp::KeyValue(KeyValuePatProp { key, .. }) => match key {
965 PropName::Ident(ident) => Lit::Str(Str {
966 span: ident.span,
967 raw: None,
968 value: ident.sym.clone().into(),
969 })
970 .as_arg(),
971 PropName::Str(s) => Lit::Str(s.clone()).as_arg(),
972 PropName::Num(Number { span, value, .. }) => Lit::Str(Str {
973 span: *span,
974 raw: None,
975
976 value: format!("{value}").into(),
977 })
978 .as_arg(),
979 PropName::BigInt(BigInt { span, value, .. }) => Lit::Str(Str {
980 span: *span,
981 raw: None,
982
983 value: format!("{value}").into(),
984 })
985 .as_arg(),
986 PropName::Computed(c) => c.expr.clone().as_arg(),
987 #[cfg(swc_ast_unknown)]
988 _ => panic!("unable to access unknown nodes"),
989 },
990 ObjectPatProp::Assign(AssignPatProp { key, .. }) => Lit::Str(Str {
991 span: key.span,
992 raw: None,
993 value: key.sym.clone().into(),
994 })
995 .as_arg(),
996 ObjectPatProp::Rest(..) => unreachable!("invalid syntax (multiple rest element)"),
997 #[cfg(swc_ast_unknown)]
998 _ => panic!("unable to access unknown nodes"),
999 })
1000 .map(Some)
1001 .collect()
1002}
1003
1004struct PatSimplifier;
1008
1009#[swc_trace]
1010impl VisitMut for PatSimplifier {
1011 noop_visit_mut_type!(fail);
1012
1013 fn visit_mut_pat(&mut self, pat: &mut Pat) {
1014 pat.visit_mut_children_with(self);
1015
1016 if let Pat::Object(o) = pat {
1017 o.props.retain(|prop| {
1018 if let ObjectPatProp::KeyValue(KeyValuePatProp { value, .. }) = prop {
1019 match &**value {
1020 Pat::Object(ObjectPat { props, .. }) if props.is_empty() => {
1021 return false;
1022 }
1023 _ => {}
1024 }
1025 }
1026
1027 true
1028 });
1029 }
1030 }
1031}