Repository: swc-project/swc (rust)

Issue:
Panic error when destructuring assignment target is an object property
### Describe the bug

As part of some testing, when going through the [test262 suite](https://github.com/tc39/test262) and most specifically test case [obj-rest-to-property.js](https://github.com/tc39/test262/blob/main/test/language/statements/for-of/dstr/obj-rest-to-property.js), SWC is crashing with the following error (Detailed log when using `RUST_BACKTRACE=1 SWC_DEBUG=1 npx swc --sync <file>`):

```log
Panic: PanicInfo { payload: Any { .. }, message: Some(not implemented: Pattern VarDeclarator { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #0 }, name: Expr(Member(MemberExpr { span: Span { lo: BytePos(1203), hi: BytePos(1208), ctxt: #0 }, obj: Ident(Ident { span: Span { lo: BytePos(1203), hi: BytePos(1206), ctxt: #1 }, sym: Atom('src' type=inline), optional: false }), prop: Ident(Ident { span: Span { lo: BytePos(1207), hi: BytePos(1208), ctxt: #0 }, sym: Atom('y' type=inline), optional: false }) })), init: Some(Call(CallExpr { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #0 }, callee: Expr(Ident(Ident { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #3 }, sym: Atom('_extends' type=static), optional: false })), args: [ExprOrSpread { spread: None, expr: Object(ObjectLit { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #0 }, props: [] }) }, ExprOrSpread { spread: None, expr: Ident(Ident { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #2 }, sym: Atom('_ref' type=inline), optional: false }) }], type_args: None })), definite: false }), location: Location { file: "crates/swc_ecma_transforms_compat/src/es2015/destructuring.rs", line: 472, col: 18 } }
Backtrace:    0: <unknown>
   1: <unknown>
   2: <unknown>
   3: _rust_eh_personality
   4: _rust_eh_personality
   5: _rust_eh_personality
   6: _rust_eh_personality
   7: _wasmer_vm_f64_nearest
   8: _rust_eh_personality
   9: _rust_eh_personality
// This file was procedurally generated from the following sources:
  10: _rust_eh_personality
  11: _rust_eh_personality
  12: _rust_eh_personality
  13: _rust_eh_personality
  14: _rust_eh_personality
  15: _rust_eh_personality
  16: _rust_eh_personality
  17: _rust_eh_personality
  18: _rust_eh_personality
  19: <unknown>
  20: __ZN6v8impl12_GLOBAL__N_123FunctionCallbackWrapper6InvokeERKN2v820FunctionCallbackInfoINS2_5ValueEEE
  21: __ZN2v88internal25FunctionCallbackArguments4CallENS0_15CallHandlerInfoE
  22: __ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE
  23: __ZN2v88internalL26Builtin_Impl_HandleApiCallENS0_16BuiltinArgumentsEPNS0_7IsolateE

failed to handle: not implemented: Pattern VarDeclarator { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #0 }, name: Expr(Member(MemberExpr { span: Span { lo: BytePos(1203), hi: BytePos(1208), ctxt: #0 }, obj: Ident(Ident { span: Span { lo: BytePos(1203), hi: BytePos(1206), ctxt: #1 }, sym: Atom('src' type=inline), optional: false }), prop: Ident(Ident { span: Span { lo: BytePos(1207), hi: BytePos(1208), ctxt: #0 }, sym: Atom('y' type=inline), optional: false }) })), init: Some(Call(CallExpr { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #0 }, callee: Expr(Ident(Ident { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #3 }, sym: Atom('_extends' type=static), optional: false })), args: [ExprOrSpread { spread: None, expr: Object(ObjectLit { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #0 }, props: [] }) }, ExprOrSpread { spread: None, expr: Ident(Ident { span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #2 }, sym: Atom('_ref' type=inline), optional: false }) }], type_args: None })), definite: false }

Stack backtrace:
   0: _rust_eh_personality
   1: _wasmer_vm_f64_nearest
   2: <unknown>
   3: __ZN6v8impl12_GLOBAL__N_123FunctionCallbackWrapper6InvokeERKN2v820FunctionCallbackInfoINS2_5ValueEEE
   4: __ZN2v88internal25FunctionCallbackArguments4CallENS0_15CallHandlerInfoE
   5: __ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE
   6: __ZN2v88internalL26Builtin_Impl_HandleApiCallENS0_16BuiltinArgumentsEPNS0_7IsolateE
Error: Failed to compile 1 file with swc.
    at Object.assertCompilationResult (/Users/edwin.coronado/Desktop/test/node_modules/@swc/cli/lib/swc/util.js:115:15)
    at files (/Users/edwin.coronado/Desktop/test/node_modules/@swc/cli/lib/swc/file.js:175:18)
    at async _default (/Users/edwin.coronado/Desktop/test/node_modules/@swc/cli/lib/swc/file.js:194:9)
```




### Input code

```typescript
var src = {};

var counter = 0;

for ({...src.y} of [{ x: 1, y: 2}]) {
  assert.sameValue(src.y.x, 1);
  assert.sameValue(src.y.y, 2);

  verifyProperty(src, "y", {
    enumerable: true,
    writable: true,
    configurable: true
  });
  counter += 1;
}

assert.sameValue(counter, 1);
```


### Config

```json
{
  "jsc": {
    "target": "es5",
    "parser": {
      "syntax": "ecmascript"
    }
  },
  "module": {
    "type": "commonjs"
  },
  "sourceMaps": true
}
```


### Playground link

https://play.swc.rs/?version=1.2.138&code=H4sIAAAAAAAAA3WPwQqDMBBE7%2FmKwZPSEKpHxX%2FoqZfSQyprEdSU1ViD5N%2BbhhZKS4%2F75jHMLpoxcYMam6%2BEWMLZGDvOxAHtA2kNI92UUsFSzsO0OG1YS%2BQSrkThzxk2AehpIp7VpAc66t5SGn21SuRZ9T93EkXIg7AQd607sLkFzz1jicQlMrYDNNqBWF96KjGzJRnpnbv5mzVmbLur%2FXAD9nHE%2B7VdjbwSXoifVS8jrn4Akhf7dBwBAAA%3D&config=H4sIAAAAAAAAAx3LSwrAIAxF0b28sdNO3EMXEdJQKrVKEqEi7r2f0R0c7kAyRhxw0l0cEWILAiqpiX5g%2FXK6P%2BBMxnpUx5wBuWztlH%2Ft9S245FyuZHjRSlOWlaohujaZDx1euC9oAAAA

### Expected behavior

SWC should not crash and it should transpile correctly. 

Babel output is this:
```js
function _objectDestructuringEmpty(obj) { if (obj == null) throw new TypeError("Cannot destructure undefined"); }

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

var src = {};
var counter = 0;

for (var _i = 0, _arr = [{
  x: 1,
  y: 2
}]; _i < _arr.length; _i++) {
  var _ref = _arr[_i];
  var _ref2 = _ref;

  _objectDestructuringEmpty(_ref2);

  src.y = _extends({}, _ref2);
  _ref2;
  assert.sameValue(src.y.x, 1);
  assert.sameValue(src.y.y, 2);
  verifyProperty(src, "y", {
    enumerable: true,
    writable: true,
    configurable: true
  });
  counter += 1;
}

assert.sameValue(counter, 1);
```

### Actual behavior

SWC is craashing

### Version

v1.2.138

### Additional context

_No response_

Pull request:
fix(es/compat): bind value in DestructuringAssignmentTarget
<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**
<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
- fix #3524 

Patch:
diff --git a/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs b/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs
index 00384c3ab7..d08210dcc9 100644
--- a/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs
+++ b/crates/swc_ecma_transforms_compat/src/es2018/object_rest_spread.rs
@@ -57,6 +57,8 @@ macro_rules! impl_for_for_stmt {
                 return;
             }
 
+            let mut stmt = None;
+
             let left = match &mut for_stmt.left {
                 VarDeclOrPat::VarDecl(var_decl) => {
                     let ref_ident = private_ident!("_ref");
@@ -67,27 +69,29 @@ macro_rules! impl_for_for_stmt {
                         .take()
                         .into_iter()
                         .map(|decl| VarDeclarator {
-                            name: self.fold_rest(
-                                &mut 0,
-                                decl.name,
-                                Box::new(Expr::Ident(ref_ident.clone())),
-                                false,
-                                true,
-                            ),
+                            name: decl.name,
                             init: Some(Box::new(Expr::Ident(ref_ident.clone()))),
                             ..decl
                         })
                         .collect::<Vec<_>>();
-                    // .fold_with(self);
 
                     // **prepend** decls to self.vars
-                    decls.append(&mut self.vars);
-                    mem::swap(&mut self.vars, &mut decls);
+                    decls.append(&mut self.vars.take());
+
+                    stmt = Some(Stmt::Decl(
+                        VarDecl {
+                            span: DUMMY_SP,
+                            kind: VarDeclKind::Var,
+                            decls,
+                            declare: false,
+                        }
+                        .into(),
+                    ));
 
                     VarDeclOrPat::VarDecl(VarDecl {
                         decls: vec![VarDeclarator {
                             span: DUMMY_SP,
-                            name: ref_ident.clone().into(),
+                            name: ref_ident.into(),
                             init: None,
                             definite: false,
                         }],
@@ -96,18 +100,26 @@ macro_rules! impl_for_for_stmt {
                 }
                 VarDeclOrPat::Pat(pat) => {
                     let var_ident = private_ident!("_ref");
-                    let mut index = self.vars.len();
-                    let pat = self.fold_rest(
-                        &mut index,
-                        pat.take(),
-                        Box::new(Expr::Ident(var_ident.clone())),
-                        false,
-                        true,
-                    );
+                    let index = self.vars.len();
+                    let pat = pat.take();
 
                     // initialize (or destructure)
                     match pat {
                         Pat::Object(ObjectPat { ref props, .. }) if props.is_empty() => {}
+                        Pat::Object(ObjectPat { .. }) => {
+                            stmt = Some(Stmt::Expr(ExprStmt {
+                                span: DUMMY_SP,
+                                expr: Box::new(
+                                    AssignExpr {
+                                        span: DUMMY_SP,
+                                        op: op!("="),
+                                        left: PatOrExpr::Pat(Box::new(pat)),
+                                        right: Box::new(Expr::Ident(var_ident.clone())),
+                                    }
+                                    .into(),
+                                ),
+                            }));
+                        }
                         _ => {
                             // insert at index to create
                             // `var { a } = _ref, b = _objectWithoutProperties(_ref, ['a']);`
@@ -133,7 +145,7 @@ macro_rules! impl_for_for_stmt {
                         kind: VarDeclKind::Var,
                         decls: vec![VarDeclarator {
                             span: DUMMY_SP,
-                            name: var_ident.clone().into(),
+                            name: var_ident.into(),
                             init: None,
                             definite: false,
                         }],
@@ -143,24 +155,18 @@ macro_rules! impl_for_for_stmt {
             };
             for_stmt.left = left;
 
-            let stmt = Stmt::Decl(Decl::Var(VarDecl {
-                span: DUMMY_SP,
-                kind: VarDeclKind::Var,
-                decls: mem::take(&mut self.vars),
-                declare: false,
-            }));
-
-            for_stmt.body.visit_mut_with(self);
             for_stmt.body = Box::new(Stmt::Block(match &mut *for_stmt.body {
                 Stmt::Block(BlockStmt { span, stmts }) => BlockStmt {
                     span: *span,
-                    stmts: iter::once(stmt).chain(stmts.take()).collect(),
+                    stmts: stmt.into_iter().chain(stmts.take()).collect(),
                 },
                 body => BlockStmt {
                     span: DUMMY_SP,
-                    stmts: vec![stmt, body.take()],
+                    stmts: stmt.into_iter().chain(iter::once(body.take())).collect(),
                 },
             }));
+
+            for_stmt.body.visit_mut_with(self);
         }
     };
 }


Test patch:
diff --git a/crates/swc/tests/tsc-references/objectRestForOf_es2015.1.normal.js b/crates/swc/tests/tsc-references/objectRestForOf_es2015.1.normal.js
index a0139cd82b..8ca8d6ce77 100644
--- a/crates/swc/tests/tsc-references/objectRestForOf_es2015.1.normal.js
+++ b/crates/swc/tests/tsc-references/objectRestForOf_es2015.1.normal.js
@@ -67,9 +67,10 @@ for (let _ref of array){
 let xx;
 let rrestOff;
 for (var _ref1 of array){
-    var { x: xx  } = _ref1, rrestOff = _objectWithoutProperties(_ref1, [
+    var __ref;
+    __ref = _ref1, rrestOff = _objectWithoutProperties(__ref, [
         "x"
-    ]);
+    ]), ({ x: xx  } = __ref), __ref;
     [
         xx,
         rrestOff
diff --git a/crates/swc/tests/tsc-references/objectRestForOf_es2015.2.minified.js b/crates/swc/tests/tsc-references/objectRestForOf_es2015.2.minified.js
index f2ea592404..f9aad9efc8 100644
--- a/crates/swc/tests/tsc-references/objectRestForOf_es2015.2.minified.js
+++ b/crates/swc/tests/tsc-references/objectRestForOf_es2015.2.minified.js
@@ -23,17 +23,15 @@ function _objectWithoutPropertiesLoose(source, excluded) {
 }
 let array;
 for (let _ref of array){
-    var { x  } = _ref;
+    var __ref, { x  } = _ref;
     _objectWithoutProperties(_ref, [
         "x"
     ]);
 }
-for (var _ref1 of array){
-    var { x: xx  } = _ref1;
-    _objectWithoutProperties(_ref1, [
-        "x"
-    ]);
-}
+let xx;
+for (var _ref1 of array)_objectWithoutProperties(__ref = _ref1, [
+    "x"
+]), { x: xx  } = __ref;
 for (const norest of array.map((a)=>(function(target) {
         for(var i = 1; i < arguments.length; i++){
             var source = null != arguments[i] ? arguments[i] : {}, ownKeys = Object.keys(source);
diff --git a/crates/swc/tests/tsc-references/objectRestForOf_es5.1.normal.js b/crates/swc/tests/tsc-references/objectRestForOf_es5.1.normal.js
index 9c882bd8d1..8cc80e88ed 100644
--- a/crates/swc/tests/tsc-references/objectRestForOf_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/objectRestForOf_es5.1.normal.js
@@ -87,9 +87,11 @@ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorErro
 try {
     for(var _iterator1 = array[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
         var _ref1 = _step1.value;
-        var xx = _ref1.x, rrestOff = _objectWithoutProperties(_ref1, [
+        var __ref;
+        var ref;
+        __ref = _ref1, rrestOff = _objectWithoutProperties(__ref, [
             "x"
-        ]);
+        ]), ref = __ref, xx = ref.x, ref, __ref;
         [
             xx,
             rrestOff
diff --git a/crates/swc/tests/tsc-references/objectRestForOf_es5.2.minified.js b/crates/swc/tests/tsc-references/objectRestForOf_es5.2.minified.js
index d3f061fb5c..1dfe181d62 100644
--- a/crates/swc/tests/tsc-references/objectRestForOf_es5.2.minified.js
+++ b/crates/swc/tests/tsc-references/objectRestForOf_es5.2.minified.js
@@ -40,12 +40,9 @@ try {
 }
 var _iteratorNormalCompletion1 = !0, _didIteratorError1 = !1, _iteratorError1 = void 0;
 try {
-    for(var _step1, _iterator1 = array[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = !0){
-        var _ref1 = _step1.value;
-        _ref1.x, _objectWithoutProperties(_ref1, [
-            "x"
-        ]);
-    }
+    for(var __ref, _step1, _iterator1 = array[Symbol.iterator](); !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = !0)__ref = _step1.value, _objectWithoutProperties(__ref, [
+        "x"
+    ]), __ref.x;
 } catch (err) {
     _didIteratorError1 = !0, _iteratorError1 = err;
 } finally{
diff --git a/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs b/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs
index 33d4e06002..fcb4727682 100644
--- a/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs
+++ b/crates/swc_ecma_transforms_compat/tests/es2018_object_rest_spread.rs
@@ -281,18 +281,18 @@ for (var _ref of []) {
 }
 
 for (var _ref1 of []) {
-  var {
-    a
-  } = _ref1,
-      b = _objectWithoutProperties(_ref1, ["a"]);
+  var __ref;
+  __ref = _ref1, b = _objectWithoutProperties(__ref, [
+      "a"
+  ]), ({ a  } = __ref), __ref;
 }
 
 async function a() {
   for await (var _ref2 of []) {
-    var {
-      a
-    } = _ref2,
-        b = _objectWithoutProperties(_ref2, ["a"]);
+      var __ref;
+      __ref = _ref2, b = _objectWithoutProperties(__ref, [
+          "a"
+      ]), ({ a  } = __ref), __ref;
   }
 } // skip
 
@@ -3011,3 +3011,62 @@ const o2 = { ...o };
 expect(Array.isArray(Object.getPrototypeOf(o2))).toBe(true);
 "#
 );
+
+test!(
+    syntax(),
+    |_| tr(Config {
+        no_symbol: true,
+        set_property: true
+    }),
+    statements_for_of_dstr_obj_rest_to_property,
+    r#"
+    var src = {};
+
+    var counter = 0;
+    
+    for ({ ...src.y } of [{ x: 1, y: 2 }]) {
+        expect(src.y.x).toEqual(1);
+        expect(src.y.y).toEqual(2);
+
+        counter += 1;
+    }
+    
+    expect(counter).toEqual(1);
+"#,
+    r#"
+    var src = {};
+    var counter = 0;
+    for (var _ref of [{ x: 1, y: 2 }]) {
+        var __ref;
+        __ref = _ref, src.y = _extends({}, __ref), __ref;
+        expect(src.y.x).toEqual(1);
+        expect(src.y.y).toEqual(2);
+
+        counter += 1;
+    }
+    expect(counter).toEqual(1);
+  "#
+);
+
+test_exec!(
+    syntax(),
+    |_| tr(Config {
+        no_symbol: true,
+        set_property: true
+    }),
+    statements_for_of_dstr_obj_rest_to_property_exec,
+    r#"
+    var src = {};
+
+    var counter = 0;
+
+    for ({ ...src.y } of [{ x: 1, y: 2 }]) {
+        expect(src.y.x).toEqual(1);
+        expect(src.y.y).toEqual(2);
+
+        counter += 1;
+    }
+
+    expect(counter).toEqual(1);
+"#
+);