mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
fix(parser): disambiguate await using in for initializers (#26527)
Allow classic `for` initializers such as `for (await using;;)` when `using` is an ordinary identifier. Reuse the statement parser’s `await using` lookahead so resource declarations require a following binding identifier, preserving the existing line-break restrictions.
This commit is contained in:
1 parent
36d5534689
commit
e98beef227
13 files changed
+45
-44
No files matched your search
@@ -1664,7 +1664,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
/// Returns `true` when await is definitely an await expression (not ambiguous).
|
||||
///
|
||||
/// Unambiguous cases (returns `true`):
|
||||
/// - Next token is identifier, keyword (except `of`), or literal on same line
|
||||
/// - Next token is identifier, keyword, or literal on same line, except the cases below
|
||||
///
|
||||
/// Ambiguous cases (returns `false`):
|
||||
/// - Line break after `await` (could be ASI)
|
||||
@@ -1672,6 +1672,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
/// - Next token is `(` `[` (could be call/member or grouping/array)
|
||||
/// - Next token is template literal
|
||||
/// - Next token is `of` (for-await-of ambiguity: `for (await of [])`)
|
||||
/// - Next tokens form an `await using` declaration
|
||||
/// - Next token is `/` (division or regex literal)
|
||||
/// - Next token cannot start an expression (`)`, `}`, `;`, etc.)
|
||||
fn is_unambiguous_await(&mut self) -> bool {
|
||||
@@ -1685,8 +1686,12 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let kind = token.kind();
|
||||
|
||||
// Special case: `await of` is ambiguous (for-await-of loop)
|
||||
// Special case: `await using` should be handled as a declaration, not `await (using)`
|
||||
if matches!(kind, Kind::Of | Kind::Using) {
|
||||
if kind == Kind::Of {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only reserve `await using` for a declaration when a binding identifier follows.
|
||||
if kind == Kind::Using && self.is_using_statement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -422,17 +422,8 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// [+Using, +Await] await [no LineTerminator here] using [no LineTerminator here]
|
||||
if self.at(Kind::Await)
|
||||
&& self.lookahead(|p| {
|
||||
p.bump_any();
|
||||
if !p.at(Kind::Using) || p.cur_token().is_on_new_line() {
|
||||
return false;
|
||||
}
|
||||
p.bump_any();
|
||||
!p.cur_token().is_on_new_line()
|
||||
})
|
||||
{
|
||||
// [+Using, +Await] await [no LineTerminator here] using [no LineTerminator here] ForBinding[?Yield, ?Await, ~Pattern]
|
||||
if self.at(Kind::Await) && self.is_using_statement() {
|
||||
return self.parse_using_declaration_for_statement(
|
||||
for_start,
|
||||
parenthesis_opening_span,
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
for (await using;;);
|
||||
export {};
|
||||
@@ -0,0 +1 @@
|
||||
for (await using;;);
|
||||
@@ -0,0 +1,11 @@
|
||||
async function f(using) {
|
||||
for (await using;;) { break; }
|
||||
for (await using + 1;;) { break; }
|
||||
for (await using.foo;;) { break; }
|
||||
for (await using(1);;) { break; }
|
||||
for (await (using);;) { break; }
|
||||
for (await using
|
||||
;;) { break; }
|
||||
for (await
|
||||
using;;) { break; }
|
||||
}
|
||||
File renamed without changes.
@@ -1,3 +1,3 @@
|
||||
codegen_misc Summary:
|
||||
AST Parsed : 99/99 (100.00%)
|
||||
Positive Passed: 99/99 (100.00%)
|
||||
AST Parsed : 103/103 (100.00%)
|
||||
Positive Passed: 103/103 (100.00%)
|
||||
@@ -1,3 +1,3 @@
|
||||
formatter_misc Summary:
|
||||
AST Parsed : 99/99 (100.00%)
|
||||
Positive Passed: 99/99 (100.00%)
|
||||
AST Parsed : 103/103 (100.00%)
|
||||
Positive Passed: 103/103 (100.00%)
|
||||
@@ -1,3 +1,3 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 302/302 (100.00%)
|
||||
Positive Passed: 302/302 (100.00%)
|
||||
AST Parsed : 305/305 (100.00%)
|
||||
Positive Passed: 305/305 (100.00%)
|
||||
@@ -1,7 +1,7 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 99/99 (100.00%)
|
||||
Positive Passed: 99/99 (100.00%)
|
||||
Negative Passed: 203/203 (100.00%)
|
||||
AST Parsed : 103/103 (100.00%)
|
||||
Positive Passed: 103/103 (100.00%)
|
||||
Negative Passed: 202/202 (100.00%)
|
||||
|
||||
× Cannot assign to 'arguments' in strict mode
|
||||
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
|
||||
@@ -1071,15 +1071,6 @@ Negative Passed: 203/203 (100.00%)
|
||||
· ╰── `b` has already been declared here
|
||||
╰────
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/oxc-10503.ts:4:6]
|
||||
3 │
|
||||
4 │ await using
|
||||
· ▲
|
||||
5 │ foo = bar();
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[misc/fail/oxc-10638.js:2:1]
|
||||
1 │ for(
|
||||
|
||||
@@ -28867,14 +28867,13 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
|
||||
╰────
|
||||
help: Wrap this declaration in a block statement
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.4.ts:2:10]
|
||||
× Cannot assign to this expression
|
||||
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.4.ts:2:5]
|
||||
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.5.ts:3:17]
|
||||
@@ -28885,10 +28884,10 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
|
||||
╰────
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.6.ts:2:10]
|
||||
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarations.6.ts:2:16]
|
||||
1 │ {
|
||||
2 │ await using {a} = null;
|
||||
· ▲
|
||||
· ▲
|
||||
3 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
@@ -28928,11 +28927,12 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
|
||||
╰────
|
||||
help: Did you mean to use a for...of statement?
|
||||
|
||||
× Using declarations may not have binding patterns.
|
||||
× Expected `;` but found `{`
|
||||
╭─[typescript/tests/cases/conformance/statements/VariableStatements/usingDeclarations/awaitUsingDeclarationsInForOf.3.ts:2:22]
|
||||
1 │ async function main() {
|
||||
2 │ for (await using {} of []) {
|
||||
· ──
|
||||
· ┬
|
||||
· ╰── `;` expected
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
semantic_misc Summary:
|
||||
AST Parsed : 99/99 (100.00%)
|
||||
Positive Passed: 95/99 (95.96%)
|
||||
AST Parsed : 103/103 (100.00%)
|
||||
Positive Passed: 99/103 (96.12%)
|
||||
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 : 99/99 (100.00%)
|
||||
Positive Passed: 99/99 (100.00%)
|
||||
AST Parsed : 103/103 (100.00%)
|
||||
Positive Passed: 103/103 (100.00%)
|
||||
Reference in new issue
Block a user