mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(parser): switch to module goal eagerly on export (#22684)
## Summary In unambiguous (`.js` auto-detect) sources, `export var x = await + 1;` reported a spurious `Duplicated export 'x'` error. The parser first parsed the statement with `await` as an identifier, then re-parsed it under `Context::Await` to patch the AST — but the second pass re-invoked `visit_export_named_declaration`, recording the binding `x` a second time in the module record. `export` is unambiguously module syntax per ECMA-262 §16.2.3 (`ExportDeclaration` is only a `ModuleItem`, never a Script production). So commit to the Module goal — enable `Context::Await` — as soon as the next top-level token is `Kind::Export`. The statement then parses correctly on the first attempt; no reparse and no duplicate record. `Kind::Import` is intentionally **not** eager-switched. TypeScript's `import name = ns.path` is a script-compatible namespace alias (see `topLevelAwait.2.ts`), so treating `import` as a module starter would break that case. The only TS snapshot churn is +16 lines of additional error messages for already-failing tests (`topLevelAwaitErrors.5.ts`, `topLevelAwaitErrors.6.ts` — `export ... await`). Summary counts unchanged. Closes #22520 Fixes #22158
This commit is contained in:
8 files changed
+40
-13
No files matched your search
@@ -50,9 +50,14 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
break;
|
||||
}
|
||||
|
||||
// Once ESM syntax is detected, enable await context for remaining statements
|
||||
// and stop tracking (we'll reparse earlier statements at the end)
|
||||
if track_await_reparse && self.module_record_builder.has_module_syntax() {
|
||||
// Eagerly commit to Module goal on `export` so the statement is parsed
|
||||
// under `Await` on the first pass; otherwise the reparse below runs it
|
||||
// again and the export binding gets recorded twice.
|
||||
// `import` is not eager: TypeScript's `import name = ns.foo` is a
|
||||
// script-compatible namespace alias, not module syntax.
|
||||
if track_await_reparse
|
||||
&& (self.module_record_builder.has_module_syntax() || self.at(Kind::Export))
|
||||
{
|
||||
track_await_reparse = false;
|
||||
self.ctx = self.ctx.and_await(true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// https://github.com/oxc-project/oxc/issues/22158
|
||||
// `await` in initializer of an `export var` declaration must not be
|
||||
// reported as a duplicated export when parsed with unambiguous `.js`.
|
||||
export var a = await + 1;
|
||||
export var b = await(1);
|
||||
export var c = await + 0;
|
||||
@@ -1,3 +1,3 @@
|
||||
codegen_misc Summary:
|
||||
AST Parsed : 66/66 (100.00%)
|
||||
Positive Passed: 66/66 (100.00%)
|
||||
AST Parsed : 67/67 (100.00%)
|
||||
Positive Passed: 67/67 (100.00%)
|
||||
@@ -1,3 +1,3 @@
|
||||
formatter_misc Summary:
|
||||
AST Parsed : 66/66 (100.00%)
|
||||
Positive Passed: 66/66 (100.00%)
|
||||
AST Parsed : 67/67 (100.00%)
|
||||
Positive Passed: 67/67 (100.00%)
|
||||
@@ -1,6 +1,6 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 66/66 (100.00%)
|
||||
Positive Passed: 66/66 (100.00%)
|
||||
AST Parsed : 67/67 (100.00%)
|
||||
Positive Passed: 67/67 (100.00%)
|
||||
Negative Passed: 137/137 (100.00%)
|
||||
|
||||
× Cannot assign to 'arguments' in strict mode
|
||||
|
||||
@@ -21019,6 +21019,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc
|
||||
· ─────
|
||||
╰────
|
||||
|
||||
× Cannot use `await` as an identifier in an async context
|
||||
╭─[typescript/tests/cases/conformance/externalModules/topLevelAwaitErrors.5.ts:2:14]
|
||||
1 │ // await in exported class name should fail
|
||||
2 │ export class await {
|
||||
· ─────
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
× The keyword 'await' is reserved
|
||||
╭─[typescript/tests/cases/conformance/externalModules/topLevelAwaitErrors.5.ts:2:14]
|
||||
1 │ // await in exported class name should fail
|
||||
@@ -21027,6 +21035,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
× Cannot use `await` as an identifier in an async context
|
||||
╭─[typescript/tests/cases/conformance/externalModules/topLevelAwaitErrors.6.ts:2:17]
|
||||
1 │ // await in exported function name should fail
|
||||
2 │ export function await() {
|
||||
· ─────
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
× The keyword 'await' is reserved
|
||||
╭─[typescript/tests/cases/conformance/externalModules/topLevelAwaitErrors.6.ts:2:17]
|
||||
1 │ // await in exported function name should fail
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
semantic_misc Summary:
|
||||
AST Parsed : 66/66 (100.00%)
|
||||
Positive Passed: 52/66 (78.79%)
|
||||
AST Parsed : 67/67 (100.00%)
|
||||
Positive Passed: 53/67 (79.10%)
|
||||
semantic Error: tasks/coverage/misc/pass/conditional-arrow-alternate-return-type.ts
|
||||
Unresolved references mismatch:
|
||||
after transform: ["Pick", "Record"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
transformer_misc Summary:
|
||||
AST Parsed : 66/66 (100.00%)
|
||||
Positive Passed: 66/66 (100.00%)
|
||||
AST Parsed : 67/67 (100.00%)
|
||||
Positive Passed: 67/67 (100.00%)
|
||||
Reference in new issue
Block a user