Repository: swc-project/swc (rust)

Issue:
`delete` used with parentheses always incorrectly leads to TS2703
### Describe the bug

```typescript
let foo: { bar?: number } = { bar: 123 }
delete (foo.bar);
```

The above codes [worked in tsc](https://www.typescriptlang.org/play?#code/DYUwLgBAZg9jBcEDeEBGBDATgfkQOwFcBbVETCAXwgF5k0tEBGAJgGZKAoAExFDBAgAKWDAB0GTAEoA3EA), but [got the following output in swc](https://play.swc.rs/?version=1.2.247&code=H4sIAAAAAAAAA8tJLVFIy8%2B3UqhWSEossrdSyCvNTUotUqhVsIUIWSkYGhkr1HKlpOaklqQqaAAV6wGFNa0BJpypLzoAAAA%3D&config=H4sIAAAAAAAAA0WNSwrDMAxE76J1tt3kDj2ESZXg4B8aBWqM7165OGQnzedNoxMbrY2KE7CMCzWp%2B9JKWgtjE1%2BUFlKYtLsA7vY4OVgtwniZF3IGT3eh6JPf6yBtORZh4LFcOsKd7AaK%2BXMNof3HDGiVmNMJ6g9olj3eM61ycf8BnwbyVboAAAA%3D):

```
  x The operand of a delete operator must be a property reference.
   ,----
 2 | delete (foo.bar);
   :         ^^^^^^^
   `----


Caused by:
    0: failed to parse code
    1: error was recoverable, but proceeding would result in wrong codegen
    2: Syntax Error
```

### Input code

_No response_

### Config

_No response_

### Playground link

https://play.swc.rs/?version=1.2.247&code=H4sIAAAAAAAAA8tJLVFIy8%2B3UqhWSEossrdSyCvNTUotUqhVsIUIWSkYGhkr1HKlpOaklqQqaAAV6wGFNa0BJpypLzoAAAA%3D&config=H4sIAAAAAAAAA0WNSwrDMAxE76J1tt3kDj2ESZXg4B8aBWqM7165OGQnzedNoxMbrY2KE7CMCzWp%2B9JKWgtjE1%2BUFlKYtLsA7vY4OVgtwniZF3IGT3eh6JPf6yBtORZh4LFcOsKd7AaK%2BXMNof3HDGiVmNMJ6g9olj3eM61ycf8BnwbyVboAAAA%3D

### Expected behavior

Should not report any errors.

### Actual behavior

_No response_

### Version

1.2.247

### Additional context

I found that the TS2703 error is thrown by `swc_ecma_parser/src/parser/expr/ops.rs`:

```rust
match &*arg {
    Expr::Member(..) => {}
    Expr::OptChain(OptChainExpr {
        base: OptChainBase::Member(..),
        ..
    }) => {}
    _ => self.emit_err(unwrap_paren(&arg).span(), SyntaxError::TS2703),
}
```

Here the branches does not take `Paren`s into account.

Would love to open a PR for this if it is confirmed as a bug!

Pull request description:
fix(ecma_parser): 🐛 #5752
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**
1. Fixing parser issue about `delete valid parenthesis expr`
<!-- 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):**
Closes #5752

Patch:
diff --git a/crates/swc_ecma_ast/src/expr.rs b/crates/swc_ecma_ast/src/expr.rs
index 30470d815c..c085f795f6 100644
--- a/crates/swc_ecma_ast/src/expr.rs
+++ b/crates/swc_ecma_ast/src/expr.rs
@@ -172,10 +172,11 @@ impl Expr {
     ///
     /// If `self` is not a parenthesized expression, it will be returned as is.
     pub fn unwrap_parens(&self) -> &Expr {
-        match self {
-            Expr::Paren(ParenExpr { expr, .. }) => expr.unwrap_parens(),
-            _ => self,
+        let mut cur = self;
+        while let Expr::Paren(ref expr) = cur {
+            cur = &expr.expr;
         }
+        cur
     }
 
     /// Normalize parenthesized expressions.
@@ -184,10 +185,11 @@ impl Expr {
     ///
     /// If `self` is not a parenthesized expression, it will be returned as is.
     pub fn unwrap_parens_mut(&mut self) -> &mut Expr {
-        match self {
-            Expr::Paren(ParenExpr { expr, .. }) => expr.unwrap_parens_mut(),
-            _ => self,
+        let mut cur = self;
+        while let Expr::Paren(ref mut expr) = cur {
+            cur = &mut expr.expr;
         }
+        cur
     }
 
     /// Creates an expression from `exprs`. This will return first element if
diff --git a/crates/swc_ecma_parser/src/parser/expr/ops.rs b/crates/swc_ecma_parser/src/parser/expr/ops.rs
index e19ebeac26..417e40daf1 100644
--- a/crates/swc_ecma_parser/src/parser/expr/ops.rs
+++ b/crates/swc_ecma_parser/src/parser/expr/ops.rs
@@ -294,19 +294,20 @@ impl<I: Tokens> Parser<I> {
             }
 
             if self.input.syntax().typescript() && op == op!("delete") {
-                fn unwrap_paren(e: &Expr) -> &Expr {
-                    match *e {
-                        Expr::Paren(ref p) => unwrap_paren(&p.expr),
-                        _ => e,
+                fn is_valid_arg(e: &Expr) -> bool {
+                    match e {
+                        Expr::Member(..)
+                        | Expr::OptChain(OptChainExpr {
+                            base: OptChainBase::Member(..),
+                            ..
+                        }) => true,
+                        Expr::Paren(expr) => is_valid_arg(expr.expr.unwrap_parens()),
+                        _ => false,
                     }
                 }
-                match &*arg {
-                    Expr::Member(..) => {}
-                    Expr::OptChain(OptChainExpr {
-                        base: OptChainBase::Member(..),
-                        ..
-                    }) => {}
-                    _ => self.emit_err(unwrap_paren(&arg).span(), SyntaxError::TS2703),
+
+                if !is_valid_arg(&arg) {
+                    self.emit_err(arg.unwrap_parens().span(), SyntaxError::TS2703);
                 }
             }
 


Test patch:
diff --git a/crates/swc/tests/tsc-references/deleteChain.1.normal.js b/crates/swc/tests/tsc-references/deleteChain.1.normal.js
index 5b46be8748..b65d0770fa 100644
--- a/crates/swc/tests/tsc-references/deleteChain.1.normal.js
+++ b/crates/swc/tests/tsc-references/deleteChain.1.normal.js
@@ -1,37 +1,15 @@
 //// [deleteChain.ts]
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!    ,----
-//!  4 | delete (o1?.b);
-//!    :         ^^^^^
-//!    `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!    ,----
-//!  8 | delete (o2?.b.c);
-//!    :         ^^^^^^^
-//!    `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  12 | delete (o3.b?.c);
-//!     :         ^^^^^^^
-//!     `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  17 | delete (o4.b?.c.d?.e);
-//!     :         ^^^^^^^^^^^^
-//!     `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  21 | delete (o5.b?.().c.d?.e);
-//!     :         ^^^^^^^^^^^^^^^
-//!     `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  25 | delete (o6.b?.['c'].d?.['e']);
-//!     :         ^^^^^^^^^^^^^^^^^^^^
-//!     `----
+var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, ref12, ref13, ref14, ref15;
+o1 === null || o1 === void 0 ? void 0 : delete o1.b;
+delete (o1 === null || o1 === void 0 ? void 0 : o1.b);
+o2 === null || o2 === void 0 ? void 0 : delete o2.b.c;
+delete (o2 === null || o2 === void 0 ? void 0 : o2.b.c);
+(ref = o3.b) === null || ref === void 0 ? void 0 : delete ref.c;
+delete ((ref1 = o3.b) === null || ref1 === void 0 ? void 0 : ref1.c);
+(ref3 = (ref2 = o4.b) === null || ref2 === void 0 ? void 0 : ref2.c.d) === null || ref3 === void 0 ? void 0 : delete ref3.e;
+(ref5 = (ref4 = o4.b) === null || ref4 === void 0 ? void 0 : ref4.c.d) === null || ref5 === void 0 ? void 0 : delete ref5.e;
+delete ((ref7 = (ref6 = o4.b) === null || ref6 === void 0 ? void 0 : ref6.c.d) === null || ref7 === void 0 ? void 0 : ref7.e);
+(ref9 = (ref8 = o5.b) === null || ref8 === void 0 ? void 0 : ref8.call(o5).c.d) === null || ref9 === void 0 ? void 0 : delete ref9.e;
+delete ((ref11 = (ref10 = o5.b) === null || ref10 === void 0 ? void 0 : ref10.call(o5).c.d) === null || ref11 === void 0 ? void 0 : ref11.e);
+(ref13 = (ref12 = o6.b) === null || ref12 === void 0 ? void 0 : ref12["c"].d) === null || ref13 === void 0 ? void 0 : delete ref13["e"];
+delete ((ref15 = (ref14 = o6.b) === null || ref14 === void 0 ? void 0 : ref14["c"].d) === null || ref15 === void 0 ? void 0 : ref15["e"]);
diff --git a/crates/swc/tests/tsc-references/deleteChain.2.minified.js b/crates/swc/tests/tsc-references/deleteChain.2.minified.js
index 5b46be8748..cb841fa722 100644
--- a/crates/swc/tests/tsc-references/deleteChain.2.minified.js
+++ b/crates/swc/tests/tsc-references/deleteChain.2.minified.js
@@ -1,37 +1,3 @@
 //// [deleteChain.ts]
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!    ,----
-//!  4 | delete (o1?.b);
-//!    :         ^^^^^
-//!    `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!    ,----
-//!  8 | delete (o2?.b.c);
-//!    :         ^^^^^^^
-//!    `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  12 | delete (o3.b?.c);
-//!     :         ^^^^^^^
-//!     `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  17 | delete (o4.b?.c.d?.e);
-//!     :         ^^^^^^^^^^^^
-//!     `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  21 | delete (o5.b?.().c.d?.e);
-//!     :         ^^^^^^^^^^^^^^^
-//!     `----
-//! 
-//!   x The operand of a delete operator must be a property reference.
-//!     ,----
-//!  25 | delete (o6.b?.['c'].d?.['e']);
-//!     :         ^^^^^^^^^^^^^^^^^^^^
-//!     `----
+var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, ref10, ref11, ref12, ref13, ref14, ref15;
+null == o1 || delete o1.b, delete (null == o1 ? void 0 : o1.b), null == o2 || delete o2.b.c, delete (null == o2 ? void 0 : o2.b.c), null === (ref = o3.b) || void 0 === ref || delete ref.c, delete (null === (ref1 = o3.b) || void 0 === ref1 ? void 0 : ref1.c), null === (ref3 = null === (ref2 = o4.b) || void 0 === ref2 ? void 0 : ref2.c.d) || void 0 === ref3 || delete ref3.e, null === (ref5 = null === (ref4 = o4.b) || void 0 === ref4 ? void 0 : ref4.c.d) || void 0 === ref5 || delete ref5.e, delete (null === (ref7 = null === (ref6 = o4.b) || void 0 === ref6 ? void 0 : ref6.c.d) || void 0 === ref7 ? void 0 : ref7.e), null === (ref9 = null === (ref8 = o5.b) || void 0 === ref8 ? void 0 : ref8.call(o5).c.d) || void 0 === ref9 || delete ref9.e, delete (null === (ref11 = null === (ref10 = o5.b) || void 0 === ref10 ? void 0 : ref10.call(o5).c.d) || void 0 === ref11 ? void 0 : ref11.e), null === (ref13 = null === (ref12 = o6.b) || void 0 === ref12 ? void 0 : ref12.c.d) || void 0 === ref13 || delete ref13.e, delete (null === (ref15 = null === (ref14 = o6.b) || void 0 === ref14 ? void 0 : ref14.c.d) || void 0 === ref15 ? void 0 : ref15.e);