fix(parser): recover await using object binding patterns (#26721)

Recognize object binding patterns after `await using` so invalid declarations retain their AST and report “Using declarations may not have binding patterns.” For example, `for (await using {} of []) {}` now recovers instead of aborting with an expected-semicolon error. Updates the existing Babel, Test262, and TypeScript diagnostic snapshots.

Known limitation: transforming the recovered loop with resource-management lowering enabled (`target: es2015`) still panics because the transformer assumes an identifier binding. The parser diagnostic is emitted before the panic.
This commit is contained in:
camc314 committed 2026-09-16 08:00:01 +00:00
1 parent 432f7606c8
commit 313040530c
4 files changed
+37 -29

No files matched your search

+6 -11
View File
@@ -40,20 +40,15 @@ impl<'a, C: Config> ParserImpl<'a, C> {
pub(crate) fn is_using_statement(&mut self) -> bool {
// `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.
// (checkpoint + rewind) and only `await using` pays for the declaration 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 {
self.bump_any();
if !self.cur_token().is_on_new_line() && self.eat(Kind::Using) {
self.cur_kind().is_binding_identifier() && !self.cur_token().is_on_new_line()
} else {
false
}
&& self.lookahead(|p| {
p.bump_any(); // bump `await`
// Also recognize object patterns so invalid declarations can recover.
p.is_using_declaration()
})
}
pub(crate) fn parse_using_statement(&mut self, stmt_ctx: StatementContext) -> Statement<'a> {
+23 -7
View File
@@ -9136,14 +9136,13 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Add an initializer (e.g. ` = undefined`) here
× Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-using-object-pattern/input.js:2:14]
× Using declarations may not have binding patterns.
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-using-object-pattern/input.js:2:15]
1 │ async function f() {
2 │ await using { foo } = f();
· ▲
· ───────
3 │ }
╰────
help: Try inserting a semicolon here
× Using declarations may not have binding patterns.
╭─[babel/packages/babel-parser/test/fixtures/es2026/explicit-resource-management/disallow-destructuring/input.js:1:17]
@@ -15732,11 +15731,28 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Try inserting a semicolon here
× Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[babel/packages/babel-parser/test/fixtures/typescript/variable-declarator/invalid-definite-pattern-assignment-await-using/input.ts:2:14]
× Missing initializer in destructuring declaration
╭─[babel/packages/babel-parser/test/fixtures/typescript/variable-declarator/invalid-definite-pattern-assignment-await-using/input.ts:2:15]
1 │ {
2 │ await using {}! = {};
· ▲
· ──
3 │ }
╰────
help: Add an initializer (e.g. ` = undefined`) here
× Using declarations may not have binding patterns.
╭─[babel/packages/babel-parser/test/fixtures/typescript/variable-declarator/invalid-definite-pattern-assignment-await-using/input.ts:2:15]
1 │ {
2 │ await using {}! = {};
· ──
3 │ }
╰────
× Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[babel/packages/babel-parser/test/fixtures/typescript/variable-declarator/invalid-definite-pattern-assignment-await-using/input.ts:2:17]
1 │ {
2 │ await using {}! = {};
· ▲
3 │ }
╰────
help: Try inserting a semicolon here
+3 -4
View File
@@ -27684,14 +27684,13 @@ Negative Passed: 4651/4651 (100.00%)
18 │ }
╰────
× Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[test262/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern.js:17:14]
× Using declarations may not have binding patterns.
╭─[test262/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern.js:17:15]
16 │ async function f() {
17 │ await using {} = null;
· ▲
· ──
18 │ }
╰────
help: Try inserting a semicolon here
× 'await using' declaration cannot appear in the bare case statement.
╭─[test262/test/language/statements/await-using/syntax/await-using-invalid-switchstatement-caseclause.js:22:7]
@@ -28964,14 +28964,13 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
4 │ c = null;
╰────
× Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.6.ts:2:16]
× Using declarations may not have binding patterns.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.6.ts:2:17]
1 │ {
2 │ await using {a} = null;
· ▲
· ───
3 │ }
╰────
help: Try inserting a semicolon here
× Using declarations may not have binding patterns.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.7.ts:3:17]
@@ -29008,12 +29007,11 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: Did you mean to use a for...of statement?
× Expected `;` but found `{`
× Using declarations may not have binding patterns.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarationsInForOf.3.ts:2:22]
1 │ async function main() {
2 │ for (await using {} of []) {
· ┬
· ╰── `;` expected
· ──
3 │ }
╰────