Skip to content

Commit

Permalink
refactor(parser): improve parsing of `parse_function_or_constructor_t…
Browse files Browse the repository at this point in the history
…ype` (#3892)

part of #3502
  • Loading branch information
Boshen committed Jun 25, 2024
1 parent 442aca3 commit 187f078
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 119 deletions.
2 changes: 1 addition & 1 deletion crates/oxc_parser/src/js/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl<'a> ParserImpl<'a> {
Ok(self.ast.binding_pattern(self.end_span(span), kind, type_annotation, optional))
}

pub(super) fn parse_binding_pattern_kind(&mut self) -> Result<BindingPatternKind<'a>> {
pub(crate) fn parse_binding_pattern_kind(&mut self) -> Result<BindingPatternKind<'a>> {
match self.cur_kind() {
Kind::LCurly => self.parse_object_binding_pattern(),
Kind::LBrack => self.parse_array_binding_pattern(),
Expand Down
1 change: 0 additions & 1 deletion crates/oxc_parser/src/ts/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,6 @@ impl<'a> ParserImpl<'a> {

pub(crate) fn parse_ts_type_assertion(&mut self) -> Result<Expression<'a>> {
let span = self.start_span();
self.re_lex_ts_l_angle();
self.expect(Kind::LAngle)?;
let type_annotation = self.parse_ts_type()?;
self.expect(Kind::RAngle)?;
Expand Down
180 changes: 80 additions & 100 deletions crates/oxc_parser/src/ts/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use super::list::{
};
use crate::{
diagnostics,
js::list::{ArrayPatternList, ObjectPatternProperties},
lexer::Kind,
list::{NormalList, SeparatedList},
modifiers::ModifierFlags,
Expand All @@ -19,18 +18,91 @@ use crate::{

impl<'a> ParserImpl<'a> {
pub(crate) fn parse_ts_type(&mut self) -> Result<TSType<'a>> {
if self.is_at_constructor_type() {
return self.parse_ts_constructor_type();
if self.is_start_of_function_type_or_constructor_type() {
return self.parse_function_or_constructor_type();
}
let left_span = self.start_span();
let left = self.parse_ts_union_type()?;
self.parse_ts_conditional_type(left_span, left)
}

fn parse_function_or_constructor_type(&mut self) -> Result<TSType<'a>> {
let span = self.start_span();
let r#abstract = self.eat(Kind::Abstract);
let is_constructor_type = self.eat(Kind::New);
let type_parameters = self.parse_ts_type_parameters()?;
let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?;
self.expect(Kind::Arrow)?;
let return_type = {
let return_type_span = self.start_span();
let return_type = self.parse_ts_return_type()?;
self.ast.ts_type_annotation(self.end_span(return_type_span), return_type)
};

if self.is_at_function_type() {
return self.parse_ts_function_type();
let span = self.end_span(span);
Ok(if is_constructor_type {
if let Some(this_param) = &this_param {
// type Foo = new (this: number) => any;
self.error(diagnostics::ts_constructor_this_parameter(this_param.span));
}
self.ast.ts_constructor_type(span, r#abstract, params, return_type, type_parameters)
} else {
self.ast.ts_function_type(span, this_param, params, return_type, type_parameters)
})
}

fn is_start_of_function_type_or_constructor_type(&mut self) -> bool {
if self.at(Kind::LAngle) {
return true;
}
if self.at(Kind::LParen) && self.lookahead(Self::is_unambiguously_start_of_function_type) {
return true;
}
self.at(Kind::New) || (self.at(Kind::Abstract) && self.peek_at(Kind::New))
}

let left_span = self.start_span();
let left = self.parse_ts_union_type()?;
fn is_unambiguously_start_of_function_type(&mut self) -> bool {
self.bump_any();
// ( )
// ( ...
if matches!(self.cur_kind(), Kind::RParen | Kind::Dot3) {
return true;
}
if self.skip_parameter_start() {
// ( xxx :
// ( xxx ,
// ( xxx ?
// ( xxx =
if matches!(self.cur_kind(), Kind::Colon | Kind::Comma | Kind::Question | Kind::Eq) {
return true;
}
// ( xxx ) =>
if self.eat(Kind::RParen) && self.at(Kind::Arrow) {
return true;
}
}
false
}

self.parse_ts_conditional_type(left_span, left)
fn skip_parameter_start(&mut self) -> bool {
// Skip modifiers
loop {
if self.cur_kind().is_modifier_kind() && !self.peek_at(Kind::Comma) {
self.bump_any();
} else {
break;
}
}
if self.cur_kind().is_identifier() || self.at(Kind::This) {
self.bump_any();
return true;
}
if matches!(self.cur_kind(), Kind::LBrack | Kind::LCurly)
&& self.parse_binding_pattern_kind().is_ok()
{
return true;
}
false
}

pub(crate) fn parse_ts_type_parameters(
Expand Down Expand Up @@ -162,10 +234,6 @@ impl<'a> ParserImpl<'a> {
Ok(left)
}

fn is_at_constructor_type(&mut self) -> bool {
self.at(Kind::New) || (self.at(Kind::Abstract) && self.peek_at(Kind::New))
}

// test ts ts_union_type
// type A = string | number;
// type B = | A | void | null;
Expand Down Expand Up @@ -474,51 +542,6 @@ impl<'a> ParserImpl<'a> {
Ok(self.ast.ts_tuple_type(self.end_span(span), elements))
}

fn is_at_function_type(&mut self) -> bool {
if self.at(Kind::LAngle) {
return true;
}

if !self.at(Kind::LParen) {
return false;
}

let checkpoint = self.checkpoint();

self.bump_any(); // bump (

if self.at(Kind::RParen) || self.at(Kind::Dot3) {
self.rewind(checkpoint);
return true;
}

let mut is_function_parameter_start =
self.at(Kind::This) || self.cur_kind().is_binding_identifier();

if is_function_parameter_start {
self.bump_any();
}

if match self.cur_kind() {
Kind::LBrack => ArrayPatternList::parse(self).is_ok(),
Kind::LCurly => ObjectPatternProperties::parse(self).is_ok(),
_ => false,
} {
is_function_parameter_start = true;
}

let result = if is_function_parameter_start {
matches!(self.cur_kind(), Kind::Colon | Kind::Eq | Kind::Comma | Kind::Question)
|| (self.at(Kind::RParen) && self.peek_at(Kind::Arrow))
} else {
false
};

self.rewind(checkpoint);

result
}

fn is_at_mapped_type(&mut self) -> bool {
if !self.at(Kind::LCurly) {
return false;
Expand Down Expand Up @@ -731,49 +754,6 @@ impl<'a> ParserImpl<'a> {
Ok(TSImportAttributes { span, elements })
}

fn parse_ts_constructor_type(&mut self) -> Result<TSType<'a>> {
let span = self.start_span();
let r#abstract = self.eat(Kind::Abstract);
self.expect(Kind::New)?;
let type_parameters = self.parse_ts_type_parameters()?;
let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?;

if let Some(this_param) = this_param {
// type Foo = new (this: number) => any;
self.error(diagnostics::ts_constructor_this_parameter(this_param.span));
}

self.expect(Kind::Arrow)?;
let return_type_span = self.start_span();
let return_type = self.parse_ts_return_type()?;
let return_type = self.ast.ts_type_annotation(self.end_span(return_type_span), return_type);

Ok(self.ast.ts_constructor_type(
self.end_span(span),
r#abstract,
params,
return_type,
type_parameters,
))
}

fn parse_ts_function_type(&mut self) -> Result<TSType<'a>> {
let span = self.start_span();
let type_parameters = self.parse_ts_type_parameters()?;
let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?;
let return_type_span = self.start_span();
self.expect(Kind::Arrow)?;
let return_type = self.parse_ts_return_type()?;
let return_type = self.ast.ts_type_annotation(self.end_span(return_type_span), return_type);
Ok(self.ast.ts_function_type(
self.end_span(span),
this_param,
params,
return_type,
type_parameters,
))
}

fn parse_ts_infer_type(&mut self) -> Result<TSType<'a>> {
let span = self.start_span();
self.expect(Kind::Infer)?;
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_semantic/src/checker/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ pub fn check_formal_parameters(params: &FormalParameters, ctx: &SemanticBuilder<
check_duplicate_bound_names(params, ctx);
}

let is_inside_constructor = ctx.current_scope_flags().is_constructor();
let is_inside_constructor =
!params.kind.is_signature() && ctx.current_scope_flags().is_constructor();
let mut has_optional = false;

for item in &params.items {
Expand Down
28 changes: 12 additions & 16 deletions tasks/coverage/parser_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3999,20 +3999,18 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
2 │ }
╰────

× Expected `)` but found `Identifier`
╭─[compiler/ParameterList5.ts:1:23]
× A parameter property is only allowed in a constructor implementation.
╭─[compiler/ParameterList5.ts:1:16]
1 │ function A(): (public B) => C {
· ┬
· ╰── `)` expected
· ────────
2 │ }
╰────

× Expected `)` but found `Identifier`
╭─[compiler/ParameterList6.ts:2:26]
× A parameter property is only allowed in a constructor implementation.
╭─[compiler/ParameterList6.ts:2:19]
1 │ class C {
2 │ constructor(C: (public A) => any) {
· ┬
· ╰── `)` expected
· ────────
3 │ }
╰────

Expand Down Expand Up @@ -17725,20 +17723,18 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
2 │ }
╰────

× Expected `)` but found `Identifier`
╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList5.ts:1:23]
× A parameter property is only allowed in a constructor implementation.
╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList5.ts:1:16]
1 │ function A(): (public B) => C {
· ┬
· ╰── `)` expected
· ────────
2 │ }
╰────

× Expected `)` but found `Identifier`
╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList6.ts:2:26]
× A parameter property is only allowed in a constructor implementation.
╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList6.ts:2:19]
1 │ class C {
2 │ constructor(C: (public A) => any) {
· ┬
· ╰── `)` expected
· ────────
3 │ }
╰────

Expand Down

0 comments on commit 187f078

Please sign in to comment.