mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(parser): combine parenthesized arrow lookahead (#28412)
Classify parenthesized arrow heads in one lexer checkpoint, avoiding a duplicate token scan. Local arm64 parse time: ordinary corpus −0.67% (−0.39% with tokens); controlled empty arrow fixture −11.7%, parenthesized string fixture +1.4%. Validated parser and Node suites, unchanged conformance and allocations, and 82,200 differential comparisons. AI assistance: Codex.
This commit is contained in:
1 file changed
+97
-91
@@ -64,14 +64,17 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
fn is_parenthesized_arrow_function_expression(&mut self) -> Tristate {
|
||||
match self.cur_kind() {
|
||||
Kind::LParen => {
|
||||
// `(1 + a)` can never be arrow parameters: the leading literal is not the start of
|
||||
// a `BindingElement`, so the worker would bump past it and return `Tristate::False`
|
||||
// (`!second.is_binding_identifier() && second != This`). Skip the lookahead.
|
||||
if self.lexer.peek_token().kind().is_literal() {
|
||||
// This check only reads lexer tokens. Keep one checkpoint for both the
|
||||
// literal fast path and the remaining lookahead, so the first token is read once.
|
||||
let checkpoint = self.lexer.checkpoint();
|
||||
let second = self.lexer.next_token().kind();
|
||||
let result = if second.is_literal() {
|
||||
Tristate::False
|
||||
} else {
|
||||
self.lookahead(Self::is_parenthesized_arrow_function_expression_worker)
|
||||
}
|
||||
self.is_parenthesized_arrow_function_expression_after_lparen(second, false)
|
||||
};
|
||||
self.lexer.rewind(checkpoint);
|
||||
result
|
||||
}
|
||||
Kind::LAngle | Kind::Async => {
|
||||
self.lookahead(Self::is_parenthesized_arrow_function_expression_worker)
|
||||
@@ -80,6 +83,93 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
}
|
||||
}
|
||||
|
||||
// `second` follows `(` and the lexer is positioned after it. Only lexer state changes;
|
||||
// callers must rewind their checkpoint before parsing the expression.
|
||||
fn is_parenthesized_arrow_function_expression_after_lparen(
|
||||
&mut self,
|
||||
second: Kind,
|
||||
is_async: bool,
|
||||
) -> Tristate {
|
||||
match second {
|
||||
// Simple cases: "() =>", "(): ", and "() {".
|
||||
// This is an arrow function with no parameters.
|
||||
// The last one is not actually an arrow function,
|
||||
// but this is probably what the user intended.
|
||||
Kind::RParen => {
|
||||
let third = self.lexer.next_token().kind();
|
||||
match third {
|
||||
// `async()` can be a call on the true side of a conditional.
|
||||
Kind::Colon if self.is_ts && is_async => Tristate::Maybe,
|
||||
// Bare `()` cannot be a parenthesized expression, so the colon
|
||||
// must start a return type, even inside a conditional expression.
|
||||
Kind::Colon if self.is_ts => Tristate::True,
|
||||
Kind::Arrow | Kind::LCurly => Tristate::True,
|
||||
_ => Tristate::False,
|
||||
}
|
||||
}
|
||||
// If encounter "([" or "({", this could be the start of a binding pattern.
|
||||
// Examples:
|
||||
// ([ x ]) => { }
|
||||
// ({ x }) => { }
|
||||
// ([ x ])
|
||||
// ({ x })
|
||||
Kind::LBrack | Kind::LCurly => Tristate::Maybe,
|
||||
// Simple case: "(..."
|
||||
// This is an arrow function with a rest parameter.
|
||||
Kind::Dot3 => {
|
||||
let third = self.lexer.next_token().kind();
|
||||
match third {
|
||||
// '(...ident' is a lambda
|
||||
Kind::Ident => Tristate::True,
|
||||
// '(...null' is not a lambda
|
||||
kind if kind.is_literal() => Tristate::False,
|
||||
_ => Tristate::Maybe,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let third = self.lexer.next_token().kind();
|
||||
|
||||
// Check for "(xxx yyy", where xxx is a modifier and yyy is an identifier. This
|
||||
// isn't actually allowed, but we want to treat it as a lambda so we can provide
|
||||
// a good error message.
|
||||
if second.is_modifier_kind()
|
||||
&& second != Kind::Async
|
||||
&& third.is_binding_identifier()
|
||||
{
|
||||
if third == Kind::As {
|
||||
return Tristate::False; // https://github.com/microsoft/TypeScript/issues/44466
|
||||
}
|
||||
return Tristate::True;
|
||||
}
|
||||
|
||||
// If we had "(" followed by something that's not an identifier,
|
||||
// then this definitely doesn't look like a lambda. "this" is not
|
||||
// valid, but we want to parse it and then give a semantic error.
|
||||
if !second.is_binding_identifier() && second != Kind::This {
|
||||
return Tristate::False;
|
||||
}
|
||||
|
||||
match third {
|
||||
// If we have something like "(a:", then we must have a
|
||||
// type-annotated parameter in an arrow function expression.
|
||||
Kind::Colon => Tristate::True,
|
||||
// If we have "(a?:" or "(a?," or "(a?=" or "(a?)" then it is definitely a lambda.
|
||||
Kind::Question => {
|
||||
let fourth = self.lexer.next_token().kind();
|
||||
if matches!(fourth, Kind::Colon | Kind::Comma | Kind::Eq | Kind::RParen) {
|
||||
return Tristate::True;
|
||||
}
|
||||
Tristate::False
|
||||
}
|
||||
// If we have "(a," or "(a=" or "(a)" this *could* be an arrow function
|
||||
Kind::Comma | Kind::Eq | Kind::RParen => Tristate::Maybe,
|
||||
// It is definitely not an arrow function
|
||||
_ => Tristate::False,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_parenthesized_arrow_function_expression_worker(&mut self) -> Tristate {
|
||||
let is_async = self.eat(Kind::Async);
|
||||
if is_async {
|
||||
@@ -98,91 +188,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
|
||||
match first {
|
||||
Kind::LParen => {
|
||||
match second {
|
||||
// Simple cases: "() =>", "(): ", and "() {".
|
||||
// This is an arrow function with no parameters.
|
||||
// The last one is not actually an arrow function,
|
||||
// but this is probably what the user intended.
|
||||
Kind::RParen => {
|
||||
self.bump_any();
|
||||
let third = self.cur_kind();
|
||||
match third {
|
||||
// `async()` can be a call on the true side of a conditional.
|
||||
Kind::Colon if self.is_ts && is_async => Tristate::Maybe,
|
||||
// Bare `()` cannot be a parenthesized expression, so the colon
|
||||
// must start a return type, even inside a conditional expression.
|
||||
Kind::Colon if self.is_ts => Tristate::True,
|
||||
Kind::Arrow | Kind::LCurly => Tristate::True,
|
||||
_ => Tristate::False,
|
||||
}
|
||||
}
|
||||
// If encounter "([" or "({", this could be the start of a binding pattern.
|
||||
// Examples:
|
||||
// ([ x ]) => { }
|
||||
// ({ x }) => { }
|
||||
// ([ x ])
|
||||
// ({ x })
|
||||
Kind::LBrack | Kind::LCurly => Tristate::Maybe,
|
||||
// Simple case: "(..."
|
||||
// This is an arrow function with a rest parameter.
|
||||
Kind::Dot3 => {
|
||||
self.bump_any();
|
||||
let third = self.cur_kind();
|
||||
match third {
|
||||
// '(...ident' is a lambda
|
||||
Kind::Ident => Tristate::True,
|
||||
// '(...null' is not a lambda
|
||||
kind if kind.is_literal() => Tristate::False,
|
||||
_ => Tristate::Maybe,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.bump_any();
|
||||
let third = self.cur_kind();
|
||||
|
||||
// Check for "(xxx yyy", where xxx is a modifier and yyy is an identifier. This
|
||||
// isn't actually allowed, but we want to treat it as a lambda so we can provide
|
||||
// a good error message.
|
||||
if second.is_modifier_kind()
|
||||
&& second != Kind::Async
|
||||
&& third.is_binding_identifier()
|
||||
{
|
||||
if third == Kind::As {
|
||||
return Tristate::False; // https://github.com/microsoft/TypeScript/issues/44466
|
||||
}
|
||||
return Tristate::True;
|
||||
}
|
||||
|
||||
// If we had "(" followed by something that's not an identifier,
|
||||
// then this definitely doesn't look like a lambda. "this" is not
|
||||
// valid, but we want to parse it and then give a semantic error.
|
||||
if !second.is_binding_identifier() && second != Kind::This {
|
||||
return Tristate::False;
|
||||
}
|
||||
|
||||
match third {
|
||||
// If we have something like "(a:", then we must have a
|
||||
// type-annotated parameter in an arrow function expression.
|
||||
Kind::Colon => Tristate::True,
|
||||
// If we have "(a?:" or "(a?," or "(a?=" or "(a?)" then it is definitely a lambda.
|
||||
Kind::Question => {
|
||||
self.bump_any();
|
||||
let fourth = self.cur_kind();
|
||||
if matches!(
|
||||
fourth,
|
||||
Kind::Colon | Kind::Comma | Kind::Eq | Kind::RParen
|
||||
) {
|
||||
return Tristate::True;
|
||||
}
|
||||
Tristate::False
|
||||
}
|
||||
// If we have "(a," or "(a=" or "(a)" this *could* be an arrow function
|
||||
Kind::Comma | Kind::Eq | Kind::RParen => Tristate::Maybe,
|
||||
// It is definitely not an arrow function
|
||||
_ => Tristate::False,
|
||||
}
|
||||
}
|
||||
}
|
||||
self.is_parenthesized_arrow_function_expression_after_lparen(second, is_async)
|
||||
}
|
||||
Kind::LAngle => {
|
||||
// If we have "<" not followed by an identifier,
|
||||
|
||||
Reference in new issue
Block a user