Address code review: remove boolean field, use sentinel value instead

Changes based on code review feedback:

1. Removed `is_at_module_scope` boolean field from lexer
   - Instead, use `fn_or_arrow_start_loc` as a sentinel
   - When at module scope, set it to `await_keyword_loc`
   - Compare locations to detect module scope: `fn_or_arrow_start_loc.start == await_keyword_loc.start`
   - This avoids adding persistent state to the lexer

2. Fixed test for async function in CJS format
   - Changed from `main().catch(console.error)` to IIFE: `(async function main() { ... })().catch(console.error)`
   - This ensures the promise is created and executed immediately
   - Prevents the CJS process from exiting before async work completes

The sentinel approach is cleaner as it reuses existing fields rather than
adding new state. The lexer already tracks both `await_keyword_loc` and
`fn_or_arrow_start_loc`, so we can encode the module scope information by
setting them to the same value when appropriate.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2025-10-25 02:36:42 +00:00
parent b42cde31b1
commit c2a432c9f5
3 changed files with 17 additions and 9 deletions

View File

@@ -136,9 +136,14 @@ pub fn ParsePrefix(
.allow_ident => {
p.lexer.prev_token_was_await_keyword = true;
p.lexer.await_keyword_loc = name_range.loc;
p.lexer.fn_or_arrow_start_loc = p.fn_or_arrow_data_parse.needs_async_loc;
// Check if we're at module scope by comparing current scope to module scope
p.lexer.is_at_module_scope = p.current_scope == p.module_scope;
// Use fn_or_arrow_start_loc as a signal:
// - If at module scope, set it to await_keyword_loc (sentinel)
// - Otherwise, set it to needs_async_loc (may be empty for arrow functions)
if (p.current_scope == p.module_scope) {
p.lexer.fn_or_arrow_start_loc = name_range.loc;
} else {
p.lexer.fn_or_arrow_start_loc = p.fn_or_arrow_data_parse.needs_async_loc;
}
},
}
},

View File

@@ -138,7 +138,6 @@ fn NewLexer_(
prev_token_was_await_keyword: bool = false,
await_keyword_loc: logger.Loc = logger.Loc.Empty,
fn_or_arrow_start_loc: logger.Loc = logger.Loc.Empty,
is_at_module_scope: bool = true,
regex_flags_start: ?u16 = null,
allocator: std.mem.Allocator,
string_literal_raw_content: string = "",
@@ -1821,7 +1820,13 @@ fn NewLexer_(
pub fn expectedString(self: *LexerType, text: string) !void {
if (self.prev_token_was_await_keyword) {
if (self.is_at_module_scope) {
// Use fn_or_arrow_start_loc as a signal:
// - If it equals await_keyword_loc, we're at module scope (sentinel value)
// - If it's not empty and different, we're in a function with a location for "async"
// - If it's empty, we're in a function without a clear location (e.g., arrow function)
const is_at_module_scope = self.fn_or_arrow_start_loc.start == self.await_keyword_loc.start;
if (is_at_module_scope) {
// Top-level await - provide better error message
try self.addRangeErrorWithNotes(
self.range(),

View File

@@ -54,16 +54,14 @@ describe("bundler", () => {
itBundled("top_level_await_error/AwaitInAsyncFunctionShouldStillWork", {
files: {
"/entry.ts": /* ts */ `
async function main() {
(async function main() {
async function sum(a: number, b: number) {
return a + b;
}
const result = await sum(5, 5);
console.log(result);
}
main().catch(console.error);
})().catch(console.error);
`,
},
format: "cjs",