Repository: swc-project/swc (rust)

Issue:
TypeScript: `x < y >= z` is incorrectly interpreted as a type argument list
### Describe the bug

SWC's interpretation of type arguments in TypeScript code is different than the TypeScript compiler, which can cause SWC to generate incorrect JavaScript.


### Input code

```typescript
fn(x < y, x >= y)
```


### Config

```json
(I only ran SWC via the playground)
```


### Playground link

https://play.swc.rs/?version=1.3.57&code=H4sIAAAAAAAAA0vL06hQsFGo1FGoULCzVajU5AIAIT%2B1gxIAAAA%3D&config=H4sIAAAAAAAAA1VPOw7DIAzdOQXy3CHK0KF36CEQdSIqfsKOVBTl7oUE0maz38fveRVSwps0PORaxrJElQjTuReEsmf1KQhwjkg6mchw6yxTpSZlCXdoOxhglWbk6kIah3FoDrAhEHZHw5zxZsr%2FmTq4mJDoKqxS5WeL10TRUsGF17KT7Zfa92hwh5%2Boh52HwdCzOzktKLYvIO%2B2AhcBAAA%3D

### Expected behavior

SWC should produce this to match the TypeScript compiler:

```js
fn(x < y, x >= y);
```


### Actual behavior

SWC produces this instead, which is incorrect:

```js
fn(x = y);
```

### Version

1.3.57

### Additional context

Specifically TypeScript's `parseTypeArgumentsInExpression` function backtracks unless the trailing `>` comes from a `>` real token. This is subtle because TypeScript's type argument parser normally strips off the leading `>` from a token, which is why `>>` works in `a<b<c>>(d)`. But it turns out that should only be done when you're in a type context. When you're in an expression context, the trailing `>` must not come from a token that has anything else after it. So for example `a<b<c>>=(d)` should not be considered a type argument list because `parseTypeArgumentsInExpression` encounters a `>=` token.

I just fixed this bug in esbuild: https://github.com/evanw/esbuild/issues/3111. I'm reporting it here because SWC appears to also have this issue. The bug does not introduce a syntax error, which means the problematic output might be missed before releasing it, so I considered this bug to be higher severity.


Pull request:
fix(es/parser): Fix `>=` with type instantiate
<!-- 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:**

<!-- 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 #7387 

Patch:
diff --git a/crates/swc_ecma_parser/src/macros.rs b/crates/swc_ecma_parser/src/macros.rs
index dabf96cc3d..389c9018cb 100644
--- a/crates/swc_ecma_parser/src/macros.rs
+++ b/crates/swc_ecma_parser/src/macros.rs
@@ -102,7 +102,7 @@ macro_rules! tok {
         crate::token::Token::AssignOp(crate::token::AssignOpToken::DivAssign)
     };
     ('%') => {
-        crate::token::Token::BinOp(Mod)
+        crate::token::Token::BinOp(crate::token::BinOpToken::Mod)
     };
     ('~') => {
         crate::token::Token::Tilde
@@ -116,6 +116,9 @@ macro_rules! tok {
     (">>") => {
         crate::token::Token::BinOp(crate::token::BinOpToken::RShift)
     };
+    (">=") => {
+        crate::token::Token::BinOp(crate::token::BinOpToken::GtEq)
+    };
 
     ("++") => {
         crate::token::Token::PlusPlus
diff --git a/crates/swc_ecma_parser/src/parser/typescript.rs b/crates/swc_ecma_parser/src/parser/typescript.rs
index 790618a28e..a0cdb9683e 100644
--- a/crates/swc_ecma_parser/src/parser/typescript.rs
+++ b/crates/swc_ecma_parser/src/parser/typescript.rs
@@ -576,7 +576,7 @@ impl<I: Tokens> Parser<I> {
 
             if is_one_of!(
                 p, '<', // invalid syntax
-                '>', ">>", '+', '-', // becomes relational expression
+                '>', '=', ">>", ">=", '+', '-', // becomes relational expression
                 /* these should be type arguments in function call or template,
                  * not instantiation expression */
                 '(', '`'


Test patch:
diff --git a/crates/swc_ecma_parser/tests/typescript/issue-7387/input.ts b/crates/swc_ecma_parser/tests/typescript/issue-7387/input.ts
new file mode 100644
index 0000000000..ff3a04321c
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-7387/input.ts
@@ -0,0 +1,1 @@
+fn(x < y, x >= y1)
diff --git a/crates/swc_ecma_parser/tests/typescript/issue-7387/input.ts.json b/crates/swc_ecma_parser/tests/typescript/issue-7387/input.ts.json
new file mode 100644
index 0000000000..ea46ed1339
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-7387/input.ts.json
@@ -0,0 +1,104 @@
+{
+  "type": "Script",
+  "span": {
+    "start": 1,
+    "end": 19,
+    "ctxt": 0
+  },
+  "body": [
+    {
+      "type": "ExpressionStatement",
+      "span": {
+        "start": 1,
+        "end": 19,
+        "ctxt": 0
+      },
+      "expression": {
+        "type": "CallExpression",
+        "span": {
+          "start": 1,
+          "end": 19,
+          "ctxt": 0
+        },
+        "callee": {
+          "type": "Identifier",
+          "span": {
+            "start": 1,
+            "end": 3,
+            "ctxt": 0
+          },
+          "value": "fn",
+          "optional": false
+        },
+        "arguments": [
+          {
+            "spread": null,
+            "expression": {
+              "type": "BinaryExpression",
+              "span": {
+                "start": 4,
+                "end": 9,
+                "ctxt": 0
+              },
+              "operator": "<",
+              "left": {
+                "type": "Identifier",
+                "span": {
+                  "start": 4,
+                  "end": 5,
+                  "ctxt": 0
+                },
+                "value": "x",
+                "optional": false
+              },
+              "right": {
+                "type": "Identifier",
+                "span": {
+                  "start": 8,
+                  "end": 9,
+                  "ctxt": 0
+                },
+                "value": "y",
+                "optional": false
+              }
+            }
+          },
+          {
+            "spread": null,
+            "expression": {
+              "type": "BinaryExpression",
+              "span": {
+                "start": 11,
+                "end": 18,
+                "ctxt": 0
+              },
+              "operator": ">=",
+              "left": {
+                "type": "Identifier",
+                "span": {
+                  "start": 11,
+                  "end": 12,
+                  "ctxt": 0
+                },
+                "value": "x",
+                "optional": false
+              },
+              "right": {
+                "type": "Identifier",
+                "span": {
+                  "start": 16,
+                  "end": 18,
+                  "ctxt": 0
+                },
+                "value": "y1",
+                "optional": false
+              }
+            }
+          }
+        ],
+        "typeArguments": null
+      }
+    }
+  ],
+  "interpreter": null
+}