perf(parser): peek before the await-using lookahead (#23059)

## What

`is_using_statement` is gated only on `Kind::Await` in the statement dispatch (`Kind::Await if self.is_using_statement()`), so **every `await` statement** (`await foo();`) ran a full parser `lookahead` — `checkpoint` + `bump` + `eat` + `rewind` — only to discover it isn't `await using`.

Add a cheap `peek_token` pre-check: `await using` requires `using` immediately after `await` on the same line, so only that case pays for the full lookahead (which still does the binding-identifier check on the token after `using`).

```rust
let next = self.lexer.peek_token();
next.kind() == Kind::Using
    && !next.is_on_new_line()
    && self.lookahead(Self::is_next_token_using_keyword_then_binding_identifier)
```

## Why

`await` statements are extremely common in async code; the previous code paid a parser-level checkpoint/rewind on each. `peek_token` is a cached lexer-level re-lex, much cheaper, and short-circuits before the checkpoint in the overwhelmingly common `await <expr>` case.

## Conformance

No AST change — estree (full AST + spans + token streams) byte-identical to `main`.
This commit is contained in:
Boshen committed 2026-06-07 07:41:14 +00:00
1 parent 388af9d7d5
commit b1711530e9
1 file changed
+7 -1
+7 -1
View File
@@ -38,7 +38,13 @@ impl<'a, C: Config> ParserImpl<'a, C> {
}
pub(crate) fn is_using_statement(&mut self) -> bool {
self.lookahead(Self::is_next_token_using_keyword_then_binding_identifier)
// `await using` requires `using` immediately after `await` on the same line. Cheaply peek
// for it first, so the common `await <expr>` statement avoids the heavier `lookahead`
// (checkpoint + rewind) and only `await using` pays for the binding-identifier check.
let next = self.lexer.peek_token();
next.kind() == Kind::Using
&& !next.is_on_new_line()
&& self.lookahead(Self::is_next_token_using_keyword_then_binding_identifier)
}
fn is_next_token_using_keyword_then_binding_identifier(&mut self) -> bool {