diff --git a/crates/swc/tests/fixture/issues-6xxx/6739/input/.swcrc b/crates/swc/tests/fixture/issues-6xxx/6739/input/.swcrc new file mode 100644 index 000000000000..9381cfabfff4 --- /dev/null +++ b/crates/swc/tests/fixture/issues-6xxx/6739/input/.swcrc @@ -0,0 +1,8 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": false + } + } +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-6xxx/6739/input/index.ts b/crates/swc/tests/fixture/issues-6xxx/6739/input/index.ts new file mode 100644 index 000000000000..d1e85649e624 --- /dev/null +++ b/crates/swc/tests/fixture/issues-6xxx/6739/input/index.ts @@ -0,0 +1 @@ +console.log(x < y >>> z); diff --git a/crates/swc/tests/fixture/issues-6xxx/6739/output/index.js b/crates/swc/tests/fixture/issues-6xxx/6739/output/index.js new file mode 100644 index 000000000000..d1e85649e624 --- /dev/null +++ b/crates/swc/tests/fixture/issues-6xxx/6739/output/index.js @@ -0,0 +1 @@ +console.log(x < y >>> z); diff --git a/crates/swc/tests/fixture/issues-6xxx/6739/output/index.ts b/crates/swc/tests/fixture/issues-6xxx/6739/output/index.ts new file mode 100644 index 000000000000..d1e85649e624 --- /dev/null +++ b/crates/swc/tests/fixture/issues-6xxx/6739/output/index.ts @@ -0,0 +1 @@ +console.log(x < y >>> z); diff --git a/crates/swc_ecma_parser/src/lexer/state.rs b/crates/swc_ecma_parser/src/lexer/state.rs index 3f588eda7686..4cf9fb7e523b 100644 --- a/crates/swc_ecma_parser/src/lexer/state.rs +++ b/crates/swc_ecma_parser/src/lexer/state.rs @@ -329,7 +329,7 @@ impl<'a, I: Input> Iterator for Lexer<'a, I> { if c == '<' { self.input.bump(); return Ok(Some(tok!('<'))); - } else if c == '>' { + } else if c == '>' && !self.ctx.prefer_bin_op_over_type_arg_closing { self.input.bump(); return Ok(Some(tok!('>'))); } diff --git a/crates/swc_ecma_parser/src/lib.rs b/crates/swc_ecma_parser/src/lib.rs index 03ec34504ff9..b523edfabb70 100644 --- a/crates/swc_ecma_parser/src/lib.rs +++ b/crates/swc_ecma_parser/src/lib.rs @@ -375,6 +375,10 @@ pub struct Context { ignore_else_clause: bool, disallow_conditional_types: bool, + + /// Used for parsing `>`. If true, `>>` and `>>>` are parsed as binary + /// operators. + prefer_bin_op_over_type_arg_closing: bool, } #[derive(Debug, Clone, Copy, Default)] diff --git a/crates/swc_ecma_parser/src/parser/expr.rs b/crates/swc_ecma_parser/src/parser/expr.rs index c528113cbc24..3ad445d9c87a 100644 --- a/crates/swc_ecma_parser/src/parser/expr.rs +++ b/crates/swc_ecma_parser/src/parser/expr.rs @@ -671,7 +671,11 @@ impl Parser { return_if_arrow!(self, obj); let type_args = if self.syntax().typescript() && is!(self, '<') { - self.try_parse_ts_type_args() + self.with_ctx(Context { + prefer_bin_op_over_type_arg_closing: true, + ..self.ctx() + }) + .try_parse_ts_type_args() } else { None }; diff --git a/crates/swc_ecma_parser/src/parser/typescript.rs b/crates/swc_ecma_parser/src/parser/typescript.rs index 4a4c990120b1..70965116ff86 100644 --- a/crates/swc_ecma_parser/src/parser/typescript.rs +++ b/crates/swc_ecma_parser/src/parser/typescript.rs @@ -557,7 +557,11 @@ impl Parser { pub(super) fn try_parse_ts_type_args(&mut self) -> Option> { debug_assert!(self.input.syntax().typescript()); - self.try_parse_ts(|p| { + self.with_ctx(Context { + prefer_bin_op_over_type_arg_closing: true, + ..self.ctx() + }) + .try_parse_ts(|p| { let type_args = p.parse_ts_type_args()?; if is_one_of!( p, '<', // invalid syntax diff --git a/crates/swc_ecma_parser/tests/typescript/issue-6739/1.ts b/crates/swc_ecma_parser/tests/typescript/issue-6739/1.ts new file mode 100644 index 000000000000..d1e85649e624 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-6739/1.ts @@ -0,0 +1 @@ +console.log(x < y >>> z); diff --git a/crates/swc_ecma_parser/tests/typescript/issue-6739/1.ts.json b/crates/swc_ecma_parser/tests/typescript/issue-6739/1.ts.json new file mode 100644 index 000000000000..07cf524f3a85 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-6739/1.ts.json @@ -0,0 +1,109 @@ +{ + "type": "Script", + "span": { + "start": 1, + "end": 26, + "ctxt": 0 + }, + "body": [ + { + "type": "ExpressionStatement", + "span": { + "start": 1, + "end": 26, + "ctxt": 0 + }, + "expression": { + "type": "CallExpression", + "span": { + "start": 1, + "end": 25, + "ctxt": 0 + }, + "callee": { + "type": "MemberExpression", + "span": { + "start": 1, + "end": 12, + "ctxt": 0 + }, + "object": { + "type": "Identifier", + "span": { + "start": 1, + "end": 8, + "ctxt": 0 + }, + "value": "console", + "optional": false + }, + "property": { + "type": "Identifier", + "span": { + "start": 9, + "end": 12, + "ctxt": 0 + }, + "value": "log", + "optional": false + } + }, + "arguments": [ + { + "spread": null, + "expression": { + "type": "BinaryExpression", + "span": { + "start": 13, + "end": 24, + "ctxt": 0 + }, + "operator": "<", + "left": { + "type": "Identifier", + "span": { + "start": 13, + "end": 14, + "ctxt": 0 + }, + "value": "x", + "optional": false + }, + "right": { + "type": "BinaryExpression", + "span": { + "start": 17, + "end": 24, + "ctxt": 0 + }, + "operator": ">>>", + "left": { + "type": "Identifier", + "span": { + "start": 17, + "end": 18, + "ctxt": 0 + }, + "value": "y", + "optional": false + }, + "right": { + "type": "Identifier", + "span": { + "start": 23, + "end": 24, + "ctxt": 0 + }, + "value": "z", + "optional": false + } + } + } + } + ], + "typeArguments": null + } + } + ], + "interpreter": null +} diff --git a/crates/swc_ecma_parser/tests/typescript/issue-6739/2.tsx b/crates/swc_ecma_parser/tests/typescript/issue-6739/2.tsx new file mode 100644 index 000000000000..d1e85649e624 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-6739/2.tsx @@ -0,0 +1 @@ +console.log(x < y >>> z); diff --git a/crates/swc_ecma_parser/tests/typescript/issue-6739/2.tsx.json b/crates/swc_ecma_parser/tests/typescript/issue-6739/2.tsx.json new file mode 100644 index 000000000000..07cf524f3a85 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-6739/2.tsx.json @@ -0,0 +1,109 @@ +{ + "type": "Script", + "span": { + "start": 1, + "end": 26, + "ctxt": 0 + }, + "body": [ + { + "type": "ExpressionStatement", + "span": { + "start": 1, + "end": 26, + "ctxt": 0 + }, + "expression": { + "type": "CallExpression", + "span": { + "start": 1, + "end": 25, + "ctxt": 0 + }, + "callee": { + "type": "MemberExpression", + "span": { + "start": 1, + "end": 12, + "ctxt": 0 + }, + "object": { + "type": "Identifier", + "span": { + "start": 1, + "end": 8, + "ctxt": 0 + }, + "value": "console", + "optional": false + }, + "property": { + "type": "Identifier", + "span": { + "start": 9, + "end": 12, + "ctxt": 0 + }, + "value": "log", + "optional": false + } + }, + "arguments": [ + { + "spread": null, + "expression": { + "type": "BinaryExpression", + "span": { + "start": 13, + "end": 24, + "ctxt": 0 + }, + "operator": "<", + "left": { + "type": "Identifier", + "span": { + "start": 13, + "end": 14, + "ctxt": 0 + }, + "value": "x", + "optional": false + }, + "right": { + "type": "BinaryExpression", + "span": { + "start": 17, + "end": 24, + "ctxt": 0 + }, + "operator": ">>>", + "left": { + "type": "Identifier", + "span": { + "start": 17, + "end": 18, + "ctxt": 0 + }, + "value": "y", + "optional": false + }, + "right": { + "type": "Identifier", + "span": { + "start": 23, + "end": 24, + "ctxt": 0 + }, + "value": "z", + "optional": false + } + } + } + } + ], + "typeArguments": null + } + } + ], + "interpreter": null +}