Fix await using expression printing using as await#31324
Conversation
When `await using` is not followed by an identifier on the same line, it
is an `await` expression whose operand is the identifier `using`, not an
`await using` declaration. The fallback path stored the wrong token text
("await" instead of "using") as the identifier name, so
`await using instanceof o` printed as `await await instanceof o`.
|
Updated 6:27 AM PT - May 24th, 2026
❌ @robobun, your commit 7e62bd5 has some failures in 🧪 To try this PR locally: bunx bun-pr 31324That installs a local version of the PR into your bun-31324 --bun |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughParser fix corrects the identifier name reference in Changesawait using identifier parsing
Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
LGTM — the raw → raw2 fix is obviously correct and well-tested; the inline notes are all pre-existing/optional follow-ups (the Zig parity one at parse.zig:719 is the most actionable if you want to keep the port in sync).
Extended reasoning...
Overview
This PR is a one-character typo fix in src/js_parser/parse/mod.rs:922: store_name_in_ref(raw) → store_name_in_ref(raw2). In the await using fallback path (where using is an identifier, not a declaration keyword), the code was storing the raw text of the await token instead of the using token, causing the printer to emit await await instead of await using. The fix is unambiguously correct — raw is captured before consuming await, raw2 is captured after, and the identifier being built is for the using token. A new test in transpiler.test.js covers six variants (instanceof, bare, ASI, member access, for-init, top-level) and the PR description confirms it fails without the fix and passes with it.
Security risks
None. This is a parser correctness fix for a contrived-but-valid JS construct (await using where using is an ordinary identifier). No auth, crypto, permissions, or untrusted-input handling is affected beyond what the parser already does.
Level of scrutiny
The JS parser is a critical code path, but the change itself is trivial: a single variable-name correction whose intent is obvious from the surrounding three lines (let raw2 = p.lexer.raw() … raw2 == b"using" … store_name_in_ref(raw2)). The blast radius is limited to the rare await <identifier 'using'> <not-an-identifier> fallback, and the new tests pin the behavior. This is well within the bar for shadow approval.
Other factors
All three inline findings (mine from the prior run plus the two new ones) are explicitly flagged 🟣 pre-existing and "fine to defer":
await\nusing x = ymissing[no LT]guard at line 885 — matches esbuild/TS, accept-more-than-spec only.await using.foo ** xnot rejected —TAsteriskAsteriskcheck ordering, accept-more-than-spec only.- The same
raw→raw2bug still present in the Zig reference atparse.zig:719— a parity/re-sync hazard, worth a one-line follow-up but not a blocker for this PR.
None of these are introduced by this PR and none change the correctness of the one-line fix. The aarch64-musl build-rust CI failure was on the pre-autofix commit (fe6c1c9); the autofix commit (2df3d79) only reformatted the test file, so the failure is unrelated to the correctness of this change.
There was a problem hiding this comment.
LGTM — one-character fix (raw → raw2) so the await using fallback stores the using identifier instead of await; all prior inline notes were pre-existing nits and are resolved.
Extended reasoning...
Overview
One-line change in src/js_parser/parse/mod.rs:922 swapping store_name_in_ref(raw) → store_name_in_ref(raw2) in the await using fallback path of parse_expr_or_let_stmt, plus a new test block in test/bundler/transpiler/transpiler.test.js covering await using instanceof o, bare await using, ASI-via-newline, member access, for-loop init, and top-level await. raw is the lexeme of the consumed await token and raw2 is the lexeme of the consumed using token, so the fix is self-evidently correct from local context.
Security risks
None. Pure parser/AST-construction change for an extreme edge case (using used as an ordinary identifier inside an await expression). No I/O, auth, crypto, or permissions touched.
Level of scrutiny
Low. The fix is mechanical and verifiable by inspection; the new regression tests fail without the change and pass with it. The single CI failure (fetch-preconnect.test.ts on macOS aarch64) is unrelated to the parser. No CODEOWNERS entry covers these paths.
Other factors
I previously left three inline comments on this PR — all explicitly flagged as pre-existing issues (line-885 [no LT] guard, **-before-parse_suffix ordering, and the uncompiled Zig reference still carrying the same typo). The author resolved all three with reasonable rationale (esbuild/TS parity, accept-more-than-spec only, .zig files are non-compiled porting references per src/CLAUDE.md and recent precedent in #31230/#31242/#31266). None of those notes concern the change in this PR, and the bug hunting system found nothing new this run.
|
CI status for maintainers: the change itself is green everywhere it ran — 72 of 74 checks passing, including all Linux (x64/aarch64/musl/baseline/ASAN) and Windows test lanes, The only red entry left on build #57555 is Nothing here is related to the diff (a one-token parser fix plus tests, nothing platform-specific). Retrying that single macOS job — or merging as-is — is all that's left; I've already used one retrigger and won't push more. |
* oven/main (20 new commits): webcore: free Blob's owned content type on drop (oven-sh#31358) Support cross-compiling macOS binaries from Linux (oven-sh#31303) test: forward keep-alive requests in proxy.test.ts's mock proxy (oven-sh#31352) Port Bun.stringWidth to C++ with explicit SIMD (oven-sh#31351) Fix quadratic hang reporting duplicate-binding parse errors in the transpiler (oven-sh#31341) shell: don't abort when a glob's directory prefix doesn't exist (oven-sh#31367) Error instead of crashing on deeply nested statements in the transpiler (oven-sh#31333) Fix JSX transform panic when a bare `key` prop precedes `key` with a value (oven-sh#31350) Cap ANSI markdown indentation so deeply nested lists render in linear time (oven-sh#31366) css: bound selector-list expansion when compiling nesting for older targets (oven-sh#31277) node:http2: reassemble HEADERS+CONTINUATION before HPACK decoding (oven-sh#31323) Fix `await using` expression printing `using` as `await` (oven-sh#31324) Parenthesize `async` when it starts a for-of loop initializer (oven-sh#31326) Print Infinity and negative numeric property keys as computed properties (oven-sh#31328) css: keep required grouping parens in @container conditions when minifying (oven-sh#31330) Fix panic on anonymous export default class with an auto-accessor field (oven-sh#31331) node:http2: send GOAWAY frames on stream 0 (oven-sh#31353) parser: fix Scope mismatch while visiting panic from decorators on dropped class members (oven-sh#31340) webcrypto: reject oversized BufferSource inputs instead of aborting (oven-sh#31356) Error instead of crashing on deeply nested TypeScript types in the transpiler (oven-sh#31361) Resolved conflicts: - scripts/build.ts: kept both OHOS and macOS-cross argv entries - scripts/build/config.ts: kept both OHOS and macOS-cross config fields - scripts/build/deps/webkit.ts: kept OHOS fno-pic exclusion, adopted upstream -flto=thin
Problem
Found by the transpiler fuzzer (printed output does not reparse):
await usingonly starts anawait usingdeclaration when it is followed by an identifier on the same line. Otherwise it is anawaitexpression whose operand is the identifierusing— e.g.await using instanceof o,await using.foo(),await usingfollowed by a newline. In that fallback path the parser printed the operand asawait, producing invalid output likeawait await instanceof o.Cause
In
parse_expr_or_let_stmt(src/js_parser/parse/mod.rs), the fallback that builds theE::Identifierforusingcalledstore_name_in_ref(raw), whererawis the raw text of the first token ("await"). The identifier therefore got the nameawait. The correct name israw2, the raw text of theusingtoken.Fix
Pass
raw2("using") tostore_name_in_ref, matching what the normal identifier-expression path would have stored.Verification
bun bd test test/bundler/transpiler/transpiler.test.js— 147 pass, 0 fail.await of the identifier 'using' is not an await using declarationcoversinstanceof, bareawait using, ASI via newline, member access, for-loop init, and top-level await; it fails without the src change and passes with it.await using x = y,using x = y, andfor (await using a of b)declarations still parse and lower through__using/__callDisposeas before.