mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(parser): specialize known-token parsing (#28286)
Callers already establish these token kinds, so specialized parsing avoids repeating classification while retaining contextual identifier checks. AI-assisted implementation.
This commit is contained in:
1 parent
91a638d109
commit
c903f5c0e1
17 files changed
+102
-38
No files matched your search
@@ -37,7 +37,13 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
if self.at(Kind::Async) && self.is_un_parenthesized_async_arrow_function_worker() {
|
||||
let start = self.cur_start();
|
||||
self.bump_any(); // bump `async`
|
||||
let expr = self.parse_binary_expression_or_higher(Precedence::Comma);
|
||||
// Lookahead established a single binding identifier followed by `=>`.
|
||||
// An ordinary identifier cannot extend into a member, call, or binary expression here.
|
||||
let expr = if self.at(Kind::Ident) {
|
||||
self.parse_plain_identifier_expression()
|
||||
} else {
|
||||
self.parse_binary_expression_or_higher(Precedence::Comma)
|
||||
};
|
||||
let Expression::Identifier(ident) = &expr else {
|
||||
return self.unexpected();
|
||||
};
|
||||
|
||||
@@ -24,6 +24,10 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
|
||||
pub(crate) fn parse_binding_pattern(&mut self) -> BindingPattern<'a> {
|
||||
match self.cur_kind() {
|
||||
Kind::Ident => {
|
||||
let ident = self.parse_plain_binding_identifier();
|
||||
BindingPattern::BindingIdentifier(self.alloc(ident))
|
||||
}
|
||||
Kind::LCurly => self.parse_object_binding_pattern(),
|
||||
Kind::LBrack => self.parse_array_binding_pattern(),
|
||||
_ => self.parse_binding_pattern_identifier(),
|
||||
|
||||
@@ -77,7 +77,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
&& !(self.at(Kind::Implements)
|
||||
&& self.lexer.peek_token().kind().is_identifier_or_keyword())
|
||||
{
|
||||
Some(self.parse_binding_identifier())
|
||||
Some(self.parse_known_binding_identifier())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -602,14 +602,14 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
|
||||
fn parse_constructor_name(&mut self) -> Option<PropertyKey<'a>> {
|
||||
if self.at(Kind::Constructor) {
|
||||
let ident = self.parse_identifier_name();
|
||||
let ident = self.parse_known_identifier_name();
|
||||
return Some(PropertyKey::StaticIdentifier(self.alloc(ident)));
|
||||
}
|
||||
if self.at(Kind::Str)
|
||||
&& self.cur_js_string() == "constructor"
|
||||
&& self.lexer.peek_token().kind() == Kind::LParen
|
||||
{
|
||||
let string_literal = self.parse_literal_string();
|
||||
let string_literal = self.parse_known_literal_string();
|
||||
return Some(PropertyKey::StringLiteral(self.alloc(string_literal)));
|
||||
}
|
||||
None
|
||||
|
||||
@@ -81,6 +81,14 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
Expression::Identifier(self.alloc(ident))
|
||||
}
|
||||
|
||||
#[expect(clippy::inline_always)]
|
||||
#[inline(always)]
|
||||
pub(super) fn parse_plain_identifier_expression(&mut self) -> Expression<'a> {
|
||||
debug_assert!(self.at(Kind::Ident));
|
||||
let (span, name) = self.parse_identifier_kind(Kind::Ident);
|
||||
Expression::Identifier(self.alloc(IdentifierReference::new(span, name, self)))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_identifier_reference(&mut self) -> IdentifierReference<'a> {
|
||||
// allow `await` and `yield`, let semantic analysis report error
|
||||
let kind = self.cur_kind();
|
||||
@@ -111,7 +119,25 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
self.unexpected()
|
||||
};
|
||||
}
|
||||
self.check_identifier(cur, self.ctx);
|
||||
self.parse_known_binding_identifier()
|
||||
}
|
||||
|
||||
/// Parse a token already known to be a `BindingIdentifier`.
|
||||
/// Membership alone does not rule out the `Await` and `Yield` early errors.
|
||||
#[inline]
|
||||
pub(crate) fn parse_known_binding_identifier(&mut self) -> BindingIdentifier<'a> {
|
||||
let kind = self.cur_kind();
|
||||
debug_assert!(kind.is_binding_identifier());
|
||||
self.check_identifier(kind, self.ctx);
|
||||
let (span, name) = self.parse_identifier_kind(Kind::Ident);
|
||||
BindingIdentifier::new(span, name, self)
|
||||
}
|
||||
|
||||
/// Ordinary identifiers cannot trigger the parser's `Await`/`Yield` checks.
|
||||
/// Strict-mode binding restrictions are still checked by semantic analysis.
|
||||
#[inline]
|
||||
pub(crate) fn parse_plain_binding_identifier(&mut self) -> BindingIdentifier<'a> {
|
||||
debug_assert!(self.at(Kind::Ident));
|
||||
let (span, name) = self.parse_identifier_kind(Kind::Ident);
|
||||
BindingIdentifier::new(span, name, self)
|
||||
}
|
||||
@@ -130,6 +156,13 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
if !self.cur_kind().is_identifier_name() {
|
||||
return self.unexpected();
|
||||
}
|
||||
self.parse_known_identifier_name()
|
||||
}
|
||||
|
||||
/// Parse an `IdentifierName` whose token kind has already been checked.
|
||||
#[inline]
|
||||
pub(crate) fn parse_known_identifier_name(&mut self) -> IdentifierName<'a> {
|
||||
debug_assert!(self.cur_kind().is_identifier_name());
|
||||
let (span, name) = self.parse_identifier_kind(Kind::Ident);
|
||||
IdentifierName::new(span, name, self)
|
||||
}
|
||||
@@ -240,7 +273,8 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
|
||||
let kind = self.cur_kind();
|
||||
match kind {
|
||||
Kind::Ident => self.parse_identifier_expression(), // fast path, keywords are checked at the end
|
||||
// Fast path: `Ident` is already classified as a valid identifier reference.
|
||||
Kind::Ident => self.parse_plain_identifier_expression(),
|
||||
// ArrayLiteral
|
||||
Kind::LBrack => self.parse_array_expression(),
|
||||
// ObjectLiteral
|
||||
@@ -351,7 +385,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let kind = self.cur_kind();
|
||||
match kind {
|
||||
Kind::Str => {
|
||||
let lit = self.parse_literal_string();
|
||||
let lit = self.parse_known_literal_string();
|
||||
Expression::StringLiteral(self.alloc(lit))
|
||||
}
|
||||
Kind::True | Kind::False => Expression::BooleanLiteral(self.parse_literal_boolean()),
|
||||
@@ -503,6 +537,13 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
if !self.at(Kind::Str) {
|
||||
return self.unexpected();
|
||||
}
|
||||
self.parse_known_literal_string()
|
||||
}
|
||||
|
||||
/// Parse a string literal whose `Str` token kind has already been checked.
|
||||
#[inline]
|
||||
pub(crate) fn parse_known_literal_string(&mut self) -> StringLiteral<'a> {
|
||||
debug_assert!(self.at(Kind::Str));
|
||||
let span = self.cur_token().span();
|
||||
let raw = Str::from(self.cur_src());
|
||||
let value = self.cur_js_string();
|
||||
|
||||
@@ -530,6 +530,9 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
generator: bool,
|
||||
) -> Option<BindingIdentifier<'a>> {
|
||||
let kind = self.cur_kind();
|
||||
if kind == Kind::Ident {
|
||||
return Some(self.parse_plain_binding_identifier());
|
||||
}
|
||||
if kind.is_binding_identifier() {
|
||||
let mut ctx = self.ctx;
|
||||
if func_kind.is_expression() {
|
||||
|
||||
@@ -79,7 +79,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let mut identifier_after_import: Option<BindingIdentifier<'_>> =
|
||||
if self.cur_kind().is_binding_identifier() {
|
||||
// `import something ...`
|
||||
Some(self.parse_binding_identifier())
|
||||
Some(self.parse_known_binding_identifier())
|
||||
} else {
|
||||
// `import ...`
|
||||
None
|
||||
@@ -115,7 +115,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
} else if kind.is_binding_identifier() {
|
||||
// `import type something ...`
|
||||
let token = self.cur_token();
|
||||
let identifier_after_type = self.parse_binding_identifier();
|
||||
let identifier_after_type = self.parse_known_binding_identifier();
|
||||
if token.kind() == Kind::From && self.at(Kind::Str) {
|
||||
// `import type from 'source'`
|
||||
if token.escaped() {
|
||||
@@ -167,7 +167,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
token_after_import.span(),
|
||||
));
|
||||
// Parse the identifier as the default specifier to continue recovery
|
||||
identifier_after_import = Some(self.parse_binding_identifier());
|
||||
identifier_after_import = Some(self.parse_known_binding_identifier());
|
||||
phase = Some(ImportPhase::Defer);
|
||||
}
|
||||
// else: `import defer from 'source'` - defer is the binding name, no phase
|
||||
@@ -175,7 +175,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
if self.cur_kind().is_binding_identifier() {
|
||||
// `import source something ...`
|
||||
let token = self.cur_token();
|
||||
let identifier_after_source = self.parse_binding_identifier();
|
||||
let identifier_after_source = self.parse_known_binding_identifier();
|
||||
if token.kind() == Kind::From {
|
||||
// `import source from ...`
|
||||
if self.at(Kind::From) {
|
||||
@@ -402,7 +402,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
fn parse_import_attribute(&mut self) -> ImportAttribute<'a> {
|
||||
let start = self.cur_start();
|
||||
let key = match self.cur_kind() {
|
||||
Kind::Str => ImportAttributeKey::StringLiteral(self.parse_literal_string()),
|
||||
Kind::Str => ImportAttributeKey::StringLiteral(self.parse_known_literal_string()),
|
||||
_ => ImportAttributeKey::Identifier(self.parse_identifier_name()),
|
||||
};
|
||||
self.expect(Kind::Colon);
|
||||
@@ -411,7 +411,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
self.cur_token().span(),
|
||||
));
|
||||
}
|
||||
let value = self.parse_literal_string();
|
||||
let value = self.parse_known_literal_string();
|
||||
ImportAttribute::new(self.end_span(start), key, value, self)
|
||||
}
|
||||
|
||||
@@ -878,10 +878,10 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
// import { type as as as } from "mod"; - isTypeOnly: true, name: as, propertyName: as
|
||||
if self.at(Kind::As) {
|
||||
// { type as ...? }
|
||||
let first_as = self.parse_identifier_name();
|
||||
let first_as = self.parse_known_identifier_name();
|
||||
if self.at(Kind::As) {
|
||||
// { type as as ...? }
|
||||
let second_as = self.parse_identifier_name();
|
||||
let second_as = self.parse_known_identifier_name();
|
||||
if self.can_parse_module_export_name() {
|
||||
// { type as as something }
|
||||
// { type as as "something" }
|
||||
@@ -994,7 +994,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
pub(crate) fn parse_module_export_name(&mut self) -> ModuleExportName<'a> {
|
||||
match self.cur_kind() {
|
||||
Kind::Str => {
|
||||
let literal = self.parse_literal_string();
|
||||
let literal = self.parse_known_literal_string();
|
||||
// ModuleExportName : StringLiteral
|
||||
// It is a Syntax Error if IsStringWellFormedUnicode(the SV of StringLiteral) is false.
|
||||
// The error is fatal so that no AST ever carries an ill-formed export name.
|
||||
|
||||
@@ -154,7 +154,14 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
pub(crate) fn parse_property_name(&mut self) -> (PropertyKey<'a>, bool) {
|
||||
let mut computed = false;
|
||||
let key = match self.cur_kind() {
|
||||
Kind::Str => PropertyKey::from(self.parse_literal_expression()),
|
||||
Kind::Ident => {
|
||||
let ident = self.parse_known_identifier_name();
|
||||
PropertyKey::StaticIdentifier(self.alloc(ident))
|
||||
}
|
||||
Kind::Str => {
|
||||
let literal = self.parse_known_literal_string();
|
||||
PropertyKey::StringLiteral(self.alloc(literal))
|
||||
}
|
||||
kind if kind.is_number() => PropertyKey::from(self.parse_literal_expression()),
|
||||
// { [foo]() {} }
|
||||
Kind::LBrack => {
|
||||
|
||||
@@ -481,7 +481,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
fn parse_jsx_attribute_value(&mut self) -> JSXAttributeValue<'a> {
|
||||
match self.cur_kind() {
|
||||
Kind::Str => {
|
||||
let str_lit = self.parse_literal_string();
|
||||
let str_lit = self.parse_known_literal_string();
|
||||
JSXAttributeValue::StringLiteral(self.alloc(str_lit))
|
||||
}
|
||||
Kind::LCurly => {
|
||||
|
||||
@@ -72,7 +72,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
fn parse_ts_enum_member_name(&mut self) -> TSEnumMemberName<'a> {
|
||||
match self.cur_kind() {
|
||||
Kind::Str => {
|
||||
let literal = self.parse_literal_string();
|
||||
let literal = self.parse_known_literal_string();
|
||||
TSEnumMemberName::String(self.alloc(literal))
|
||||
}
|
||||
Kind::LBrack => match self.parse_computed_property_name() {
|
||||
@@ -459,7 +459,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
start: u32,
|
||||
modifiers: &Modifiers,
|
||||
) -> ArenaBox<'a, TSExternalModuleDeclaration<'a>> {
|
||||
let id = self.parse_literal_string();
|
||||
let id = self.parse_known_literal_string();
|
||||
if !self.ctx.has_ambient() {
|
||||
self.error(diagnostics::quoted_module_name_only_allowed_in_ambient_module(id.span()));
|
||||
}
|
||||
@@ -715,7 +715,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
&& self.cur_kind().is_binding_identifier()
|
||||
{
|
||||
// `import type something ...`
|
||||
identifier = self.parse_binding_identifier();
|
||||
identifier = self.parse_known_binding_identifier();
|
||||
import_kind = ImportOrExportKind::Type;
|
||||
}
|
||||
self.parse_ts_import_equals_declaration(import_kind, identifier, start)
|
||||
|
||||
@@ -799,7 +799,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let parameter_name = if self.at(Kind::This) {
|
||||
TSTypePredicateName::This(self.parse_this_type_node())
|
||||
} else {
|
||||
let ident_name = self.parse_identifier_name();
|
||||
let ident_name = self.parse_known_identifier_name();
|
||||
TSTypePredicateName::Identifier(self.alloc(ident_name))
|
||||
};
|
||||
let mut type_annotation = None;
|
||||
@@ -1005,7 +1005,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
&& self.lookahead(Self::is_next_token_colon_or_question_colon)
|
||||
{
|
||||
let member_start = self.cur_start();
|
||||
let label = self.parse_identifier_name();
|
||||
let label = self.parse_known_identifier_name();
|
||||
let optional = self.eat(Kind::Question);
|
||||
self.expect(Kind::Colon);
|
||||
let type_start = self.cur_start();
|
||||
@@ -1106,7 +1106,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
self.expect(Kind::LParen);
|
||||
|
||||
let source = if self.at(Kind::Str) {
|
||||
self.parse_literal_string()
|
||||
self.parse_known_literal_string()
|
||||
} else {
|
||||
// `StringLiteral` is the only valid type for `TSImportType`'s `source` field. So this is an error.
|
||||
// Fallback to parsing a `TSType`, to obtain a good span for the diagnostic.
|
||||
@@ -1241,7 +1241,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
(key, value, true)
|
||||
} else {
|
||||
let key = if self.at(Kind::Str) {
|
||||
let string_literal = self.parse_literal_string();
|
||||
let string_literal = self.parse_known_literal_string();
|
||||
PropertyKey::StringLiteral(self.alloc(string_literal))
|
||||
} else {
|
||||
let ident = self.parse_identifier_name();
|
||||
@@ -1317,7 +1317,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let parameter_name = if self.at(Kind::This) {
|
||||
TSTypePredicateName::This(self.parse_this_type_node())
|
||||
} else {
|
||||
let ident_name = self.parse_identifier_name();
|
||||
let ident_name = self.parse_known_identifier_name();
|
||||
TSTypePredicateName::Identifier(self.alloc(ident_name))
|
||||
};
|
||||
self.bump_any(); // bump `is`
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
const plain = async foo => ({ foo, "key": foo });
|
||||
const contextual = async get => ({ get, "key": get });
|
||||
const escaped = async \u0066oo => ({ foo, "key": \u0066oo });
|
||||
@@ -1,3 +1,3 @@
|
||||
codegen_misc Summary:
|
||||
AST Parsed : 117/117 (100.00%)
|
||||
Positive Passed: 117/117 (100.00%)
|
||||
AST Parsed : 118/118 (100.00%)
|
||||
Positive Passed: 118/118 (100.00%)
|
||||
@@ -1,3 +1,3 @@
|
||||
formatter_misc Summary:
|
||||
AST Parsed : 117/117 (100.00%)
|
||||
Positive Passed: 117/117 (100.00%)
|
||||
AST Parsed : 118/118 (100.00%)
|
||||
Positive Passed: 118/118 (100.00%)
|
||||
@@ -1,3 +1,3 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 372/372 (100.00%)
|
||||
Positive Passed: 372/372 (100.00%)
|
||||
AST Parsed : 373/373 (100.00%)
|
||||
Positive Passed: 373/373 (100.00%)
|
||||
@@ -1,6 +1,6 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 117/117 (100.00%)
|
||||
Positive Passed: 117/117 (100.00%)
|
||||
AST Parsed : 118/118 (100.00%)
|
||||
Positive Passed: 118/118 (100.00%)
|
||||
Negative Passed: 257/257 (100.00%)
|
||||
|
||||
× TS(1275): 'accessor' modifier can only appear on a property declaration.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
semantic_misc Summary:
|
||||
AST Parsed : 117/117 (100.00%)
|
||||
Positive Passed: 111/117 (94.87%)
|
||||
AST Parsed : 118/118 (100.00%)
|
||||
Positive Passed: 112/118 (94.92%)
|
||||
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["private"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
transformer_misc Summary:
|
||||
AST Parsed : 117/117 (100.00%)
|
||||
Positive Passed: 117/117 (100.00%)
|
||||
AST Parsed : 118/118 (100.00%)
|
||||
Positive Passed: 118/118 (100.00%)
|
||||
Reference in new issue
Block a user