mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(parser): improve parsing of TypeScript type arguments (#3923)
This commit is contained in:
parent
4cf3c7645f
commit
3db2553dc2
10 changed files with 135 additions and 71 deletions
|
|
@ -254,17 +254,23 @@ impl<'a> ParserImpl<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn re_lex_ts_l_angle(&mut self) {
|
||||
pub(crate) fn re_lex_l_angle(&mut self) -> Kind {
|
||||
let kind = self.cur_kind();
|
||||
if matches!(kind, Kind::ShiftLeft | Kind::ShiftLeftEq | Kind::LtEq) {
|
||||
self.token = self.lexer.re_lex_as_typescript_l_angle(kind);
|
||||
self.token.kind
|
||||
} else {
|
||||
kind
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn re_lex_ts_r_angle(&mut self) {
|
||||
pub(crate) fn re_lex_ts_r_angle(&mut self) -> Kind {
|
||||
let kind = self.cur_kind();
|
||||
if matches!(kind, Kind::ShiftRight | Kind::ShiftRight3) {
|
||||
self.token = self.lexer.re_lex_as_typescript_r_angle(kind);
|
||||
self.token.kind
|
||||
} else {
|
||||
kind
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ impl<'a> ParserImpl<'a> {
|
|||
first_extends = expr.expression;
|
||||
first_type_argument = Some(expr.type_parameters);
|
||||
} else {
|
||||
first_type_argument = self.parse_ts_type_arguments()?;
|
||||
first_type_argument = self.try_parse_type_arguments()?;
|
||||
}
|
||||
extends.push((first_extends, first_type_argument, self.end_span(span)));
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ impl<'a> ParserImpl<'a> {
|
|||
extend = expr.expression;
|
||||
type_argument = Some(expr.type_parameters);
|
||||
} else {
|
||||
type_argument = self.parse_ts_type_arguments()?;
|
||||
type_argument = self.try_parse_type_arguments()?;
|
||||
}
|
||||
|
||||
extends.push((extend, type_argument, self.end_span(span)));
|
||||
|
|
|
|||
|
|
@ -602,7 +602,7 @@ impl<'a> ParserImpl<'a> {
|
|||
}
|
||||
Kind::LAngle | Kind::ShiftLeft => {
|
||||
if let Ok(Some(arguments)) =
|
||||
self.try_parse(Self::parse_ts_type_arguments_in_expression)
|
||||
self.try_parse(Self::parse_type_arguments_in_expression)
|
||||
{
|
||||
lhs = self.ast.ts_instantiation_expression(
|
||||
self.end_span(lhs_span),
|
||||
|
|
@ -713,8 +713,7 @@ impl<'a> ParserImpl<'a> {
|
|||
*in_optional_chain = if optional_call { true } else { *in_optional_chain };
|
||||
|
||||
if optional_call {
|
||||
if let Ok(Some(args)) = self.try_parse(Self::parse_ts_type_arguments_in_expression)
|
||||
{
|
||||
if let Ok(Some(args)) = self.try_parse(Self::parse_type_arguments_in_expression) {
|
||||
type_arguments = Some(args);
|
||||
}
|
||||
if self.cur_kind().is_template_start_of_tagged_template() {
|
||||
|
|
|
|||
|
|
@ -88,11 +88,8 @@ impl<'a> ParserImpl<'a> {
|
|||
self.expect(Kind::LAngle)?;
|
||||
let name = self.parse_jsx_element_name()?;
|
||||
// <Component<TsType> for tsx
|
||||
let type_parameters = if self.ts_enabled() {
|
||||
self.context(Context::default(), self.ctx, Self::parse_ts_type_arguments)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let type_parameters =
|
||||
if self.ts_enabled() { self.try_parse_type_arguments()? } else { None };
|
||||
let attributes = self.parse_jsx_attributes()?;
|
||||
let self_closing = self.eat(Kind::Slash);
|
||||
if !self_closing || in_jsx_child {
|
||||
|
|
|
|||
|
|
@ -643,15 +643,6 @@ impl Kind {
|
|||
BigInt => "bigint",
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
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::Eq
|
||||
| Self::Neq | Self::Neq2 | Self::Amp2 | Self::Pipe2 | Self::Question2
|
||||
| Self::Caret | Self::Amp | Self::Pipe | Self::RCurly | Self::Eof)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Kind {
|
||||
|
|
|
|||
|
|
@ -159,8 +159,7 @@ impl<'a> SeparatedList<'a> for TSTypeArgumentList<'a> {
|
|||
|
||||
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) {
|
||||
if matches!(p.re_lex_right_angle(), Kind::GtEq) {
|
||||
return Err(p.unexpected());
|
||||
}
|
||||
p.re_lex_ts_r_angle();
|
||||
|
|
|
|||
|
|
@ -649,12 +649,8 @@ impl<'a> ParserImpl<'a> {
|
|||
self.bump_any(); // `bump `typeof`
|
||||
let entity_name = self.parse_ts_type_name()?; // TODO: parseEntityName
|
||||
let entity_name = self.ast.ts_type_query_expr_name_type_name(entity_name);
|
||||
let type_arguments = if self.cur_token().is_on_new_line {
|
||||
None
|
||||
} else {
|
||||
// TODO: tryParseTypeArguments
|
||||
self.parse_ts_type_arguments()?
|
||||
};
|
||||
let type_arguments =
|
||||
if self.cur_token().is_on_new_line { None } else { self.try_parse_type_arguments()? };
|
||||
Ok(self.ast.ts_type_query_type(self.end_span(span), entity_name, type_arguments))
|
||||
}
|
||||
|
||||
|
|
@ -751,18 +747,15 @@ impl<'a> ParserImpl<'a> {
|
|||
fn parse_type_reference(&mut self) -> Result<TSType<'a>> {
|
||||
let span = self.start_span();
|
||||
let type_name = self.parse_ts_type_name()?;
|
||||
let type_parameters =
|
||||
if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? };
|
||||
let type_parameters = self.parse_type_arguments_of_type_reference()?;
|
||||
Ok(self.ast.ts_type_reference(self.end_span(span), type_name, type_parameters))
|
||||
}
|
||||
|
||||
fn parse_ts_implement_name(&mut self) -> Result<TSClassImplements<'a>> {
|
||||
let span = self.start_span();
|
||||
let expression = self.parse_ts_type_name()?;
|
||||
let type_parameters =
|
||||
if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? };
|
||||
|
||||
Ok(self.ast.ts_type_implement(self.end_span(span), expression, type_parameters))
|
||||
let type_name = self.parse_ts_type_name()?;
|
||||
let type_parameters = self.parse_type_arguments_of_type_reference()?;
|
||||
Ok(self.ast.ts_type_implement(self.end_span(span), type_name, type_parameters))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_ts_type_name(&mut self) -> Result<TSTypeName<'a>> {
|
||||
|
|
@ -781,42 +774,58 @@ impl<'a> ParserImpl<'a> {
|
|||
Ok(left)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_ts_type_arguments(
|
||||
pub(crate) fn try_parse_type_arguments(
|
||||
&mut self,
|
||||
) -> Result<Option<Box<'a, TSTypeParameterInstantiation<'a>>>> {
|
||||
self.re_lex_ts_l_angle();
|
||||
if !self.at(Kind::LAngle) {
|
||||
return Ok(None);
|
||||
if self.at(Kind::LAngle) {
|
||||
let span = self.start_span();
|
||||
let params = TSTypeArgumentList::parse(self, false)?.params;
|
||||
return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params)));
|
||||
}
|
||||
let span = self.start_span();
|
||||
let params = TSTypeArgumentList::parse(self, false)?.params;
|
||||
Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params)))
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_ts_type_arguments_in_expression(
|
||||
fn parse_type_arguments_of_type_reference(
|
||||
&mut self,
|
||||
) -> Result<Option<Box<'a, TSTypeParameterInstantiation<'a>>>> {
|
||||
self.re_lex_l_angle();
|
||||
if !self.cur_token().is_on_new_line && self.re_lex_l_angle() == Kind::LAngle {
|
||||
let span = self.start_span();
|
||||
let params = TSTypeArgumentList::parse(self, false)?.params;
|
||||
return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params)));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_type_arguments_in_expression(
|
||||
&mut self,
|
||||
) -> Result<Option<Box<'a, TSTypeParameterInstantiation<'a>>>> {
|
||||
if !self.ts_enabled() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let span = self.start_span();
|
||||
self.re_lex_ts_l_angle();
|
||||
if !self.at(Kind::LAngle) {
|
||||
if self.re_lex_l_angle() != Kind::LAngle {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let params = TSTypeArgumentList::parse(self, /* in_expression */ true)?.params;
|
||||
|
||||
let token = self.cur_token();
|
||||
|
||||
if token.is_on_new_line || token.kind.can_follow_type_arguments_in_expr() {
|
||||
if self.can_follow_type_arguments_in_expr() {
|
||||
return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params)));
|
||||
}
|
||||
|
||||
Err(self.unexpected())
|
||||
}
|
||||
|
||||
fn can_follow_type_arguments_in_expr(&mut self) -> bool {
|
||||
match self.cur_kind() {
|
||||
Kind::LParen | Kind::NoSubstitutionTemplate | Kind::TemplateHead => true,
|
||||
Kind::LAngle | Kind::RAngle | Kind::Plus | Kind::Minus => false,
|
||||
_ => {
|
||||
self.cur_token().is_on_new_line
|
||||
|| self.is_binary_operator()
|
||||
|| !self.is_start_of_expression()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_tuple_type(&mut self) -> Result<TSType<'a>> {
|
||||
let span = self.start_span();
|
||||
let elements = TSTupleElementList::parse(self)?.elements;
|
||||
|
|
@ -951,7 +960,7 @@ impl<'a> ParserImpl<'a> {
|
|||
if self.eat(Kind::Comma) { Some(self.parse_ts_import_attributes()?) } else { None };
|
||||
self.expect(Kind::RParen)?;
|
||||
let qualifier = if self.eat(Kind::Dot) { Some(self.parse_ts_type_name()?) } else { None };
|
||||
let type_parameters = self.parse_ts_type_arguments()?;
|
||||
let type_parameters = self.parse_type_arguments_of_type_reference()?;
|
||||
Ok(self.ast.ts_import_type(
|
||||
self.end_span(span),
|
||||
is_type_of,
|
||||
|
|
@ -1307,4 +1316,45 @@ impl<'a> ParserImpl<'a> {
|
|||
let ty = self.parse_non_array_type()?;
|
||||
Ok(self.ast.js_doc_non_nullable_type(self.end_span(span), ty, /* postfix */ false))
|
||||
}
|
||||
|
||||
fn is_binary_operator(&mut self) -> bool {
|
||||
if self.ctx.has_in() && self.at(Kind::In) {
|
||||
return false;
|
||||
}
|
||||
self.cur_kind().is_binary_operator()
|
||||
}
|
||||
|
||||
fn is_start_of_expression(&mut self) -> bool {
|
||||
if self.is_start_of_left_hand_side_expression() {
|
||||
return true;
|
||||
}
|
||||
match self.cur_kind() {
|
||||
kind if kind.is_unary_operator() => true,
|
||||
kind if kind.is_update_operator() => true,
|
||||
Kind::LAngle | Kind::Await | Kind::Yield | Kind::Private | Kind::At => true,
|
||||
kind if kind.is_binary_operator() => true,
|
||||
kind => kind.is_identifier(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_start_of_left_hand_side_expression(&mut self) -> bool {
|
||||
match self.cur_kind() {
|
||||
kind if kind.is_literal() => true,
|
||||
kind if kind.is_template_start_of_tagged_template() => true,
|
||||
Kind::This
|
||||
| Kind::Super
|
||||
| Kind::LParen
|
||||
| Kind::LBrack
|
||||
| Kind::LCurly
|
||||
| Kind::Function
|
||||
| Kind::Class
|
||||
| Kind::New
|
||||
| Kind::Slash
|
||||
| Kind::SlashEq => true,
|
||||
Kind::Import => {
|
||||
matches!(self.peek_kind(), Kind::LParen | Kind::LAngle | Kind::Dot)
|
||||
}
|
||||
kind => kind.is_identifier(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
commit: 12619ffe
|
||||
|
||||
parser_babel Summary:
|
||||
AST Parsed : 2095/2101 (99.71%)
|
||||
Positive Passed: 2087/2101 (99.33%)
|
||||
AST Parsed : 2091/2101 (99.52%)
|
||||
Positive Passed: 2083/2101 (99.14%)
|
||||
Negative Passed: 1364/1501 (90.87%)
|
||||
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"
|
||||
|
|
@ -351,6 +351,36 @@ Expect to Parse: "typescript/regression/nested-extends-in-arrow-type-param-babel
|
|||
· ───┬───
|
||||
· ╰── `,` expected
|
||||
╰────
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like/class-heritage/input.ts"
|
||||
|
||||
× Expected `{` but found `<<`
|
||||
╭─[typescript/type-arguments-bit-shift-left-like/class-heritage/input.ts:1:17]
|
||||
1 │ (class extends f<<T>(v: T) => void> {});
|
||||
· ─┬
|
||||
· ╰── `{` expected
|
||||
╰────
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like/jsx-opening-element/input.tsx"
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/type-arguments-bit-shift-left-like/jsx-opening-element/input.tsx:1:11]
|
||||
1 │ <Component<<T>(v: T) => void> />
|
||||
· ──
|
||||
╰────
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like-babel-7/class-heritage/input.ts"
|
||||
|
||||
× Expected `{` but found `<<`
|
||||
╭─[typescript/type-arguments-bit-shift-left-like-babel-7/class-heritage/input.ts:1:17]
|
||||
1 │ (class extends f<<T>(v: T) => void> {});
|
||||
· ─┬
|
||||
· ╰── `{` expected
|
||||
╰────
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like-babel-7/jsx-opening-element/input.tsx"
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/type-arguments-bit-shift-left-like-babel-7/jsx-opening-element/input.tsx:1:11]
|
||||
1 │ <Component<<T>(v: T) => void> />
|
||||
· ──
|
||||
╰────
|
||||
Expect to Parse: "typescript/types/const-type-parameters/input.ts"
|
||||
|
||||
× Unexpected token
|
||||
|
|
@ -10563,11 +10593,12 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts"
|
|||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/type-arguments/new-without-arguments-missing-semicolon/input.ts:1:10]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/type-arguments/new-without-arguments-missing-semicolon/input.ts:1:9]
|
||||
1 │ new A<T> if (0);
|
||||
· ──
|
||||
· ─
|
||||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/type-only-import-export-specifiers/export-invalid-type-only-keyword/input.ts:1:7]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ commit: d8086f14
|
|||
parser_typescript Summary:
|
||||
AST Parsed : 5280/5283 (99.94%)
|
||||
Positive Passed: 5273/5283 (99.81%)
|
||||
Negative Passed: 1069/4875 (21.93%)
|
||||
Negative Passed: 1068/4875 (21.91%)
|
||||
Expect Syntax Error: "compiler/ClassDeclaration10.ts"
|
||||
Expect Syntax Error: "compiler/ClassDeclaration11.ts"
|
||||
Expect Syntax Error: "compiler/ClassDeclaration13.ts"
|
||||
|
|
@ -967,6 +967,7 @@ Expect Syntax Error: "compiler/inlineSourceMap2.ts"
|
|||
Expect Syntax Error: "compiler/innerAliases.ts"
|
||||
Expect Syntax Error: "compiler/innerTypeCheckOfLambdaArgument.ts"
|
||||
Expect Syntax Error: "compiler/instanceSubtypeCheck2.ts"
|
||||
Expect Syntax Error: "compiler/instanceofOnInstantiationExpression.ts"
|
||||
Expect Syntax Error: "compiler/instanceofOperator.ts"
|
||||
Expect Syntax Error: "compiler/instanceofWithPrimitiveUnion.ts"
|
||||
Expect Syntax Error: "compiler/instanceofWithStructurallyIdenticalTypes.ts"
|
||||
|
|
@ -6955,14 +6956,6 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[compiler/instanceofOnInstantiationExpression.ts:14:13]
|
||||
13 │
|
||||
14 │ Box<number> instanceof Object; // OK
|
||||
· ──────────
|
||||
15 │ (Box<number>) instanceof Object; // OK
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[compiler/intTypeCheck.ts:37:17]
|
||||
36 │ //Index Signatures
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ commit: 12619ffe
|
|||
|
||||
prettier_babel Summary:
|
||||
AST Parsed : 2101/2101 (100.00%)
|
||||
Positive Passed: 1893/2101 (90.10%)
|
||||
Positive Passed: 1895/2101 (90.20%)
|
||||
Expect to Parse: "comments/attachComment-false/array-expression-trailing-comma/input.js"
|
||||
Expect to Parse: "comments/basic/array-expression-trailing-comma/input.js"
|
||||
Expect to Parse: "comments/basic/object-expression-trailing-comma/input.js"
|
||||
|
|
@ -149,9 +149,7 @@ Expect to Parse: "typescript/type-alias/generic-complex-tokens-true-babel-7/inpu
|
|||
Expect to Parse: "typescript/type-arguments/tsx/input.ts"
|
||||
Expect to Parse: "typescript/type-arguments/whitespace/input.ts"
|
||||
Expect to Parse: "typescript/type-arguments/whitespace-babel-7/input.ts"
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like/jsx-opening-element/input.tsx"
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like/type-arguments-like/input.ts"
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like-babel-7/jsx-opening-element/input.tsx"
|
||||
Expect to Parse: "typescript/type-arguments-bit-shift-left-like-babel-7/type-arguments-like/input.ts"
|
||||
Expect to Parse: "typescript/types/conditional/input.ts"
|
||||
Expect to Parse: "typescript/types/conditional-infer/input.ts"
|
||||
|
|
|
|||
Loading…
Reference in a new issue