diff --git a/crates/oxc_parser/Cargo.toml b/crates/oxc_parser/Cargo.toml index 957731303..c355fc980 100644 --- a/crates/oxc_parser/Cargo.toml +++ b/crates/oxc_parser/Cargo.toml @@ -9,6 +9,10 @@ keywords.workspace = true license.workspace = true repository.workspace = true +[lib] +# We don't use doc tests because it's too slow +doctest = false + [dependencies] oxc_allocator = { workspace = true } oxc_ast = { workspace = true } diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index e5055500d..43374bdef 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -540,6 +540,16 @@ impl<'a> Parser<'a> { self.bump_any(); self.ast.ts_non_null_expression(self.end_span(lhs_span), lhs) } + kind if kind.is_template_start_of_tagged_template() => { + let (expr, type_parameters) = + if let Expression::TSInstantiationExpression(instantiation_expr) = lhs { + let expr = instantiation_expr.unbox(); + (expr.expression, Some(expr.type_parameters)) + } else { + (lhs, None) + }; + self.parse_tagged_template(lhs_span, expr, *in_optional_chain, type_parameters)? + } Kind::LAngle | Kind::ShiftLeft if self.ts_enabled() => { if let Some(arguments) = self.parse_ts_type_arguments_in_expression() { lhs = Expression::TSInstantiationExpression(self.ast.alloc( @@ -647,60 +657,33 @@ impl<'a> Parser<'a> { in_optional_chain: &mut bool, ) -> Result> { let mut lhs = lhs; - let mut type_arguments = None; loop { + let mut type_arguments = None; lhs = self.parse_member_expression_rhs(lhs_span, lhs, in_optional_chain)?; let optional_call = self.eat(Kind::QuestionDot); *in_optional_chain = if optional_call { true } else { *in_optional_chain }; - if let Expression::TSInstantiationExpression(expr) = lhs { - let expr = expr.unbox(); - type_arguments.replace(expr.type_parameters); - lhs = expr.expression; - } - match self.cur_kind() { - Kind::LParen => { - lhs = self.parse_call_arguments( - lhs_span, - lhs, - optional_call, - type_arguments.take(), - )?; - } - Kind::NoSubstitutionTemplate | Kind::TemplateHead => { - lhs = self.parse_tagged_template( - lhs_span, - lhs, - *in_optional_chain, - type_arguments.take(), - )?; - } - Kind::LAngle | Kind::ShiftLeft if self.ts_enabled() => { - let result = self.try_parse(|p| { - let arguments = p.parse_ts_type_arguments()?; - if p.at(Kind::RAngle) { - // a>c is not (a)>c, but a<(b>>c) - return Err(p.unexpected()); - } - // ac is (ac - if !p.at(Kind::LParen) - && !p.at(Kind::NoSubstitutionTemplate) - && !p.at(Kind::TemplateHead) - && p.cur_kind().is_at_expression() - && !p.cur_token().is_on_new_line - { - return Err(p.unexpected()); - } - - type_arguments = arguments; - Ok(()) - }); - if result.is_err() { - break; - } + if optional_call { + type_arguments = self.parse_ts_type_arguments_in_expression(); + if self.cur_kind().is_template_start_of_tagged_template() { + lhs = + self.parse_tagged_template(lhs_span, lhs, optional_call, type_arguments)?; + continue; } - _ => break, } + + if type_arguments.is_some() || self.at(Kind::LParen) { + if let Expression::TSInstantiationExpression(expr) = lhs { + let expr = expr.unbox(); + type_arguments.replace(expr.type_parameters); + lhs = expr.expression; + } + + lhs = + self.parse_call_arguments(lhs_span, lhs, optional_call, type_arguments.take())?; + continue; + } + break; } Ok(lhs) diff --git a/crates/oxc_parser/src/lexer/kind.rs b/crates/oxc_parser/src/lexer/kind.rs index 82c2863a6..751e63e90 100644 --- a/crates/oxc_parser/src/lexer/kind.rs +++ b/crates/oxc_parser/src/lexer/kind.rs @@ -380,6 +380,11 @@ impl Kind { | Slash | SlashEq | TemplateHead | NoSubstitutionTemplate | PrivateIdentifier | Ident | Async) } + #[must_use] + pub fn is_template_start_of_tagged_template(self) -> bool { + matches!(self, NoSubstitutionTemplate | TemplateHead) + } + #[must_use] #[rustfmt::skip] pub fn is_modifier_kind(self) -> bool { diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index ad5451435..3274e30ea 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -270,4 +270,19 @@ mod test { assert!(ret.program.is_empty()); assert_eq!(ret.errors.first().unwrap().to_string(), "Flow is not supported"); } + + #[test] + fn fuzzer() { + let allocator = Allocator::default(); + let source_type = SourceType::default(); + + let tests = [ + "1<(V=82<", + ]; + + for source in tests { + let ret = Parser::new(&allocator, source, source_type).parse(); + assert!(!ret.errors.is_empty()); + } + } } diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index d038def50..7beacdd04 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -507,7 +507,8 @@ impl<'a> Parser<'a> { p.re_lex_ts_l_angle(); let params = TSTypeArgumentList::parse(p)?.params; - if p.cur_kind().can_follow_type_arguments_in_expr() { + let token = p.cur_token(); + if token.is_on_new_line || token.kind.can_follow_type_arguments_in_expr() { Ok(params) } else { Err(p.unexpected())