fix(parser): validate await context for await using declarations (#26495)

Reject `await using` in non-async functions and ordinary generators, including `for` and `for...of` loop heads. Treat top-level `await using` as module syntax when the source type is unambiguous.
This commit is contained in:
camc314
2026-09-09 15:21:18 +00:00
parent c068fd2f44
commit a091fc4b42
14 changed files with 258 additions and 39 deletions
+10
View File
@@ -189,6 +189,16 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let is_await = self.eat(Kind::Await);
let kind = if is_await {
if !self.ctx.has_await() {
let error = diagnostics::await_expression(Span::sized(start, 5));
if self.ctx.has_top_level() {
// Top-level `await using` is module syntax in unambiguous mode.
self.module_record_builder.set_module_syntax();
self.error_on_script(error);
} else {
self.error(error);
}
}
VariableDeclarationKind::AwaitUsing
} else {
VariableDeclarationKind::Using
@@ -7,7 +7,7 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/js/declarations/using.js
"children": [
{
"children": [],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 1,
"node": "BlockStatement",
"symbols": [
@@ -22,7 +22,7 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/js/declarations/using.js
},
{
"children": [],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 2,
"node": "BlockStatement",
"symbols": [
@@ -37,7 +37,7 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/js/declarations/using.js
},
{
"children": [],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 3,
"node": "BlockStatement",
"symbols": [
@@ -52,7 +52,7 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/js/declarations/using.js
},
{
"children": [],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 4,
"node": "BlockStatement",
"symbols": [
@@ -69,13 +69,13 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/js/declarations/using.js
"children": [
{
"children": [],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 6,
"node": "BlockStatement",
"symbols": []
}
],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 5,
"node": "ForOfStatement",
"symbols": [
@@ -92,13 +92,13 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/js/declarations/using.js
"children": [
{
"children": [],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 8,
"node": "BlockStatement",
"symbols": []
}
],
"flags": "ScopeFlags(0x0)",
"flags": "ScopeFlags(StrictMode)",
"id": 7,
"node": "ForOfStatement",
"symbols": [
@@ -112,7 +112,7 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/js/declarations/using.js
]
}
],
"flags": "ScopeFlags(Top)",
"flags": "ScopeFlags(StrictMode | Top)",
"id": 0,
"node": "Program",
"symbols": []
@@ -0,0 +1,11 @@
function f() {
await using x = null;
for (await using y = null; ; ) {}
for (await using z of []) {}
}
function* g() {
await using x = null;
for (await using y = null; ; ) {}
for (await using z of []) {}
}
@@ -0,0 +1 @@
await using x = null;
+2 -2
View File
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 97/97 (100.00%)
Positive Passed: 97/97 (100.00%)
AST Parsed : 98/98 (100.00%)
Positive Passed: 98/98 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
formatter_misc Summary:
AST Parsed : 97/97 (100.00%)
Positive Passed: 97/97 (100.00%)
AST Parsed : 98/98 (100.00%)
Positive Passed: 98/98 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
lexer_misc Summary:
AST Parsed : 297/297 (100.00%)
Positive Passed: 297/297 (100.00%)
AST Parsed : 299/299 (100.00%)
Positive Passed: 299/299 (100.00%)
+104
View File
@@ -8823,6 +8823,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:2:13]
1 │ {
2 │ while (1) await using a = foo;
· ─────
3 │ for (;0;) await using b = foo;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Lexical declaration cannot appear in a single-statement context
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:2:13]
1 │ {
@@ -8832,6 +8841,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:3:13]
2 │ while (1) await using a = foo;
3 │ for (;0;) await using b = foo;
· ─────
4 │ do await using c = foo; while (1);
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Lexical declaration cannot appear in a single-statement context
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:3:13]
2 │ while (1) await using a = foo;
@@ -8841,6 +8859,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:4:6]
3 │ for (;0;) await using b = foo;
4 │ do await using c = foo; while (1);
· ─────
5 │ if (1) await using d = foo;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Lexical declaration cannot appear in a single-statement context
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:4:6]
3 │ for (;0;) await using b = foo;
@@ -8850,6 +8877,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:5:10]
4 │ do await using c = foo; while (1);
5 │ if (1) await using d = foo;
· ─────
6 │ with (1) await using e = foo;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Lexical declaration cannot appear in a single-statement context
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:5:10]
4 │ do await using c = foo; while (1);
@@ -8859,6 +8895,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:6:12]
5 │ if (1) await using d = foo;
6 │ with (1) await using e = foo;
· ─────
7 │ label: await using f = foo;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Lexical declaration cannot appear in a single-statement context
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:6:12]
5 │ if (1) await using d = foo;
@@ -8868,6 +8913,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:7:10]
6 │ with (1) await using e = foo;
7 │ label: await using f = foo;
· ─────
8 │ }
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Lexical declaration cannot appear in a single-statement context
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-in-single-statement-context-sync/input.js:7:10]
6 │ with (1) await using e = foo;
@@ -8913,6 +8967,13 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-script-top-level-labeled-using-binding/input.js:1:8]
1 │ label: await using x = bar();
· ─────
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Lexical declaration cannot appear in a single-statement context
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-script-top-level-labeled-using-binding/input.js:1:8]
1 │ label: await using x = bar();
@@ -8927,6 +8988,13 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Wrap this code in a block or use a module
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-script-top-level-using-binding/input.js:1:1]
1 │ await using x = bar();
· ─────
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× 'using' declarations are not allowed at the top level of a script
╭─[babel/packages/babel-parser/test/fixtures/es2026/async-explicit-resource-management/invalid-script-top-level-using-binding/input.js:1:1]
1 │ await using x = bar();
@@ -13786,6 +13854,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Try inserting a semicolon here
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:2:3]
1 │ declare namespace invalid_namespace_var {
2 │ await using A;
· ─────
3 │ await using A1 = 0;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:2:15]
1 │ declare namespace invalid_namespace_var {
@@ -13794,6 +13871,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
3 │ await using A1 = 0;
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:3:3]
2 │ await using A;
3 │ await using A1 = 0;
· ─────
4 │ await using A2: number = 0;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:3:15]
2 │ await using A;
@@ -13802,6 +13888,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
4 │ await using A2: number = 0;
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:4:3]
3 │ await using A1 = 0;
4 │ await using A2: number = 0;
· ─────
5 │ await using B, C;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:4:15]
3 │ await using A1 = 0;
@@ -13810,6 +13905,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
5 │ await using B, C;
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:5:3]
4 │ await using A2: number = 0;
5 │ await using B, C;
· ─────
6 │ }
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/invalid-namespace-await-using/input.ts:5:15]
4 │ await using A2: number = 0;
+57 -3
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 97/97 (100.00%)
Positive Passed: 97/97 (100.00%)
Negative Passed: 200/200 (100.00%)
AST Parsed : 98/98 (100.00%)
Positive Passed: 98/98 (100.00%)
Negative Passed: 201/201 (100.00%)
× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
@@ -228,6 +228,60 @@ Negative Passed: 200/200 (100.00%)
5 │ }
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[misc/fail/await-using-context.js:2:3]
1 │ function f() {
2 │ await using x = null;
· ─────
3 │ for (await using y = null; ; ) {}
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× `await` is only allowed within async functions and at the top levels of modules
╭─[misc/fail/await-using-context.js:3:8]
2 │ await using x = null;
3 │ for (await using y = null; ; ) {}
· ─────
4 │ for (await using z of []) {}
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× `await` is only allowed within async functions and at the top levels of modules
╭─[misc/fail/await-using-context.js:4:8]
3 │ for (await using y = null; ; ) {}
4 │ for (await using z of []) {}
· ─────
5 │ }
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× `await` is only allowed within async functions and at the top levels of modules
╭─[misc/fail/await-using-context.js:8:3]
7 │ function* g() {
8 │ await using x = null;
· ─────
9 │ for (await using y = null; ; ) {}
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× `await` is only allowed within async functions and at the top levels of modules
╭─[misc/fail/await-using-context.js:9:8]
8 │ await using x = null;
9 │ for (await using y = null; ; ) {}
· ─────
10 │ for (await using z of []) {}
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× `await` is only allowed within async functions and at the top levels of modules
╭─[misc/fail/await-using-context.js:10:8]
9 │ for (await using y = null; ; ) {}
10 │ for (await using z of []) {}
· ─────
11 │ }
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Expected `;` but found `,`
╭─[misc/fail/class-field-comma-expression.js:1:16]
1 │ class C { x = a,b }
@@ -27711,6 +27711,14 @@ Negative Passed: 4651/4651 (100.00%)
╰────
help: Wrap this declaration in a block statement
× `await` is only allowed within async functions and at the top levels of modules
╭─[test262/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-script.js:23:1]
22 │ $DONOTEVALUATE();
23 │ await using x = null;
· ─────
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× 'using' declarations are not allowed at the top level of a script
╭─[test262/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-script.js:23:1]
22 │ $DONOTEVALUATE();
+43 -15
View File
@@ -28729,13 +28729,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: Remove the `export` here and add `export { x }` as a separate statement to export the declaration
× 'using' declarations are not allowed at the top level of a script
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.13.ts:1:1]
1await using x = null;
· ─────────────────────
2 │
× `await` is only allowed within async functions and at the top levels of modules
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.13.ts:4:5]
3function f() {
4 │ await using x = null;
· ─────
5 │ }
╰────
help: Wrap this code in a block or use a module
help: Either remove this `await` or add the `async` keyword to the enclosing function
× Cannot use 'await using' in class static initialization block
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.14.ts:3:9]
@@ -28753,6 +28754,15 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
6 │ }
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:2:5]
1 │ declare namespace N {
2 │ await using x: { [Symbol.asyncDispose](): Promise<void> };
· ─────
3 │ await using y: null;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:2:17]
1 │ declare namespace N {
@@ -28761,6 +28771,15 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
3 │ await using y: null;
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:3:5]
2 │ await using x: { [Symbol.asyncDispose](): Promise<void> };
3 │ await using y: null;
· ─────
4 │ }
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:3:17]
2 │ await using x: { [Symbol.asyncDispose](): Promise<void> };
@@ -28769,6 +28788,15 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
4 │ }
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:6:5]
5 │ declare module 'M' {
6 │ await using x: { [Symbol.asyncDispose](): Promise<void> };
· ─────
7 │ await using y: null;
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:6:17]
5 │ declare module 'M' {
@@ -28777,6 +28805,15 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
7 │ await using y: null;
╰────
× `await` is only allowed within async functions and at the top levels of modules
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:7:5]
6 │ await using x: { [Symbol.asyncDispose](): Promise<void> };
7 │ await using y: null;
· ─────
8 │ }
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(1546): 'await using' declarations are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.16.ts:7:17]
6 │ await using x: { [Symbol.asyncDispose](): Promise<void> };
@@ -28882,15 +28919,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
6 │
╰────
× `for await` loops are only allowed within async functions and at the top levels of modules
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarationsInForAwaitOf.3.ts:5:5]
4 │
5 │ for await (await using of x);
· ─────
6 │
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× The left-hand side of a for...in statement cannot be an await using declaration.
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarationsInForIn.ts:2:10]
1 │ async function main() {
+2 -2
View File
@@ -1,6 +1,6 @@
semantic_misc Summary:
AST Parsed : 97/97 (100.00%)
Positive Passed: 93/97 (95.88%)
AST Parsed : 98/98 (100.00%)
Positive Passed: 94/98 (95.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 : 97/97 (100.00%)
Positive Passed: 97/97 (100.00%)
AST Parsed : 98/98 (100.00%)
Positive Passed: 98/98 (100.00%)
+5 -2
View File
@@ -89,8 +89,11 @@ impl TestCase {
fn source_type(path: &Path, options: &BabelOptions) -> SourceType {
// Some babel test cases have a js extension, but contain typescript code.
// Therefore, if the typescript plugin exists, enable typescript.
let mut source_type =
SourceType::from_path(path).unwrap().with_script(true).with_jsx(options.is_jsx());
let mut source_type = SourceType::from_path(path).unwrap().with_jsx(options.is_jsx());
// Preserve explicit module extensions; other fixtures default to scripts.
if !source_type.is_module() {
source_type = source_type.with_script(true);
}
source_type = match options.source_type.as_deref() {
Some("unambiguous") => source_type.with_unambiguous(true),
Some("script") => source_type.with_script(true),