Repository: swc-project/swc (rust)

Issue:
TypeScript cast with comment breaks return evaluation
### Describe the bug

Adding a comment to a parantheses wrapped return evaluation with embedded comment ends up stripping the wrapping parantheses and breaking the return evaluation.

### Input code

```typescript
const string1 = { errorCode: 'test' };
const string2 = 'test';

function evaluateWithoutComment() {
  return string1.errorCode === string2;
}

function evaluateWithComment() {
  return ( 
    // This comment doesn't break return evaluation
    string1?.errorCode === string2
  );
}

function evaluateWithCast() {
  return (
    (string1 as { errorCode: string })?.errorCode === string2
  );
}

function evaluateWithCastAndComment() {
  return (
    // This comment seems to cause wrapping parantheses to get stripped, breaking the return evaluation
    (string1 as { errorCode: string })?.errorCode === string2
  );
}

// Works, returns true
console.log(`evaluateWithoutComment: ${evaluateWithoutComment()}`);
// Works, returns true
console.log(`evaluateWithComment: ${evaluateWithComment()}`);
// Works, returns true
console.log(`evaluateWithCast: ${evaluateWithCast()}`);
// Breaks, returns undefined due to stripped parantheses
console.log(`evaluateWithCastAndComment: ${evaluateWithCastAndComment()}`);
```


### Config

```json
{
  "jsc": {
    "transform": {
      "decoratorVersion": "2022-03"
    },
    "parser": {
      "syntax": "typescript",
      "tsx": false
    },
    "target": "es2021",
    "loose": false,
  },
  "module": {
    "type": "commonjs"
  },
  "isModule": true,
  "minify": false
}
```


### Playground link

https://play.swc.rs/?version=1.3.89&code=H4sIAAAAAAAAA6WSwW6DMBBE73zFHioBUpSoPQahqs0vVMo1Lt4ACtjIXrcHxL%2FXGNzS1kRqc7Ps4e0wO4UUmkCTqkV5Dzn0gEpJdZAc9xATaophyKJiIXuwsukli6KzEQXVUgC%2BscYwwmNNlTR0kG2LgpIU%2BghAIRkl%2FJjt5wjI89xTs2hYwQVZCdgzwG4HL1WtoZg0wCVqERO8KmQXr51hluu%2BmW08hn1YSXrFC9M%2FjThm4iNk%2BnuG0z0M6b%2FHPQkeTiAYgEZsNZCEghmN8K5Y140GOqaYoAo1utcSp3V2HfLNlNaosoKV0G7%2FQev0KNVFb%2BYJ1ocy6KolG9w2skxO4Rbt4a5f69dwsvS%2Fole4N0Ltrn4RXV087nmMecEzguO5FsiBGxy34jey3Nb1gV%2FlCI1eVseZ%2BADOKfkp7wMAAA%3D%3D&config=H4sIAAAAAAAAA1WPPQ6DMAyF954CeaYSpVvv0LV7FEwVROLINlIR4u5NoEF0s997n3%2BWS1XBIBYe1ZLK1CibID2xP6QkdmiJjRK%2FkMVRSB60TdtemztsmbXe6WhYkM%2BozEHNJwM6RxTLLirUxVXJVm9Gwb85aviNmimUtOj2I2AkEixE1jYAPHXTiKcn0qoMW%2FKewiBwJJ08S1Z5wp12wfXzccf6BfgI7zMVAQAA

### SWC Info output

_No response_

### Expected behavior

The wrapped parantheses are preserved so that the return evaluation evaulates to `true` similar to the other cases.

### Actual behavior

As can be seen in `evaluateWithCastAndComment`, the stripped parantheses end up returning `undefined`.

### Version

1.3.89

### Additional context

_No response_

Pull request:
fix(es/codegen): Fix codegen of optional chaining expr with a comment
**Related issue:**

 - Closes #8004

Patch:
diff --git a/crates/swc_ecma_codegen/src/lib.rs b/crates/swc_ecma_codegen/src/lib.rs
index 8feeeab393..ad22b1dfc8 100644
--- a/crates/swc_ecma_codegen/src/lib.rs
+++ b/crates/swc_ecma_codegen/src/lib.rs
@@ -2941,6 +2941,19 @@ where
                 }
             }
 
+            Expr::OptChain(e) => match &*e.base {
+                OptChainBase::Member(m) => {
+                    if self.has_leading_comment(&m.obj) {
+                        return true;
+                    }
+                }
+                OptChainBase::Call(c) => {
+                    if self.has_leading_comment(&c.callee) {
+                        return true;
+                    }
+                }
+            },
+
             _ => {}
         }
 


Test patch:
diff --git a/crates/swc/tests/fixture/issues-8xxx/8004/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8004/input/.swcrc
new file mode 100644
index 0000000000..de3cf983ec
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8004/input/.swcrc
@@ -0,0 +1,18 @@
+{
+    "jsc": {
+        "transform": {
+            "decoratorVersion": "2022-03"
+        },
+        "parser": {
+            "syntax": "typescript",
+            "tsx": false
+        },
+        "target": "es2021",
+        "loose": false,
+    },
+    "module": {
+        "type": "commonjs"
+    },
+    "isModule": true,
+    "minify": false
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8004/input/1.js b/crates/swc/tests/fixture/issues-8xxx/8004/input/1.js
new file mode 100644
index 0000000000..f9976fa5aa
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8004/input/1.js
@@ -0,0 +1,35 @@
+const string1 = { errorCode: 'test' };
+const string2 = 'test';
+
+function evaluateWithoutComment() {
+    return string1.errorCode === string2;
+}
+
+function evaluateWithComment() {
+    return (
+        // This comment doesn't break return evaluation
+        string1?.errorCode === string2
+    );
+}
+
+function evaluateWithCast() {
+    return (
+        (string1 as { errorCode: string })?.errorCode === string2
+    );
+}
+
+function evaluateWithCastAndComment() {
+    return (
+        // This comment seems to cause wrapping parantheses to get stripped, breaking the return evaluation
+        (string1 as { errorCode: string })?.errorCode === string2
+    );
+}
+
+// Works, returns true
+console.log(`evaluateWithoutComment: ${evaluateWithoutComment()}`);
+// Works, returns true
+console.log(`evaluateWithComment: ${evaluateWithComment()}`);
+// Works, returns true
+console.log(`evaluateWithCast: ${evaluateWithCast()}`);
+// Breaks, returns undefined due to stripped parantheses
+console.log(`evaluateWithCastAndComment: ${evaluateWithCastAndComment()}`);
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-8xxx/8004/output/1.js b/crates/swc/tests/fixture/issues-8xxx/8004/output/1.js
new file mode 100644
index 0000000000..7081ed7ec1
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-8xxx/8004/output/1.js
@@ -0,0 +1,27 @@
+"use strict";
+const string1 = {
+    errorCode: 'test'
+};
+const string2 = 'test';
+function evaluateWithoutComment() {
+    return string1.errorCode === string2;
+}
+function evaluateWithComment() {
+    return(// This comment doesn't break return evaluation
+    string1?.errorCode === string2);
+}
+function evaluateWithCast() {
+    return string1?.errorCode === string2;
+}
+function evaluateWithCastAndComment() {
+    return(// This comment seems to cause wrapping parantheses to get stripped, breaking the return evaluation
+    string1?.errorCode === string2);
+}
+// Works, returns true
+console.log(`evaluateWithoutComment: ${evaluateWithoutComment()}`);
+// Works, returns true
+console.log(`evaluateWithComment: ${evaluateWithComment()}`);
+// Works, returns true
+console.log(`evaluateWithCast: ${evaluateWithCast()}`);
+// Breaks, returns undefined due to stripped parantheses
+console.log(`evaluateWithCastAndComment: ${evaluateWithCastAndComment()}`);