Skip to content

Commit 187f078

Browse files
committed
refactor(parser): improve parsing of parse_function_or_constructor_type (#3892)
part of #3502
1 parent 442aca3 commit 187f078

File tree

5 files changed

+95
-119
lines changed

5 files changed

+95
-119
lines changed

crates/oxc_parser/src/js/binding.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl<'a> ParserImpl<'a> {
3131
Ok(self.ast.binding_pattern(self.end_span(span), kind, type_annotation, optional))
3232
}
3333

34-
pub(super) fn parse_binding_pattern_kind(&mut self) -> Result<BindingPatternKind<'a>> {
34+
pub(crate) fn parse_binding_pattern_kind(&mut self) -> Result<BindingPatternKind<'a>> {
3535
match self.cur_kind() {
3636
Kind::LCurly => self.parse_object_binding_pattern(),
3737
Kind::LBrack => self.parse_array_binding_pattern(),

crates/oxc_parser/src/ts/statement.rs

-1
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,6 @@ impl<'a> ParserImpl<'a> {
396396

397397
pub(crate) fn parse_ts_type_assertion(&mut self) -> Result<Expression<'a>> {
398398
let span = self.start_span();
399-
self.re_lex_ts_l_angle();
400399
self.expect(Kind::LAngle)?;
401400
let type_annotation = self.parse_ts_type()?;
402401
self.expect(Kind::RAngle)?;

crates/oxc_parser/src/ts/types.rs

+80-100
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use super::list::{
99
};
1010
use crate::{
1111
diagnostics,
12-
js::list::{ArrayPatternList, ObjectPatternProperties},
1312
lexer::Kind,
1413
list::{NormalList, SeparatedList},
1514
modifiers::ModifierFlags,
@@ -19,18 +18,91 @@ use crate::{
1918

2019
impl<'a> ParserImpl<'a> {
2120
pub(crate) fn parse_ts_type(&mut self) -> Result<TSType<'a>> {
22-
if self.is_at_constructor_type() {
23-
return self.parse_ts_constructor_type();
21+
if self.is_start_of_function_type_or_constructor_type() {
22+
return self.parse_function_or_constructor_type();
2423
}
24+
let left_span = self.start_span();
25+
let left = self.parse_ts_union_type()?;
26+
self.parse_ts_conditional_type(left_span, left)
27+
}
28+
29+
fn parse_function_or_constructor_type(&mut self) -> Result<TSType<'a>> {
30+
let span = self.start_span();
31+
let r#abstract = self.eat(Kind::Abstract);
32+
let is_constructor_type = self.eat(Kind::New);
33+
let type_parameters = self.parse_ts_type_parameters()?;
34+
let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?;
35+
self.expect(Kind::Arrow)?;
36+
let return_type = {
37+
let return_type_span = self.start_span();
38+
let return_type = self.parse_ts_return_type()?;
39+
self.ast.ts_type_annotation(self.end_span(return_type_span), return_type)
40+
};
2541

26-
if self.is_at_function_type() {
27-
return self.parse_ts_function_type();
42+
let span = self.end_span(span);
43+
Ok(if is_constructor_type {
44+
if let Some(this_param) = &this_param {
45+
// type Foo = new (this: number) => any;
46+
self.error(diagnostics::ts_constructor_this_parameter(this_param.span));
47+
}
48+
self.ast.ts_constructor_type(span, r#abstract, params, return_type, type_parameters)
49+
} else {
50+
self.ast.ts_function_type(span, this_param, params, return_type, type_parameters)
51+
})
52+
}
53+
54+
fn is_start_of_function_type_or_constructor_type(&mut self) -> bool {
55+
if self.at(Kind::LAngle) {
56+
return true;
57+
}
58+
if self.at(Kind::LParen) && self.lookahead(Self::is_unambiguously_start_of_function_type) {
59+
return true;
2860
}
61+
self.at(Kind::New) || (self.at(Kind::Abstract) && self.peek_at(Kind::New))
62+
}
2963

30-
let left_span = self.start_span();
31-
let left = self.parse_ts_union_type()?;
64+
fn is_unambiguously_start_of_function_type(&mut self) -> bool {
65+
self.bump_any();
66+
// ( )
67+
// ( ...
68+
if matches!(self.cur_kind(), Kind::RParen | Kind::Dot3) {
69+
return true;
70+
}
71+
if self.skip_parameter_start() {
72+
// ( xxx :
73+
// ( xxx ,
74+
// ( xxx ?
75+
// ( xxx =
76+
if matches!(self.cur_kind(), Kind::Colon | Kind::Comma | Kind::Question | Kind::Eq) {
77+
return true;
78+
}
79+
// ( xxx ) =>
80+
if self.eat(Kind::RParen) && self.at(Kind::Arrow) {
81+
return true;
82+
}
83+
}
84+
false
85+
}
3286

33-
self.parse_ts_conditional_type(left_span, left)
87+
fn skip_parameter_start(&mut self) -> bool {
88+
// Skip modifiers
89+
loop {
90+
if self.cur_kind().is_modifier_kind() && !self.peek_at(Kind::Comma) {
91+
self.bump_any();
92+
} else {
93+
break;
94+
}
95+
}
96+
if self.cur_kind().is_identifier() || self.at(Kind::This) {
97+
self.bump_any();
98+
return true;
99+
}
100+
if matches!(self.cur_kind(), Kind::LBrack | Kind::LCurly)
101+
&& self.parse_binding_pattern_kind().is_ok()
102+
{
103+
return true;
104+
}
105+
false
34106
}
35107

36108
pub(crate) fn parse_ts_type_parameters(
@@ -162,10 +234,6 @@ impl<'a> ParserImpl<'a> {
162234
Ok(left)
163235
}
164236

165-
fn is_at_constructor_type(&mut self) -> bool {
166-
self.at(Kind::New) || (self.at(Kind::Abstract) && self.peek_at(Kind::New))
167-
}
168-
169237
// test ts ts_union_type
170238
// type A = string | number;
171239
// type B = | A | void | null;
@@ -474,51 +542,6 @@ impl<'a> ParserImpl<'a> {
474542
Ok(self.ast.ts_tuple_type(self.end_span(span), elements))
475543
}
476544

477-
fn is_at_function_type(&mut self) -> bool {
478-
if self.at(Kind::LAngle) {
479-
return true;
480-
}
481-
482-
if !self.at(Kind::LParen) {
483-
return false;
484-
}
485-
486-
let checkpoint = self.checkpoint();
487-
488-
self.bump_any(); // bump (
489-
490-
if self.at(Kind::RParen) || self.at(Kind::Dot3) {
491-
self.rewind(checkpoint);
492-
return true;
493-
}
494-
495-
let mut is_function_parameter_start =
496-
self.at(Kind::This) || self.cur_kind().is_binding_identifier();
497-
498-
if is_function_parameter_start {
499-
self.bump_any();
500-
}
501-
502-
if match self.cur_kind() {
503-
Kind::LBrack => ArrayPatternList::parse(self).is_ok(),
504-
Kind::LCurly => ObjectPatternProperties::parse(self).is_ok(),
505-
_ => false,
506-
} {
507-
is_function_parameter_start = true;
508-
}
509-
510-
let result = if is_function_parameter_start {
511-
matches!(self.cur_kind(), Kind::Colon | Kind::Eq | Kind::Comma | Kind::Question)
512-
|| (self.at(Kind::RParen) && self.peek_at(Kind::Arrow))
513-
} else {
514-
false
515-
};
516-
517-
self.rewind(checkpoint);
518-
519-
result
520-
}
521-
522545
fn is_at_mapped_type(&mut self) -> bool {
523546
if !self.at(Kind::LCurly) {
524547
return false;
@@ -731,49 +754,6 @@ impl<'a> ParserImpl<'a> {
731754
Ok(TSImportAttributes { span, elements })
732755
}
733756

734-
fn parse_ts_constructor_type(&mut self) -> Result<TSType<'a>> {
735-
let span = self.start_span();
736-
let r#abstract = self.eat(Kind::Abstract);
737-
self.expect(Kind::New)?;
738-
let type_parameters = self.parse_ts_type_parameters()?;
739-
let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?;
740-
741-
if let Some(this_param) = this_param {
742-
// type Foo = new (this: number) => any;
743-
self.error(diagnostics::ts_constructor_this_parameter(this_param.span));
744-
}
745-
746-
self.expect(Kind::Arrow)?;
747-
let return_type_span = self.start_span();
748-
let return_type = self.parse_ts_return_type()?;
749-
let return_type = self.ast.ts_type_annotation(self.end_span(return_type_span), return_type);
750-
751-
Ok(self.ast.ts_constructor_type(
752-
self.end_span(span),
753-
r#abstract,
754-
params,
755-
return_type,
756-
type_parameters,
757-
))
758-
}
759-
760-
fn parse_ts_function_type(&mut self) -> Result<TSType<'a>> {
761-
let span = self.start_span();
762-
let type_parameters = self.parse_ts_type_parameters()?;
763-
let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?;
764-
let return_type_span = self.start_span();
765-
self.expect(Kind::Arrow)?;
766-
let return_type = self.parse_ts_return_type()?;
767-
let return_type = self.ast.ts_type_annotation(self.end_span(return_type_span), return_type);
768-
Ok(self.ast.ts_function_type(
769-
self.end_span(span),
770-
this_param,
771-
params,
772-
return_type,
773-
type_parameters,
774-
))
775-
}
776-
777757
fn parse_ts_infer_type(&mut self) -> Result<TSType<'a>> {
778758
let span = self.start_span();
779759
self.expect(Kind::Infer)?;

crates/oxc_semantic/src/checker/typescript.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ pub fn check_formal_parameters(params: &FormalParameters, ctx: &SemanticBuilder<
4848
check_duplicate_bound_names(params, ctx);
4949
}
5050

51-
let is_inside_constructor = ctx.current_scope_flags().is_constructor();
51+
let is_inside_constructor =
52+
!params.kind.is_signature() && ctx.current_scope_flags().is_constructor();
5253
let mut has_optional = false;
5354

5455
for item in &params.items {

tasks/coverage/parser_typescript.snap

+12-16
Original file line numberDiff line numberDiff line change
@@ -3999,20 +3999,18 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
39993999
2 │ }
40004000
╰────
40014001

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

4010-
× Expected `)` but found `Identifier`
4011-
╭─[compiler/ParameterList6.ts:2:26]
4009+
× A parameter property is only allowed in a constructor implementation.
4010+
╭─[compiler/ParameterList6.ts:2:19]
40124011
1 │ class C {
40134012
2 │ constructor(C: (public A) => any) {
4014-
· ┬
4015-
· ╰── `)` expected
4013+
· ────────
40164014
3 │ }
40174015
╰────
40184016

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

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

17736-
× Expected `)` but found `Identifier`
17737-
╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList6.ts:2:26]
17733+
× A parameter property is only allowed in a constructor implementation.
17734+
╭─[conformance/parser/ecmascript5/ParameterLists/parserParameterList6.ts:2:19]
1773817735
1 │ class C {
1773917736
2 │ constructor(C: (public A) => any) {
17740-
· ┬
17741-
· ╰── `)` expected
17737+
· ────────
1774217738
3 │ }
1774317739
╰────
1774417740

0 commit comments

Comments
 (0)