Fix panic on anonymous export default class with an auto-accessor field - #31331
Conversation
Standard-decorator lowering requires the class to have a name ref, but s_export_default only injected the generated default name when the class had decorators. A class with only `accessor` fields (no decorators) still takes the standard-decorator lowering path, so the missing name hit an unwrap on None in lower_decorators. Inject the default name whenever the class will go through standard decorator lowering, matching what the expression path already does.
|
Warning Review limit reached
Your plan currently allows 1 review/hour. Refill in 50 minutes and 25 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
Comment |
|
Updated 7:04 AM PT - May 24th, 2026
✅ @robobun, your commit cc7efb5a39553027293711c8cb27d7ad6c2d9918 passed in 🧪 To try this PR locally: bunx bun-pr 31331That installs a local version of the PR into your bun-31331 --bun |
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
|
Partial overlap, worth spelling out: #29201's primary change is making This PR is only the minimal fix for the panic that currently reproduces on main ( So: if #29201 lands first, this PR is redundant and can be closed; if this lands first, #29201 only needs a trivial rebase of its |
There was a problem hiding this comment.
LGTM — minimal, well-reasoned fix that aligns the statement path with the expression path's existing should_lower_standard_decorators check, with regression tests covering the fuzz repro.
Extended reasoning...
Overview
This PR fixes a fuzzer-found panic when transpiling/bundling export default class { accessor x; } (anonymous class with an auto-accessor field and no decorators). The change is a one-line condition widening in src/js_parser/visit/visit_stmt.rs (s_export_default handling), plus three regression tests in test/bundler/transpiler/es-decorators.test.ts.
Verification of root cause
I traced the code path and confirmed the analysis:
parse/mod.rs:274-275setsshould_lower_standard_decorators = standard_decorators && (has_any_decorators || has_auto_accessor), so an accessor-only class hashas_decorators=falsebutshould_lower_standard_decorators=true.p.rs:6935routeslower_classintolower_standard_decorators_stmtbased onshould_lower_standard_decorators.lower_decorators.rs:1059-1062unconditionally unwrapsclass.class_namein the statement branch.- The old guard at
visit_stmt.rs:833only injected a name whenhas_decorators, leaving the accessor-only case nameless → panic.
The fix adds || should_lower_standard_decorators so the guard now covers exactly the set of classes that will hit the unwrap. This mirrors how the expression path (visit_stmt.rs:418) and other name-propagation sites already key off should_lower_standard_decorators.
Security risks
None. This is parser/transpiler AST-lowering logic with no auth, crypto, filesystem, or network surface. The change only affects whether a synthetic class name is assigned before lowering.
Level of scrutiny
Low-to-moderate. The Rust change is a single boolean disjunct in a guard, strictly widening when an already-existing fallback (inject <file>_default name) fires. It cannot regress the previously-working decorated case (that branch was already taken via has_decorators). The new tests follow the established patterns in the file (tempDir + spawn + stdout/stderr/exitCode assertions) and cover JS runtime, TS runtime, and the original Bun.build fuzz config.
Other factors
- No CODEOWNERS cover the modified files.
- No prior reviewer comments to address.
- The PR description reports the full decorator/export-default test suites pass and clippy is clean.
- Minor note: the parallel Zig reference at
visit_stmt.zig:456retains the oldhas_decorators-only check, but the active Rust parser (per the panic trace) is what's fixed here; not a blocker for this PR.
There was a problem hiding this comment.
LGTM — minimal, well-targeted fix that widens the name-injection condition to match the lowering dispatch, with solid regression tests.
Extended reasoning...
Overview
This PR fixes a fuzzer-found panic in the JS parser when transpiling export default class { accessor op; } (anonymous default-exported class with an auto-accessor and no decorators). The change is a 2-line condition widening in src/js_parser/visit/visit_stmt.rs:833 plus three regression tests in test/bundler/transpiler/es-decorators.test.ts.
Correctness
I verified the root cause: should_lower_standard_decorators is set whenever standard_decorators && (has_any_decorators || has_auto_accessor) (parse/mod.rs:274), and lower_class dispatches to lower_standard_decorators_stmt based on that flag (p.rs:6935), which then unconditionally unwraps class.class_name for statements (lower_decorators.rs:1062). The old s_export_default guard only injected a name when has_decorators was true, missing the auto-accessor-only case. The new condition has_decorators || should_lower_standard_decorators exactly matches the set of cases that reach the unwrap. The change is strictly additive — it only injects a name in more cases, and only when class_name is already None/unbound, so it cannot regress named or previously-working cases. The expression path (visit_stmt.rs:418, visit/mod.rs:303/645/684/988) already keys off should_lower_standard_decorators, so this brings the statement path into line with the established pattern.
Security risks
None. This is a parser/transpiler crash fix with no auth, crypto, permissions, or untrusted-input-handling implications beyond making the parser not panic on valid syntax.
Level of scrutiny
Low-to-moderate. It's a 2-line logic change in a hot parser path, but the change is mechanical (OR-ing in a flag that's already used identically elsewhere for the same purpose), the root cause is well-explained and verifiable, and the three new tests (JS runtime, TS runtime, Bun.build mirroring the fuzz config) follow the exact patterns of adjacent tests in the same describe block.
Other factors
- No bugs found by the bug-hunting system.
- The overlap with #29201 is already clearly explained in the thread; that's a sequencing decision for maintainers, not a correctness concern with this PR.
- CI showed a failure on 21f59ad and was retriggered via cc7efb5; the code change itself is sound regardless.
- The Zig mirror at
visit_stmt.zig:456still uses the narrowerhas_decoratorscheck — if the Zig path is still active, it may have the same latent bug, but that's out of scope for this Rust-side fix.
|
CI status for maintainers: the change is green on every lane that actually ran — twice. Both builds (57528 on 21f59ad, and the re-run 57570 on cc7efb5) have zero test failures. The only red checks are Retrying those two jobs on build 57570 once aarch64 agents are available should turn the PR fully green. The fix itself is a two-line condition change in |
* 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
Fixes a fuzzer-found panic:
called 'Option::unwrap()' on a 'None' valuewhen transpiling or bundling an anonymousexport default classthat contains anaccessor(auto-accessor) field and no decorators.Repro
The
minify/sourcemapflags from the fuzz report are incidental — any parse of the input hits it.Root cause
An
accessorfield marks the classshould_lower_standard_decorators, solower_classtakes the standard-decorator lowering path, which reads the class name ref unconditionally for class statements (lower_decorators.rs:1062).For
export default class { ... }the class statement is anonymous, ands_export_defaultonly injected the generated<file>_defaultname when the classhas_decorators. A class whose only reason for lowering is an auto-accessor hashas_decorators == false, so no name was injected and the lowering unwrappedNone.The expression path already keys this off
should_lower_standard_decorators; the statement path didn't.Fix
In
s_export_default, inject the default name whenever the class has decorators or will go through standard-decorator lowering. Output now matches the already-working decorated case:Verification
test/bundler/transpiler/es-decorators.test.ts(runtime JS, runtime TS, and aBun.buildcase mirroring the fuzz config) fail with the panic before the fix and pass after.es-decorators.test.ts,es-decorators-esbuild.test.ts,decorators.test.ts,decorator-metadata.test.ts,export-default.test.js,bundler_decorator_metadata.test.ts, andtranspiler.test.jspass with the fix.cargo clippy -p bun_js_parserclean.