From 104abe66730b9e7dcbc8d65b28bcb4bb2b453424 Mon Sep 17 00:00:00 2001 From: Amol Bhave Date: Sat, 20 Jun 2026 23:05:54 -0500 Subject: [PATCH] fix(parser): preserve fatal error across top-level await reparse --- crates/oxc_parser/src/lib.rs | 8 ++++++++ tasks/coverage/misc/fail/oxc-23673.js | 1 + tasks/coverage/snapshots/parser_misc.snap | 7 ++++++- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tasks/coverage/misc/fail/oxc-23673.js diff --git a/crates/oxc_parser/src/lib.rs b/crates/oxc_parser/src/lib.rs index a1fd9560ade00..4d2de155bd83d 100644 --- a/crates/oxc_parser/src/lib.rs +++ b/crates/oxc_parser/src/lib.rs @@ -814,6 +814,11 @@ impl<'a, C: ParserConfig> ParserImpl<'a, C> { let original_tokens = if self.lexer.config.tokens() { Some(self.lexer.take_tokens()) } else { None }; + // `rewind` resets `self.fatal_error` to each checkpoint's value, all taken before any later + // statement could fail. Reparsing only re-derives already-valid `await` statements, so a + // fatal error from a later statement must survive it. Hold it aside, restore below. + let fatal_error = self.fatal_error.take(); + let checkpoints = std::mem::take(&mut self.state.potential_await_reparse); for (stmt_index, checkpoint) in checkpoints { // Rewind to the checkpoint @@ -833,6 +838,9 @@ impl<'a, C: ParserConfig> ParserImpl<'a, C> { if let Some(original_tokens) = original_tokens { self.lexer.set_tokens(original_tokens); } + + // A fatal error from reparsing comes earlier in source order and wins; else restore. + self.fatal_error = self.fatal_error.take().or(fatal_error); } fn default_context(source_type: SourceType, options: ParseOptions) -> Context { diff --git a/tasks/coverage/misc/fail/oxc-23673.js b/tasks/coverage/misc/fail/oxc-23673.js new file mode 100644 index 0000000000000..0c46d9d524a5d --- /dev/null +++ b/tasks/coverage/misc/fail/oxc-23673.js @@ -0,0 +1 @@ +await(E);import diff --git a/tasks/coverage/snapshots/parser_misc.snap b/tasks/coverage/snapshots/parser_misc.snap index 6ad563edf9d09..568bcd21bc4b1 100644 --- a/tasks/coverage/snapshots/parser_misc.snap +++ b/tasks/coverage/snapshots/parser_misc.snap @@ -1,7 +1,7 @@ parser_misc Summary: AST Parsed : 69/69 (100.00%) Positive Passed: 69/69 (100.00%) -Negative Passed: 149/149 (100.00%) +Negative Passed: 150/150 (100.00%) × Cannot assign to 'arguments' in strict mode ╭─[misc/fail/arguments-eval.ts:1:10] @@ -3602,6 +3602,11 @@ Negative Passed: 149/149 (100.00%) · ───────── ╰──── + × Expected `from` but found `EOF` + ╭─[misc/fail/oxc-23673.js:2:1] + 1 │ await(E);import + ╰──── + × Expected 'with' in import type options ╭─[misc/fail/oxc-2394.ts:20:22] 19 │ export type LocalInterface =