Repository: swc-project/swc (rust)

Issue:
Parser error for type parameters containing a generic function signature
### Describe the bug

This code appears in a d.ts file on DefinitelyTyped, but fails to parse.

### Input code

```typescript
assertType<<K>(key: K) => K>(foo);
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true
    },
    "target": "es2022",
    "transform": {
      "useDefineForClassFields": true,
      "react": {
        "importSource": "https://esm.sh/preact",
        "runtime": "automatic"
      }
    },
    "loose": false
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true
}
```


### Playground link

https://play.swc.rs/?version=1.3.44&code=H4sIAAAAAAAAA0ssLk4tKgmpLEi1sfG208hOrbRS8NZUsLVTAPLS8vM1rQGKcDutIgAAAA%3D%3D&config=H4sIAAAAAAAAA1WQvW7DMAyE9z6FoTmoCw8ZsrbIlilPQCh0w0J%2FICkgRuB3jyTHcbqJ3%2FF04N0%2Fus78iTWH7l6eZUjAgvyaC5EpKNwKMTolFMuU1OxWVaVKyhkbmRfBKPAvajWhDF%2FDYFbOEGSM7N8TsuAPjhTwGPnbgciR0F3k%2Be8rihGsvvkKIp8i6zlmtljDrqpJDn2P4j%2Fl2qfFstsMnIOSb7uQNXpQsuYpz%2F8ucDFK3RvBSb2tcePjJTvc6qqVLFfuzbZEgcZp9TZEclqdrav5AeQIxLB6AQAA

### Expected behavior

Parses.

### Actual behavior

Doesn't parse!

### Version

1.3.44

### Additional context

_No response_

Pull request:
fix(es/parser): Re-lex `<<` as two `<`-s if required
**Description:**


**Related issue:**

 - Closes #7187.

Patch:
diff --git a/crates/swc_ecma_parser/src/lexer/state.rs b/crates/swc_ecma_parser/src/lexer/state.rs
index c632be1df7..114f53e8d7 100644
--- a/crates/swc_ecma_parser/src/lexer/state.rs
+++ b/crates/swc_ecma_parser/src/lexer/state.rs
@@ -189,6 +189,10 @@ impl Tokens for Lexer<'_> {
     fn take_errors(&mut self) -> Vec<Error> {
         take(&mut self.errors.borrow_mut())
     }
+
+    fn reset_to(&mut self, to: BytePos) {
+        self.input.reset_to(to);
+    }
 }
 
 impl<'a> Iterator for Lexer<'a> {
diff --git a/crates/swc_ecma_parser/src/macros.rs b/crates/swc_ecma_parser/src/macros.rs
index 389c9018cb..acbba5b275 100644
--- a/crates/swc_ecma_parser/src/macros.rs
+++ b/crates/swc_ecma_parser/src/macros.rs
@@ -113,6 +113,9 @@ macro_rules! tok {
     ('>') => {
         crate::token::Token::BinOp(crate::token::BinOpToken::Gt)
     };
+    ("<<") => {
+        crate::token::Token::BinOp(crate::token::BinOpToken::LShift)
+    };
     (">>") => {
         crate::token::Token::BinOp(crate::token::BinOpToken::RShift)
     };
diff --git a/crates/swc_ecma_parser/src/parser/expr.rs b/crates/swc_ecma_parser/src/parser/expr.rs
index f6d6d4dfac..2afbb67fe0 100644
--- a/crates/swc_ecma_parser/src/parser/expr.rs
+++ b/crates/swc_ecma_parser/src/parser/expr.rs
@@ -688,11 +688,12 @@ impl<I: Tokens> Parser<I> {
         let obj = self.parse_primary_expr()?;
         return_if_arrow!(self, obj);
 
-        let type_args = if self.syntax().typescript() && is!(self, '<') {
+        let type_args = if self.syntax().typescript() {
             self.try_parse_ts_type_args()
         } else {
             None
         };
+
         let obj = if let Some(type_args) = type_args {
             trace_cur!(self, parse_member_expr_or_new_expr__with_type_args);
             Box::new(Expr::TsInstantiation(TsInstantiation {
@@ -1620,9 +1621,27 @@ impl<I: Tokens> Parser<I> {
         let callee = self.parse_new_expr()?;
         return_if_arrow!(self, callee);
 
-        let type_args = if self.input.syntax().typescript() && is!(self, '<') {
+        let type_args = if self.input.syntax().typescript() && is_one_of!(self, '<', "<<") {
+            let type_args_start = self.input.cur_pos();
             self.try_parse_ts(|p| {
+                trace_cur!(p, parse_lhs_expr__type_args);
+
+                if is!(p, "<<") {
+                    let ctx = Context {
+                        should_not_lex_lt_or_gt_as_type: false,
+                        in_type: true,
+                        ..p.ctx()
+                    };
+                    p.input.reset_to(type_args_start);
+                    p.input.set_ctx(ctx);
+                }
+
+                trace_cur!(p, parse_lhs_expr__before_type_args);
+
                 let type_args = p.parse_ts_type_args()?;
+
+                trace_cur!(p, parse_lhs_expr__after_type_args);
+
                 if is!(p, '(') {
                     Ok(Some(type_args))
                 } else {
diff --git a/crates/swc_ecma_parser/src/parser/input.rs b/crates/swc_ecma_parser/src/parser/input.rs
index e0c46b735f..9f6ed2def4 100644
--- a/crates/swc_ecma_parser/src/parser/input.rs
+++ b/crates/swc_ecma_parser/src/parser/input.rs
@@ -46,6 +46,8 @@ pub trait Tokens: Clone + Iterator<Item = TokenAndSpan> {
     fn add_module_mode_error(&self, error: Error);
 
     fn take_errors(&mut self) -> Vec<Error>;
+
+    fn reset_to(&mut self, to: BytePos);
 }
 
 #[derive(Clone)]
@@ -141,6 +143,8 @@ impl Tokens for TokensInput {
     fn take_errors(&mut self) -> Vec<Error> {
         take(&mut self.errors.borrow_mut())
     }
+
+    fn reset_to(&mut self, _: BytePos) {}
 }
 
 /// Note: Lexer need access to parser's context to lex correctly.
@@ -253,6 +257,10 @@ impl<I: Tokens> Tokens for Capturing<I> {
     fn take_errors(&mut self) -> Vec<Error> {
         self.inner.take_errors()
     }
+
+    fn reset_to(&mut self, to: BytePos) {
+        self.inner.reset_to(to);
+    }
 }
 
 /// This struct is responsible for managing current token and peeked token.
@@ -318,9 +326,9 @@ impl<I: Tokens> Buffer<I> {
 
     #[cold]
     #[inline(never)]
-    pub fn dump_cur(&mut self) -> String {
-        match self.cur() {
-            Some(v) => format!("{:?}", v),
+    pub fn dump_cur(&self) -> String {
+        match &self.cur {
+            Some(v) => format!("{:?}", v.token),
             None => "<eof>".to_string(),
         }
     }
@@ -494,4 +502,10 @@ impl<I: Tokens> Buffer<I> {
     pub(crate) fn set_token_context(&mut self, c: lexer::TokenContexts) {
         self.iter.set_token_context(c)
     }
+
+    #[inline]
+    pub(crate) fn reset_to(&mut self, to: BytePos) {
+        self.cur = None;
+        self.iter.reset_to(to)
+    }
 }
diff --git a/crates/swc_ecma_parser/src/parser/typescript.rs b/crates/swc_ecma_parser/src/parser/typescript.rs
index 790642b928..8d58c07d5d 100644
--- a/crates/swc_ecma_parser/src/parser/typescript.rs
+++ b/crates/swc_ecma_parser/src/parser/typescript.rs
@@ -461,7 +461,9 @@ impl<I: Tokens> Parser<I> {
         permit_in_out: bool,
         permit_const: bool,
     ) -> PResult<Box<TsTypeParamDecl>> {
-        self.in_type().parse_with(|p| {
+        trace_cur!(self, parse_ts_type_params);
+
+        let ret = self.in_type().parse_with(|p| {
             p.ts_in_no_context(|p| {
                 let start = cur_pos!(p);
 
@@ -483,7 +485,11 @@ impl<I: Tokens> Parser<I> {
                     params,
                 }))
             })
-        })
+        })?;
+
+        trace_cur!(self, parse_ts_type_params__end);
+
+        Ok(ret)
     }
 
     /// `tsParseTypeOrTypePredicateAnnotation`
@@ -579,11 +585,25 @@ impl<I: Tokens> Parser<I> {
 
     #[cfg_attr(feature = "debug", tracing::instrument(skip_all))]
     pub(super) fn try_parse_ts_type_args(&mut self) -> Option<Box<TsTypeParamInstantiation>> {
+        let start = self.input.cur_pos();
+
         trace_cur!(self, try_parse_ts_type_args);
         debug_assert!(self.input.syntax().typescript());
 
         self.try_parse_ts(|p| {
+            let nested = is!(p, "<<");
+            if nested {
+                let ctx = Context {
+                    should_not_lex_lt_or_gt_as_type: false,
+                    in_type: true,
+                    ..p.ctx()
+                };
+                p.input.reset_to(start);
+                p.input.set_ctx(ctx);
+            }
+
             let type_args = p.parse_ts_type_args()?;
+            trace_cur!(p, try_parse_ts_type_args__after_type_args);
 
             if is_one_of!(
                 p, '<', // invalid syntax
@@ -1845,9 +1865,12 @@ impl<I: Tokens> Parser<I> {
 
         // ----- inlined `self.tsFillSignature(tt.arrow, node)`
         let type_params = self.try_parse_ts_type_params(false, true)?;
+        trace_cur!(self, parse_ts_fn_or_constructor_type__after_type_params);
         expect!(self, '(');
         let params = self.parse_ts_binding_list_for_signature()?;
+        trace_cur!(self, parse_ts_fn_or_constructor_type__after_params);
         let type_ann = self.parse_ts_type_or_type_predicate_ann(&tok!("=>"))?;
+        trace_cur!(self, parse_ts_fn_or_constructor_type__after_type_ann);
         // ----- end
 
         Ok(if is_fn_type {
@@ -2015,6 +2038,8 @@ impl<I: Tokens> Parser<I> {
         permit_in_out: bool,
         permit_const: bool,
     ) -> PResult<Option<Box<TsTypeParamDecl>>> {
+        trace_cur!(self, try_parse_ts_type_params);
+
         if !cfg!(feature = "typescript") {
             return Ok(None);
         }


Test patch:
diff --git a/crates/swc_ecma_parser/tests/typescript/issue-7187/1/input.tsx b/crates/swc_ecma_parser/tests/typescript/issue-7187/1/input.tsx
new file mode 100644
index 0000000000..ce3e41b9ef
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-7187/1/input.tsx
@@ -0,0 +1,1 @@
+assertType<<K>(key: K) => K>(foo);
diff --git a/crates/swc_ecma_parser/tests/typescript/issue-7187/1/input.tsx.json b/crates/swc_ecma_parser/tests/typescript/issue-7187/1/input.tsx.json
new file mode 100644
index 0000000000..ae027a7231
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-7187/1/input.tsx.json
@@ -0,0 +1,169 @@
+{
+  "type": "Script",
+  "span": {
+    "start": 1,
+    "end": 35,
+    "ctxt": 0
+  },
+  "body": [
+    {
+      "type": "ExpressionStatement",
+      "span": {
+        "start": 1,
+        "end": 35,
+        "ctxt": 0
+      },
+      "expression": {
+        "type": "CallExpression",
+        "span": {
+          "start": 1,
+          "end": 34,
+          "ctxt": 0
+        },
+        "callee": {
+          "type": "Identifier",
+          "span": {
+            "start": 1,
+            "end": 11,
+            "ctxt": 0
+          },
+          "value": "assertType",
+          "optional": false
+        },
+        "arguments": [
+          {
+            "spread": null,
+            "expression": {
+              "type": "Identifier",
+              "span": {
+                "start": 30,
+                "end": 33,
+                "ctxt": 0
+              },
+              "value": "foo",
+              "optional": false
+            }
+          }
+        ],
+        "typeArguments": {
+          "type": "TsTypeParameterInstantiation",
+          "span": {
+            "start": 11,
+            "end": 29,
+            "ctxt": 0
+          },
+          "params": [
+            {
+              "type": "TsFunctionType",
+              "span": {
+                "start": 12,
+                "end": 28,
+                "ctxt": 0
+              },
+              "params": [
+                {
+                  "type": "Identifier",
+                  "span": {
+                    "start": 16,
+                    "end": 22,
+                    "ctxt": 0
+                  },
+                  "value": "key",
+                  "optional": false,
+                  "typeAnnotation": {
+                    "type": "TsTypeAnnotation",
+                    "span": {
+                      "start": 19,
+                      "end": 22,
+                      "ctxt": 0
+                    },
+                    "typeAnnotation": {
+                      "type": "TsTypeReference",
+                      "span": {
+                        "start": 21,
+                        "end": 22,
+                        "ctxt": 0
+                      },
+                      "typeName": {
+                        "type": "Identifier",
+                        "span": {
+                          "start": 21,
+                          "end": 22,
+                          "ctxt": 0
+                        },
+                        "value": "K",
+                        "optional": false
+                      },
+                      "typeParams": null
+                    }
+                  }
+                }
+              ],
+              "typeParams": {
+                "type": "TsTypeParameterDeclaration",
+                "span": {
+                  "start": 12,
+                  "end": 15,
+                  "ctxt": 0
+                },
+                "parameters": [
+                  {
+                    "type": "TsTypeParameter",
+                    "span": {
+                      "start": 13,
+                      "end": 14,
+                      "ctxt": 0
+                    },
+                    "name": {
+                      "type": "Identifier",
+                      "span": {
+                        "start": 13,
+                        "end": 14,
+                        "ctxt": 0
+                      },
+                      "value": "K",
+                      "optional": false
+                    },
+                    "in": false,
+                    "out": false,
+                    "const": false,
+                    "constraint": null,
+                    "default": null
+                  }
+                ]
+              },
+              "typeAnnotation": {
+                "type": "TsTypeAnnotation",
+                "span": {
+                  "start": 24,
+                  "end": 28,
+                  "ctxt": 0
+                },
+                "typeAnnotation": {
+                  "type": "TsTypeReference",
+                  "span": {
+                    "start": 27,
+                    "end": 28,
+                    "ctxt": 0
+                  },
+                  "typeName": {
+                    "type": "Identifier",
+                    "span": {
+                      "start": 27,
+                      "end": 28,
+                      "ctxt": 0
+                    },
+                    "value": "K",
+                    "optional": false
+                  },
+                  "typeParams": null
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "interpreter": null
+}