mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
fix(parser): parse function type parameter name accessor (#3926)
fixes #3910
This commit is contained in:
parent
63b98bddd9
commit
275349a9fe
7 changed files with 180 additions and 50 deletions
|
|
@ -2,6 +2,7 @@ use bitflags::bitflags;
|
||||||
|
|
||||||
use oxc_allocator::Vec;
|
use oxc_allocator::Vec;
|
||||||
use oxc_ast::ast::TSAccessibility;
|
use oxc_ast::ast::TSAccessibility;
|
||||||
|
use oxc_diagnostics::Result;
|
||||||
use oxc_span::Span;
|
use oxc_span::Span;
|
||||||
|
|
||||||
use crate::{lexer::Kind, ParserImpl};
|
use crate::{lexer::Kind, ParserImpl};
|
||||||
|
|
@ -207,58 +208,28 @@ impl<'a> ParserImpl<'a> {
|
||||||
Kind::Export => {
|
Kind::Export => {
|
||||||
self.bump_any();
|
self.bump_any();
|
||||||
match self.cur_kind() {
|
match self.cur_kind() {
|
||||||
Kind::Default => {
|
Kind::Default => self.next_token_can_follow_default_keyword(),
|
||||||
self.bump_any();
|
|
||||||
self.can_follow_default()
|
|
||||||
}
|
|
||||||
Kind::Type => {
|
Kind::Type => {
|
||||||
self.bump_any();
|
self.bump_any();
|
||||||
self.can_follow_export()
|
self.can_follow_export_modifier()
|
||||||
}
|
}
|
||||||
_ => self.can_follow_export(),
|
_ => self.can_follow_modifier(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Kind::Default => {
|
Kind::Default => self.next_token_can_follow_default_keyword(),
|
||||||
self.bump_any();
|
|
||||||
self.can_follow_default()
|
|
||||||
}
|
|
||||||
Kind::Accessor | Kind::Static | Kind::Get | Kind::Set => {
|
Kind::Accessor | Kind::Static | Kind::Get | Kind::Set => {
|
||||||
// These modifiers can cross line.
|
// These modifiers can cross line.
|
||||||
self.bump_any();
|
self.bump_any();
|
||||||
Self::can_follow_modifier(self.cur_kind())
|
self.can_follow_modifier()
|
||||||
}
|
}
|
||||||
// Rest modifiers cannot cross line
|
// Rest modifiers cannot cross line
|
||||||
_ => {
|
_ => {
|
||||||
self.bump_any();
|
self.bump_any();
|
||||||
Self::can_follow_modifier(self.cur_kind()) && !self.cur_token().is_on_new_line
|
self.can_follow_modifier() && !self.cur_token().is_on_new_line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_follow_default(&mut self) -> bool {
|
|
||||||
let at_declaration =
|
|
||||||
matches!(self.cur_kind(), Kind::Class | Kind::Function | Kind::Interface);
|
|
||||||
let at_abstract_declaration = self.at(Kind::Abstract)
|
|
||||||
&& self.peek_at(Kind::Class)
|
|
||||||
&& !self.peek_token().is_on_new_line;
|
|
||||||
let at_async_function = self.at(Kind::Async)
|
|
||||||
&& self.peek_at(Kind::Function)
|
|
||||||
&& !self.peek_token().is_on_new_line;
|
|
||||||
at_declaration | at_abstract_declaration | at_async_function
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_follow_export(&mut self) -> bool {
|
|
||||||
// Note that the `export` in export assignment is not a modifier
|
|
||||||
// and are handled explicitly in the parser.
|
|
||||||
!matches!(self.cur_kind(), Kind::Star | Kind::As | Kind::LCurly)
|
|
||||||
&& Self::can_follow_modifier(self.cur_kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_follow_modifier(kind: Kind) -> bool {
|
|
||||||
kind.is_literal_property_name()
|
|
||||||
|| matches!(kind, Kind::LCurly | Kind::LBrack | Kind::Star | Kind::Dot3)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn modifier(kind: Kind, span: Span) -> Modifier {
|
fn modifier(kind: Kind, span: Span) -> Modifier {
|
||||||
let modifier_kind = match kind {
|
let modifier_kind = match kind {
|
||||||
Kind::Abstract => ModifierKind::Abstract,
|
Kind::Abstract => ModifierKind::Abstract,
|
||||||
|
|
@ -280,4 +251,165 @@ impl<'a> ParserImpl<'a> {
|
||||||
};
|
};
|
||||||
Modifier { span, kind: modifier_kind }
|
Modifier { span, kind: modifier_kind }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_modifiers(
|
||||||
|
&mut self,
|
||||||
|
_allow_decorators: bool,
|
||||||
|
permit_const_as_modifier: bool,
|
||||||
|
stop_on_start_of_class_static_block: bool,
|
||||||
|
) -> Modifiers<'a> {
|
||||||
|
let mut has_seen_static_modifier = false;
|
||||||
|
// let mut has_leading_modifier = false;
|
||||||
|
// let mut has_trailing_decorator = false;
|
||||||
|
let mut modifiers = self.ast.new_vec();
|
||||||
|
|
||||||
|
// parse leading decorators
|
||||||
|
// if (allowDecorators && token() === SyntaxKind.AtToken) {
|
||||||
|
// while (decorator = tryParseDecorator()) {
|
||||||
|
// list = append(list, decorator);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// parse leading modifiers
|
||||||
|
while let Some(modifier) = self.try_parse_modifier(
|
||||||
|
has_seen_static_modifier,
|
||||||
|
permit_const_as_modifier,
|
||||||
|
stop_on_start_of_class_static_block,
|
||||||
|
) {
|
||||||
|
if modifier.kind == ModifierKind::Static {
|
||||||
|
has_seen_static_modifier = true;
|
||||||
|
}
|
||||||
|
modifiers.push(modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse trailing decorators, but only if we parsed any leading modifiers
|
||||||
|
// if (hasLeadingModifier && allowDecorators && token() === SyntaxKind.AtToken) {
|
||||||
|
// while (decorator = tryParseDecorator()) {
|
||||||
|
// list = append(list, decorator);
|
||||||
|
// hasTrailingDecorator = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// parse trailing modifiers, but only if we parsed any trailing decorators
|
||||||
|
// if (hasTrailingDecorator) {
|
||||||
|
// while (modifier = tryParseModifier(hasSeenStaticModifier, permitConstAsModifier, stopOnStartOfClassStaticBlock)) {
|
||||||
|
// if (modifier.kind === SyntaxKind.StaticKeyword) hasSeenStaticModifier = true;
|
||||||
|
// list = append(list, modifier);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
Modifiers::new(modifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_parse_modifier(
|
||||||
|
&mut self,
|
||||||
|
_has_seen_static_modifier: bool,
|
||||||
|
_permit_const_as_modifier: bool,
|
||||||
|
_stop_on_start_of_class_static_block: bool,
|
||||||
|
) -> Option<Modifier> {
|
||||||
|
let span = self.start_span();
|
||||||
|
let kind = self.cur_kind();
|
||||||
|
// if (token() === SyntaxKind.ConstKeyword && permitConstAsModifier) {
|
||||||
|
// We need to ensure that any subsequent modifiers appear on the same line
|
||||||
|
// so that when 'const' is a standalone declaration, we don't issue an error.
|
||||||
|
// if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) {
|
||||||
|
// return undefined;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else if (stopOnStartOfClassStaticBlock && token() === SyntaxKind.StaticKeyword && lookAhead(nextTokenIsOpenBrace)) {
|
||||||
|
// return undefined;
|
||||||
|
// }
|
||||||
|
// else if (hasSeenStaticModifier && token() === SyntaxKind.StaticKeyword) {
|
||||||
|
// return undefined;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
if !self.parse_any_contextual_modifier() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
Some(Self::modifier(kind, self.end_span(span)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_any_contextual_modifier(&mut self) -> bool {
|
||||||
|
self.cur_kind().is_modifier_kind()
|
||||||
|
&& self.try_parse(Self::next_token_can_follow_modifier).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token_can_follow_modifier(&mut self) -> Result<()> {
|
||||||
|
let b = match self.cur_kind() {
|
||||||
|
Kind::Const => self.peek_at(Kind::Enum),
|
||||||
|
Kind::Export => {
|
||||||
|
self.bump_any();
|
||||||
|
match self.cur_kind() {
|
||||||
|
Kind::Default => self.lookahead(Self::next_token_can_follow_default_keyword),
|
||||||
|
Kind::Type => self.lookahead(Self::next_token_can_follow_export_modifier),
|
||||||
|
_ => self.can_follow_export_modifier(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Kind::Default => self.next_token_can_follow_default_keyword(),
|
||||||
|
Kind::Static | Kind::Get | Kind::Set => {
|
||||||
|
self.bump_any();
|
||||||
|
self.can_follow_modifier()
|
||||||
|
}
|
||||||
|
_ => self.next_token_is_on_same_line_and_can_follow_modifier(),
|
||||||
|
};
|
||||||
|
if b {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(self.unexpected())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token_is_on_same_line_and_can_follow_modifier(&mut self) -> bool {
|
||||||
|
self.bump_any();
|
||||||
|
if self.cur_token().is_on_new_line {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.can_follow_modifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token_can_follow_default_keyword(&mut self) -> bool {
|
||||||
|
self.bump_any();
|
||||||
|
match self.cur_kind() {
|
||||||
|
Kind::Class | Kind::Function | Kind::Interface | Kind::At => true,
|
||||||
|
Kind::Abstract if self.lookahead(Self::next_token_is_class_keyword_on_same_line) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Kind::Async if self.lookahead(Self::next_token_is_function_keyword_on_same_line) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token_can_follow_export_modifier(&mut self) -> bool {
|
||||||
|
self.bump_any();
|
||||||
|
self.can_follow_export_modifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_follow_export_modifier(&mut self) -> bool {
|
||||||
|
let kind = self.cur_kind();
|
||||||
|
kind == Kind::At
|
||||||
|
&& kind != Kind::Star
|
||||||
|
&& kind != Kind::As
|
||||||
|
&& kind != Kind::LCurly
|
||||||
|
&& self.can_follow_modifier()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_follow_modifier(&mut self) -> bool {
|
||||||
|
match self.cur_kind() {
|
||||||
|
Kind::LBrack | Kind::LCurly | Kind::Star | Kind::Dot3 => true,
|
||||||
|
kind => kind.is_literal_property_name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token_is_class_keyword_on_same_line(&mut self) -> bool {
|
||||||
|
self.bump_any();
|
||||||
|
self.cur_kind() == Kind::Class && !self.cur_token().is_on_new_line
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_token_is_function_keyword_on_same_line(&mut self) -> bool {
|
||||||
|
self.bump_any();
|
||||||
|
self.cur_kind() == Kind::Function && !self.cur_token().is_on_new_line
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,12 +118,8 @@ impl<'a> ParserImpl<'a> {
|
||||||
|
|
||||||
fn skip_parameter_start(&mut self) -> bool {
|
fn skip_parameter_start(&mut self) -> bool {
|
||||||
// Skip modifiers
|
// Skip modifiers
|
||||||
loop {
|
if self.cur_kind().is_modifier_kind() {
|
||||||
if self.cur_kind().is_modifier_kind() && !self.peek_at(Kind::Comma) {
|
self.parse_modifiers(false, false, false);
|
||||||
self.bump_any();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if self.cur_kind().is_identifier() || self.at(Kind::This) {
|
if self.cur_kind().is_identifier() || self.at(Kind::This) {
|
||||||
self.bump_any();
|
self.bump_any();
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
codegen_misc Summary:
|
codegen_misc Summary:
|
||||||
AST Parsed : 18/18 (100.00%)
|
AST Parsed : 19/19 (100.00%)
|
||||||
Positive Passed: 18/18 (100.00%)
|
Positive Passed: 19/19 (100.00%)
|
||||||
|
|
|
||||||
1
tasks/coverage/misc/pass/oxc-3910.ts
Normal file
1
tasks/coverage/misc/pass/oxc-3910.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
class Foo { private _(__: (accessor: ServicesAccessor) => unknown): void { } }
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
parser_misc Summary:
|
parser_misc Summary:
|
||||||
AST Parsed : 18/18 (100.00%)
|
AST Parsed : 19/19 (100.00%)
|
||||||
Positive Passed: 18/18 (100.00%)
|
Positive Passed: 19/19 (100.00%)
|
||||||
Negative Passed: 10/10 (100.00%)
|
Negative Passed: 10/10 (100.00%)
|
||||||
|
|
||||||
× Unexpected token
|
× Unexpected token
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
prettier_misc Summary:
|
prettier_misc Summary:
|
||||||
AST Parsed : 18/18 (100.00%)
|
AST Parsed : 19/19 (100.00%)
|
||||||
Positive Passed: 11/18 (61.11%)
|
Positive Passed: 11/19 (57.89%)
|
||||||
Expect to Parse: "pass/oxc-1740.tsx"
|
Expect to Parse: "pass/oxc-1740.tsx"
|
||||||
Expect to Parse: "pass/oxc-2087.ts"
|
Expect to Parse: "pass/oxc-2087.ts"
|
||||||
Expect to Parse: "pass/oxc-2394.ts"
|
Expect to Parse: "pass/oxc-2394.ts"
|
||||||
Expect to Parse: "pass/oxc-2674.tsx"
|
Expect to Parse: "pass/oxc-2674.tsx"
|
||||||
Expect to Parse: "pass/oxc-2723.jsx"
|
Expect to Parse: "pass/oxc-2723.jsx"
|
||||||
|
Expect to Parse: "pass/oxc-3910.ts"
|
||||||
Expect to Parse: "pass/swc-1627.js"
|
Expect to Parse: "pass/swc-1627.js"
|
||||||
Expect to Parse: "pass/swc-8243.tsx"
|
Expect to Parse: "pass/swc-8243.tsx"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
transformer_misc Summary:
|
transformer_misc Summary:
|
||||||
AST Parsed : 18/18 (100.00%)
|
AST Parsed : 19/19 (100.00%)
|
||||||
Positive Passed: 18/18 (100.00%)
|
Positive Passed: 19/19 (100.00%)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue