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:
Boshen authored and GitHub committed 2026-10-29 16:30:15 +08:00
1 parent 91a638d109
commit c903f5c0e1
17 files changed
+102 -38

No files matched your search

+7 -1
View File
@@ -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();
};
+4
View File
@@ -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(),
+3 -3
View File
@@ -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
+44 -3
View File
@@ -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();
+3
View File
@@ -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() {
+9 -9
View File
@@ -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.
+8 -1
View File
@@ -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 => {
+1 -1
View File
@@ -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 => {
+3 -3
View File
@@ -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)
+5 -5
View File
@@ -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 });
+2 -2
View File
@@ -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%)
+2 -2
View File
@@ -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%)
+2 -2
View File
@@ -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%)
+2 -2
View File
@@ -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.
+2 -2
View File
@@ -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%)