diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index c8a6161ff..02aa619f5 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1123,6 +1123,7 @@ macro_rules! match_assignment_target { | $ty::TSSatisfiesExpression(_) | $ty::TSNonNullExpression(_) | $ty::TSTypeAssertion(_) + | $ty::TSInstantiationExpression(_) | $ty::ArrayAssignmentTarget(_) | $ty::ObjectAssignmentTarget(_) }; @@ -1146,6 +1147,7 @@ pub enum SimpleAssignmentTarget<'a> { TSSatisfiesExpression(Box<'a, TSSatisfiesExpression<'a>>) = 2, TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 3, TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 4, + TSInstantiationExpression(Box<'a, TSInstantiationExpression<'a>>) = 5, // `MemberExpression` variants added here by `inherit_variants!` macro @inherit MemberExpression } @@ -1164,6 +1166,7 @@ macro_rules! match_simple_assignment_target { | $ty::TSSatisfiesExpression(_) | $ty::TSNonNullExpression(_) | $ty::TSTypeAssertion(_) + | $ty::TSInstantiationExpression(_) }; } pub use match_simple_assignment_target; diff --git a/crates/oxc_ast/src/ast/macros.rs b/crates/oxc_ast/src/ast/macros.rs index 16efa074c..a167c80e1 100644 --- a/crates/oxc_ast/src/ast/macros.rs +++ b/crates/oxc_ast/src/ast/macros.rs @@ -313,6 +313,7 @@ macro_rules! inherit_variants { TSSatisfiesExpression, TSNonNullExpression, TSTypeAssertion, + TSInstantiationExpression, ArrayAssignmentTarget, ObjectAssignmentTarget, ] @@ -344,6 +345,8 @@ macro_rules! inherit_variants { TSNonNullExpression(Box<'a, TSNonNullExpression<'a>>) = 3, /// Inherited from [`SimpleAssignmentTarget`] TSTypeAssertion(Box<'a, TSTypeAssertion<'a>>) = 4, + /// Inherited from [`SimpleAssignmentTarget`] + TSInstantiationExpression(Box<'a, TSInstantiationExpression<'a>>) = 5, // Inherited from `MemberExpression` @inherit MemberExpression @@ -368,7 +371,8 @@ macro_rules! inherit_variants { TSAsExpression, TSSatisfiesExpression, TSNonNullExpression, - TSTypeAssertion + TSTypeAssertion, + TSInstantiationExpression ] ); }; diff --git a/crates/oxc_ast/src/span.rs b/crates/oxc_ast/src/span.rs index bc4fb1994..c08683f2c 100644 --- a/crates/oxc_ast/src/span.rs +++ b/crates/oxc_ast/src/span.rs @@ -276,6 +276,7 @@ impl<'a> GetSpan for AssignmentTarget<'a> { Self::TSSatisfiesExpression(expr) => expr.span, Self::TSNonNullExpression(expr) => expr.span, Self::TSTypeAssertion(expr) => expr.span, + Self::TSInstantiationExpression(expr) => expr.span, // `AssignmentTargetPattern` Self::ComputedMemberExpression(expr) => expr.span, Self::StaticMemberExpression(expr) => expr.span, @@ -464,6 +465,7 @@ impl<'a> GetSpan for ForStatementLeft<'a> { Self::TSSatisfiesExpression(x) => x.span, Self::TSNonNullExpression(x) => x.span, Self::TSTypeAssertion(x) => x.span, + Self::TSInstantiationExpression(x) => x.span, Self::ArrayAssignmentTarget(x) => x.span, Self::ObjectAssignmentTarget(x) => x.span, Self::UsingDeclaration(x) => x.span, @@ -479,6 +481,7 @@ impl<'a> GetSpan for SimpleAssignmentTarget<'a> { Self::TSSatisfiesExpression(expr) => expr.span, Self::TSNonNullExpression(expr) => expr.span, Self::TSTypeAssertion(expr) => expr.span, + Self::TSInstantiationExpression(expr) => expr.span, // `MemberExpression` Self::ComputedMemberExpression(expr) => expr.span, Self::StaticMemberExpression(expr) => expr.span, diff --git a/crates/oxc_ast/src/visit/visit.rs b/crates/oxc_ast/src/visit/visit.rs index f8b7b1eee..50bd060b8 100644 --- a/crates/oxc_ast/src/visit/visit.rs +++ b/crates/oxc_ast/src/visit/visit.rs @@ -1861,6 +1861,9 @@ pub mod walk { SimpleAssignmentTarget::TSTypeAssertion(expr) => { visitor.visit_expression(&expr.expression); } + SimpleAssignmentTarget::TSInstantiationExpression(expr) => { + visitor.visit_expression(&expr.expression); + } } visitor.leave_node(kind); } diff --git a/crates/oxc_ast/src/visit/visit_mut.rs b/crates/oxc_ast/src/visit/visit_mut.rs index 52b03ac0f..a17172f12 100644 --- a/crates/oxc_ast/src/visit/visit_mut.rs +++ b/crates/oxc_ast/src/visit/visit_mut.rs @@ -1940,6 +1940,9 @@ pub mod walk_mut { SimpleAssignmentTarget::TSTypeAssertion(expr) => { visitor.visit_expression(&mut expr.expression); } + SimpleAssignmentTarget::TSInstantiationExpression(expr) => { + visitor.visit_expression(&mut expr.expression); + } } visitor.leave_node(kind); } diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index e91ae1af1..31b3eb5c3 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1790,6 +1790,7 @@ impl<'a, const MINIFY: bool> GenExpr for SimpleAssignmentTarget<'a> { Self::TSSatisfiesExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSNonNullExpression(e) => e.expression.gen_expr(p, precedence, ctx), Self::TSTypeAssertion(e) => e.gen_expr(p, precedence, ctx), + Self::TSInstantiationExpression(e) => e.expression.gen_expr(p, precedence, ctx), } } } diff --git a/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs b/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs index 5c36f2a0a..bb9a52b27 100644 --- a/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs +++ b/crates/oxc_linter/src/rules/oxc/misrefactored_assign_op.rs @@ -127,6 +127,9 @@ fn assignment_target_eq_expr<'a>( SimpleAssignmentTarget::TSTypeAssertion(ts_expr) => { is_same_reference(&ts_expr.expression, right_expr, ctx) } + SimpleAssignmentTarget::TSInstantiationExpression(ts_expr) => { + is_same_reference(&ts_expr.expression, right_expr, ctx) + } }; } diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs index 5382d4d1c..35f910e57 100644 --- a/crates/oxc_parser/src/cursor.rs +++ b/crates/oxc_parser/src/cursor.rs @@ -248,6 +248,13 @@ impl<'a> ParserImpl<'a> { } } + pub(crate) fn re_lex_ts_r_angle(&mut self) { + let kind = self.cur_kind(); + if matches!(kind, Kind::ShiftRight | Kind::ShiftRight3) { + self.token = self.lexer.re_lex_as_typescript_r_angle(kind); + } + } + pub(crate) fn checkpoint(&self) -> ParserCheckpoint<'a> { ParserCheckpoint { lexer: self.lexer.checkpoint(), diff --git a/crates/oxc_parser/src/js/grammar.rs b/crates/oxc_parser/src/js/grammar.rs index 0c91e964a..e14b50079 100644 --- a/crates/oxc_parser/src/js/grammar.rs +++ b/crates/oxc_parser/src/js/grammar.rs @@ -56,6 +56,9 @@ impl<'a> CoverGrammar<'a, Expression<'a>> for SimpleAssignmentTarget<'a> { Ok(SimpleAssignmentTarget::TSNonNullExpression(expr)) } Expression::TSTypeAssertion(expr) => Ok(SimpleAssignmentTarget::TSTypeAssertion(expr)), + Expression::TSInstantiationExpression(expr) => { + Ok(SimpleAssignmentTarget::TSInstantiationExpression(expr)) + } expr => Err(diagnostics::InvalidAssignment(expr.span()).into()), } } diff --git a/crates/oxc_parser/src/lexer/kind.rs b/crates/oxc_parser/src/lexer/kind.rs index e05292865..f043985ac 100644 --- a/crates/oxc_parser/src/lexer/kind.rs +++ b/crates/oxc_parser/src/lexer/kind.rs @@ -644,7 +644,7 @@ impl Kind { pub fn can_follow_type_arguments_in_expr(self) -> bool { matches!(self, Self::LParen | Self::NoSubstitutionTemplate | Self::TemplateHead | Self::Comma | Self::Dot | Self::QuestionDot | Self::RParen | Self::RBrack - | Self::Colon | Self::Semicolon | Self::Question | Self::Eq3 | Self::Eq2 + | Self::Colon | Self::Semicolon | Self::Question | Self::Eq3 | Self::Eq2 | Self::Eq | Self::Neq | Self::Neq2 | Self::Amp2 | Self::Pipe2 | Self::Question2 | Self::Caret | Self::Amp | Self::Pipe | Self::RCurly | Self::Eof) } diff --git a/crates/oxc_parser/src/lexer/typescript.rs b/crates/oxc_parser/src/lexer/typescript.rs index 0603a7576..37f4acddc 100644 --- a/crates/oxc_parser/src/lexer/typescript.rs +++ b/crates/oxc_parser/src/lexer/typescript.rs @@ -14,4 +14,18 @@ impl<'a> Lexer<'a> { self.lookahead.clear(); self.finish_next(kind) } + + /// Re-tokenize '>>' and '>>>' to '>' + pub(crate) fn re_lex_as_typescript_r_angle(&mut self, kind: Kind) -> Token { + let offset = match kind { + Kind::ShiftRight => 2, + Kind::ShiftRight3 => 3, + _ => unreachable!(), + }; + self.token.start = self.offset() - offset; + self.source.back(offset as usize - 1); + let kind = Kind::RAngle; + self.lookahead.clear(); + self.finish_next(kind) + } } diff --git a/crates/oxc_parser/src/ts/list.rs b/crates/oxc_parser/src/ts/list.rs index 75560eba6..90cedd6d9 100644 --- a/crates/oxc_parser/src/ts/list.rs +++ b/crates/oxc_parser/src/ts/list.rs @@ -154,11 +154,21 @@ impl<'a> NormalList<'a> for TSInterfaceOrObjectBodyList<'a> { pub struct TSTypeArgumentList<'a> { pub params: Vec<'a, TSType<'a>>, + in_expression: bool, +} + +impl<'a> TSTypeArgumentList<'a> { + pub fn parse(p: &mut ParserImpl<'a>, in_expression: bool) -> Result { + let mut list = Self::new(p); + list.in_expression = in_expression; + list.parse_list(p)?; + Ok(list) + } } impl<'a> SeparatedList<'a> for TSTypeArgumentList<'a> { fn new(p: &ParserImpl<'a>) -> Self { - Self { params: p.ast.new_vec() } + Self { params: p.ast.new_vec(), in_expression: false } } fn open(&self) -> Kind { @@ -174,6 +184,36 @@ impl<'a> SeparatedList<'a> for TSTypeArgumentList<'a> { self.params.push(ty); Ok(()) } + + fn parse_list(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { + p.expect(self.open())?; + + let mut first = true; + + while !p.at(self.close()) && !p.at(Kind::Eof) { + if first { + first = false; + } else { + p.expect(self.separator())?; + if p.at(self.close()) { + break; + } + } + + self.parse_element(p)?; + } + + if self.in_expression { + // `a < b> = c`` is valid but `a < b >= c` is BinaryExpression + let kind = p.re_lex_right_angle(); + if matches!(kind, Kind::GtEq) { + return Err(p.unexpected()); + } + p.re_lex_ts_r_angle(); + } + p.expect(self.close())?; + Ok(()) + } } pub struct TSImportAttributeList<'a> { diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index d8d34140d..31d21e6db 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -514,7 +514,7 @@ impl<'a> ParserImpl<'a> { return Ok(None); } let span = self.start_span(); - let params = TSTypeArgumentList::parse(self)?.params; + let params = TSTypeArgumentList::parse(self, false)?.params; Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))) } @@ -529,7 +529,7 @@ impl<'a> ParserImpl<'a> { self.try_parse(|p| { p.re_lex_ts_l_angle(); - let params = TSTypeArgumentList::parse(p)?.params; + let params = TSTypeArgumentList::parse(p, true)?.params; let token = p.cur_token(); if token.is_on_new_line || token.kind.can_follow_type_arguments_in_expr() { Ok(params) diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 46021249a..b8336ca59 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -1809,6 +1809,7 @@ impl<'a> Format<'a> for SimpleAssignmentTarget<'a> { Self::TSSatisfiesExpression(expr) => expr.expression.format(p), Self::TSNonNullExpression(expr) => expr.expression.format(p), Self::TSTypeAssertion(expr) => expr.expression.format(p), + Self::TSInstantiationExpression(expr) => expr.expression.format(p), } } } diff --git a/crates/oxc_prettier/src/needs_parens.rs b/crates/oxc_prettier/src/needs_parens.rs index 1e5e002ea..3afa16c52 100644 --- a/crates/oxc_prettier/src/needs_parens.rs +++ b/crates/oxc_prettier/src/needs_parens.rs @@ -590,6 +590,9 @@ impl<'a> Prettier<'a> { AssignmentTarget::TSTypeAssertion(e) => { Self::starts_with_no_lookahead_token(&e.expression, span) } + AssignmentTarget::TSInstantiationExpression(e) => { + Self::starts_with_no_lookahead_token(&e.expression, span) + } AssignmentTarget::ArrayAssignmentTarget(_) | AssignmentTarget::ObjectAssignmentTarget(_) => false, }, @@ -636,6 +639,9 @@ impl<'a> Prettier<'a> { SimpleAssignmentTarget::TSTypeAssertion(e) => { Self::starts_with_no_lookahead_token(&e.expression, span) } + SimpleAssignmentTarget::TSInstantiationExpression(e) => { + Self::starts_with_no_lookahead_token(&e.expression, span) + } } } Expression::SequenceExpression(e) => e diff --git a/crates/oxc_traverse/src/walk.rs b/crates/oxc_traverse/src/walk.rs index e197a104a..4e0f623f4 100644 --- a/crates/oxc_traverse/src/walk.rs +++ b/crates/oxc_traverse/src/walk.rs @@ -915,6 +915,7 @@ pub(crate) unsafe fn walk_assignment_target<'a, Tr: Traverse<'a>>( | AssignmentTarget::TSSatisfiesExpression(_) | AssignmentTarget::TSNonNullExpression(_) | AssignmentTarget::TSTypeAssertion(_) + | AssignmentTarget::TSInstantiationExpression(_) | AssignmentTarget::ComputedMemberExpression(_) | AssignmentTarget::StaticMemberExpression(_) | AssignmentTarget::PrivateFieldExpression(_) => { @@ -950,6 +951,9 @@ pub(crate) unsafe fn walk_simple_assignment_target<'a, Tr: Traverse<'a>>( SimpleAssignmentTarget::TSTypeAssertion(node) => { walk_ts_type_assertion(traverser, (&mut **node) as *mut _, ctx) } + SimpleAssignmentTarget::TSInstantiationExpression(node) => { + walk_ts_instantiation_expression(traverser, (&mut **node) as *mut _, ctx) + } SimpleAssignmentTarget::ComputedMemberExpression(_) | SimpleAssignmentTarget::StaticMemberExpression(_) | SimpleAssignmentTarget::PrivateFieldExpression(_) => { @@ -1062,6 +1066,7 @@ pub(crate) unsafe fn walk_assignment_target_maybe_default<'a, Tr: Traverse<'a>>( | AssignmentTargetMaybeDefault::TSSatisfiesExpression(_) | AssignmentTargetMaybeDefault::TSNonNullExpression(_) | AssignmentTargetMaybeDefault::TSTypeAssertion(_) + | AssignmentTargetMaybeDefault::TSInstantiationExpression(_) | AssignmentTargetMaybeDefault::ArrayAssignmentTarget(_) | AssignmentTargetMaybeDefault::ObjectAssignmentTarget(_) | AssignmentTargetMaybeDefault::ComputedMemberExpression(_) @@ -1757,6 +1762,7 @@ pub(crate) unsafe fn walk_for_statement_left<'a, Tr: Traverse<'a>>( | ForStatementLeft::TSSatisfiesExpression(_) | ForStatementLeft::TSNonNullExpression(_) | ForStatementLeft::TSTypeAssertion(_) + | ForStatementLeft::TSInstantiationExpression(_) | ForStatementLeft::ArrayAssignmentTarget(_) | ForStatementLeft::ObjectAssignmentTarget(_) | ForStatementLeft::ComputedMemberExpression(_) diff --git a/tasks/coverage/parser_babel.snap b/tasks/coverage/parser_babel.snap index 1c62393b1..f1c2324b6 100644 --- a/tasks/coverage/parser_babel.snap +++ b/tasks/coverage/parser_babel.snap @@ -1,6 +1,6 @@ parser_babel Summary: -AST Parsed : 2091/2099 (99.62%) -Positive Passed: 2084/2099 (99.29%) +AST Parsed : 2093/2099 (99.71%) +Positive Passed: 2086/2099 (99.38%) Negative Passed: 1363/1501 (90.81%) Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions/input.js" @@ -163,24 +163,6 @@ Expect to Parse: "typescript/arrow-function/generic-tsx-babel-7/input.ts" ╭─[typescript/arrow-function/generic-tsx-babel-7/input.ts:3:1] 2 │ (a: T): T => a; ╰──── -Expect to Parse: "typescript/assign/TSTypeParameterInstantiation/input.ts" - - × Unexpected token - ╭─[typescript/assign/TSTypeParameterInstantiation/input.ts:15:31] - 14 │ - 15 │ cls.myFunc = (instance) => { - · ─ - 16 │ console.log(instance.myField); - ╰──── -Expect to Parse: "typescript/assign/TSTypeParameterInstantiation-babel-7/input.ts" - - × Unexpected token - ╭─[typescript/assign/TSTypeParameterInstantiation-babel-7/input.ts:15:31] - 14 │ - 15 │ cls.myFunc = (instance) => { - · ─ - 16 │ console.log(instance.myField); - ╰──── Expect to Parse: "typescript/class/constructor-with-modifier-names/input.ts" × Multiple constructor implementations are not allowed. diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 090f954c9..0a0ec4f23 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 295/362 +Passed: 296/362 # All Passed: * babel-plugin-transform-react-display-name @@ -23,7 +23,7 @@ Passed: 295/362 * opts/optimizeConstEnums/input.ts * opts/rewriteImportExtensions/input.ts -# babel-plugin-transform-typescript (118/156) +# babel-plugin-transform-typescript (119/156) * class/accessor-allowDeclareFields-false/input.ts * class/accessor-allowDeclareFields-true/input.ts * enum/mix-references/input.ts @@ -33,7 +33,6 @@ Passed: 295/362 * exports/export-type-star-from/input.ts * imports/enum-value/input.ts * imports/type-only-export-specifier-2/input.ts -* lvalues/TSTypeParameterInstantiation/input.ts * namespace/ambient-module-nested/input.ts * namespace/ambient-module-nested-exported/input.ts * namespace/canonical/input.ts