Skip to content

Fix await using expression printing using as await#31324

Merged
Jarred-Sumner merged 3 commits into
mainfrom
farm/b916b682/fix-await-using-expr
May 24, 2026
Merged

Fix await using expression printing using as await#31324
Jarred-Sumner merged 3 commits into
mainfrom
farm/b916b682/fix-await-using-expr

Conversation

@robobun

@robobun robobun commented May 24, 2026

Copy link
Copy Markdown
Collaborator

Problem

Found by the transpiler fuzzer (printed output does not reparse):

new Bun.Transpiler({ loader: "jsx", target: "bun", minifyWhitespace: true })
  .transformSync("async function f(){await using instanceof o}")
// => "async function f(){await await instanceof o}"   (does not reparse: "Unexpected instanceof")

await using only starts an await using declaration when it is followed by an identifier on the same line. Otherwise it is an await expression whose operand is the identifier using — e.g. await using instanceof o, await using.foo(), await using followed by a newline. In that fallback path the parser printed the operand as await, producing invalid output like await await instanceof o.

Cause

In parse_expr_or_let_stmt (src/js_parser/parse/mod.rs), the fallback that builds the E::Identifier for using called store_name_in_ref(raw), where raw is the raw text of the first token ("await"). The identifier therefore got the name await. The correct name is raw2, the raw text of the using token.

Fix

Pass raw2 ("using") to store_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.
  • New test await of the identifier 'using' is not an await using declaration covers instanceof, bare await 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, and for (await using a of b) declarations still parse and lower through __using/__callDispose as before.

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`.
@robobun

robobun commented May 24, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 6:27 AM PT - May 24th, 2026

@robobun, your commit 7e62bd5 has some failures in Build #57555 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 31324

That installs a local version of the PR into your bun-31324 executable, so you can run:

bun-31324 --bun

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c15aeaa3-69a3-43e2-8787-99d16ad21b63

📥 Commits

Reviewing files that changed from the base of the PR and between fe6c1c9 and 2df3d79.

📒 Files selected for processing (1)
  • test/bundler/transpiler/transpiler.test.js

Walkthrough

Parser fix corrects the identifier name reference in await using expressions when using functions as an identifier rather than a declaration keyword. The change updates identifier storage to use the correct token text, with test coverage confirming proper transpilation for various identifier usage patterns.

Changes

await using identifier parsing

Layer / File(s) Summary
Parser identifier fix and test validation
src/js_parser/parse/mod.rs, test/bundler/transpiler/transpiler.test.js
Parser now uses the correct token text (raw2) when creating an identifier reference for using in await using contexts. Test cases verify that using is recognized as an identifier in various positions including await using instanceof, plain await using, multiline variants, and property-access scenarios.

Possibly related PRs

  • oven-sh/bun#31230: Both PRs modify parse_expr_or_let_stmt's "await using" parsing in src/js_parser/parse/mod.rs—one changes the identifier text captured for the using part, while the other adds ambient-context rejection logic for TypeScript await using.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main bug fix: incorrect printing of 'using' as 'await' in await using expressions.
Description check ✅ Passed The description comprehensively covers the problem, cause, fix, and verification, but lacks explicit sections matching the repository template structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/js_parser/parse/mod.rs

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the rawraw2 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 = y missing [no LT] guard at line 885 — matches esbuild/TS, accept-more-than-spec only.
  • await using.foo ** x not rejected — TAsteriskAsterisk check ordering, accept-more-than-spec only.
  • The same rawraw2 bug still present in the Zig reference at parse.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.

Comment thread src/js_parser/parse/mod.rs
Comment thread src/js_parser/parse/mod.rs

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — one-character fix (rawraw2) 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.

@robobun

robobun commented May 24, 2026

Copy link
Copy Markdown
Collaborator Author

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, darwin-14-x64, darwin-26-aarch64, clippy/format, and the new regression test in test/bundler/transpiler/transpiler.test.js.

The only red entry left on build #57555 is darwin-14-aarch64-test-bun: the macOS agents picked the job up late (it sat as Expired for a while), and after ~1756/2039 test files — with no test failures — the job received a cancellation signal/SIGTERM from the build timeout and exited with status 3. The earlier run (#57515) failed the same way for infra reasons (runner lost its own bun-profile binary mid-run).

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.

@Jarred-Sumner Jarred-Sumner merged commit 9d5356a into main May 24, 2026
76 of 78 checks passed
@Jarred-Sumner Jarred-Sumner deleted the farm/b916b682/fix-await-using-expr branch May 24, 2026 20:50
springmin pushed a commit to springmin/bun that referenced this pull request May 25, 2026
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants