Skip to content

Remove dead code from js_parser, bun_install, bun_sourcemap, the shell lexer, and ten WebCore files - #44363

Open
robobun wants to merge 7 commits into
mainfrom
robobun/4a688412/dead-code-sweep
Open

robobun wants to merge 7 commits into
mainfrom
robobun/4a688412/dead-code-sweep

Conversation

@robobun

@robobun robobun commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Behaviour change: none

Problem

  • Five groups of code in 24 files change nothing, cannot run, or hold values that nothing reads. No lint reports them: each has a textual user or is a comment.
  • Example: the module.exports = {...} check in src/js_parser/fold.rs (110 lines). Each of its paths ends in p.deoptimize_common_js_named_exports(); return None;, and the check after it does the same for every input that enters it.

Fix

  • Delete the five groups: 438 lines removed, 11 added. The Notes give each group with its proof.
  • Each proof is local: a block whose exits equal the code after it, a match arm that its one constructor excludes, a field that is false in each store, and comment lines.
  • Verified: bun bd, cargo clippy, cargo check for 12 targets, and 1,314 tests of the touched areas.
  • Self-reviewed: 5 concerns raised, 5 addressed.

Background

  • The workspace denies dead_code. But rustc counts a store to a field as a use.
  • To deoptimize CommonJS named exports is to stop the rewrite of exports.foo = 1 into an ESM export for that file.
  • Fourteen more verified groups are left out, because an open PR changes the same lines. Two clean open PRs conflict with what is left (Notes).

Downsides

  • ParsedSourceMap::memory_cost() reports 40 bytes less for each map: mapping::List lost two fields that were always empty.
  • None found for behaviour. Build and run output for 25 shapes of module.exports and bun add output for five bin shapes match the base commit.
Notes

Removed, one group each

  1. module.exports = {...} check (143 lines in fold.rs, p.rs, visit/mod.rs, visit/visit_stmt.rs). The check sits in the property-access fold. Its condition needs assign_target == AssignTarget::Replace. Its three exits (if deopt, inside the for over the properties, and the block after the loop) are all p.deoptimize_common_js_named_exports(); return None;. The check that follows it runs the same two statements when assign_target != AssignTarget::None, which the first condition implies. What the block evaluates before it exits has no effect. With the block gone, P::stmt_expr_value has writers and no reader, so the field, its two stores and the restore_stmt_expr! macro go. P::commonjs_replacement_stmts has no writer, so the field and the one branch that reads it go. History: the Zig original (src/js_parser/fold.zig before d451445) was a for ... else whose else ran on each completion, so the rewrite after it never ran. The port dropped that rewrite and kept the test.
  2. bin::NamesIterator::next_in_dir (54 lines in src/install/bin.rs, tree_printer.rs). The one constructor of the iterator is in the match arm for the Map, File and NamedFile tags, so the Tag::Dir arm of next never runs. With it go the fields done, dir_iterator and destination_node_modules. The Zig printer built the iterator in the same arm only. bun add prints no binary list for a directories.bin package, before and after.
  3. ParseUrlResultHint::All.include_names (125 lines in src/sourcemap, SavedSourceMap.rs, JSSourceMap.rs, src/semver/lib.rs). The one constructor writes false. So the block that loaded the JSON names array into mapping::List never ran, List.names and List.names_buffer were always empty, and List::get_name always returned None. new SourceMap(payload) reads names from JSSourceMap.names, which stays. That block held the last caller of bun_semver::String::init_append_if_needed, so the function goes too, and bun_sourcemap no longer depends on bun_semver.
  4. Lexer::delimit_quote (8 lines in src/shell_parser/parse.rs). Each store is false or a copy between the lexer, its sublexer and the backtrack snapshot. Nothing reads it.
  5. Commented-out code in ten WebCore files (108 lines). // LOG(Network, ...), // InspectorInstrumentation::..., // queueTaskKeepingObjectAlive(...), and // #include lines for headers that Bun does not have. Each removed line is a comment (87) or a blank line (21). git blame dates them from 2022-03-27 to 2025-12-26. An earlier sweep verified this set and held it (Remove dead code: 17 log scopes nothing logs to and four fields nothing reads, in 10 crates #44050).

Open PRs

Verification

The base is main at 9d9fdbe.

  • At fada614, which has every Rust change: bun bd passes (debug build with ASAN). cargo clippy --workspace and cargo fmt --all are clean. cargo check --workspace --all-targets passes, and bun run rust:check-all passes for 12 of 12 targets.
  • Tests with the debug build at fada614: transpiler.test.js, runtime-transpiler.test.ts, esModule-annotation.test.js, bundler_cjs.test.ts, bundler_cjs2esm.test.ts, six sourcemap test files under test/js/node/module and test/js/bun/sourcemap, lex.test.ts, parse.test.ts, bunshell.test.ts, brace.test.ts and bun-pm.test.ts. 1,058 pass, 0 fail.
  • The two later commits remove 14 comment and blank lines from EventTarget.cpp and Performance.cpp. At the final commit bun bd passes, and 256 tests pass in 11 files: event-target.test.ts, web-globals.test.js, URLSearchParams.test.ts, performance.test.js, performance-entries.test.ts, perf_hooks.test.ts, text-encoder.test.js, structured-clone-fastpath.test.ts, websocket-blob.test.ts, websocket-subprotocol-strict.test.ts and websocket-close-code.test.ts.
  • Probes against a debug build of the base commit, run for this same change on an earlier base (d115f54), same bytes: bun build in four modes and a run through require and import for 25 shapes of module.exports (object literal, methods, spread, computed key, after exports.x, nested in a function, +=, delete, in a for init, and more), and bun add of five tarballs (bin string, named, map, directories.bin, none).

Self-review

Five concerns, all addressed.

  • A first cut left P::stmt_expr_value in place as a written and unread field, to stay clear of js_parser: simplify an unused expression statement only when minifying, bundling or tree-shaking #36720. REVIEW.md asks for the deletion in the PR that makes the field dead, so it is deleted here and the conflict is named.
  • The same holds for bun_semver::String::init_append_if_needed.
  • The Problem text said "cannot run" for the fold.rs block, which runs and changes nothing: reworded.
  • The ten WebCore files were a part of the held set, with no stated reason for the cut: group 5 now says where the rest is.
  • Commented-out lines of the same kind stayed in the ten files, with no stated reason: eight more are removed, and group 5 names the PR that touches each of the 18 that stay.

Verified and left out, because an open PR holds the same lines

Each of these was proved, applied, built and tested, then removed from the branch.

Found and not dead, or not a plain removal

  • P::keep_expr_symbol_name returns its argument, so its ten call sites do nothing. Four of them evaluate an argument that panics on real input: new Bun.Transpiler({exports:{eliminate:["foo"]}}).transformSync("export let foo;") panics with called Option::unwrap() on a None value, and a destructuring default after a very deep member chain panics with index out of bounds. The removal changes that behaviour, so it needs its own PR with tests.
  • The counting pass of ScopeIterator in load_npmrc always counts 0, but ensure_unused_capacity(0) can still grow the hash index of a map that has more than 8 entries.

Leads from the same read, not proved a second time

  • JSValkeyClient::send cannot return Err: send_err_to_js and 31 "Failed to send" strings.
  • Exited.signal is 0 in each constructor: three "exited with a signal" branches in bun run and bunx.
  • ParserOptions.suppress_warnings_about_weird_code is true in each constructor.
  • mapping::parse: estimated_mapping_count is None at all four call sites.
  • ImportRecordFlags::WAS_ORIGINALLY_REQUIRE is never set.
  • VirtualMachine.arena has four writers and no reader.
  • GlobWalker::init and StatWatcher::init cannot fail. ResultValue::Empty has no constructor.

[policy-decision:dep] gate passed · iteration 0 · 24 files touched

passes on PR (with fix)
Dependency/toolchain change; no test proof to run.
diff hotspot
Cargo.lock                                         |   1 -
 src/install/bin.rs                                 |  50 +--------
 src/install/lockfile/printer/tree_printer.rs       |   5 -
 src/js_parser/fold.rs                              | 113 +--------------------
 src/js_parser/p.rs                                 |   6 +-
 src/js_parser/visit/mod.rs                         |   1 -
 src/js_parser/visit/visit_stmt.rs                  |  25 -----
 src/jsc/SavedSourceMap.rs                          |   1 -
 src/jsc/bindings/webcore/Event.cpp                 |   8 --
 src/jsc/bindings/webcore/EventTarget.cpp           |  27 -----
 src/jsc/bindings/webcore/JSCallbackData.cpp        |   6 --
 src/jsc/bindings/webcore/JSPerformance.cpp         |   5 -
 src/jsc/bindings/webcore/JSTextEncoder.cpp         |   6 --
 src/jsc/bindings/webcore/JSURLSearchParams.cpp     |   4 -
 src/jsc/bindings/webcore/Performance.cpp           |   6 --
 src/jsc/bindings/webcore/PerformanceMark.cpp       |   3 -
 src/jsc/bindings/webcore/SerializedScriptValue.cpp |   4 -
 src/jsc/bindings/webcore/WebSocket.cpp             |  39 -------
 src/semver/lib.rs                                  |  44 --------
 src/shell_parser/parse.rs                          |   8 --
 src/sourcemap/Cargo.toml                           |   1 -
 src/sourcemap/Mapping.rs                           |  27 +----
 src/sourcemap/lib.rs                               |  47 +--------
 src/sourcemap_jsc/JSSourceMap.rs                   |  12 +--
 24 files changed, 11 insertions(+), 438 deletions(-)

gate history · 1 passed · 0 rejected · iteration 0

evidence per changed file
file                                            reads  edits  tests
Cargo.lock                                          0      0      0
src/install/bin.rs                                  0      0      1
src/install/lockfile/printer/tree_printer.rs        0      0      0
src/js_parser/fold.rs                               0      0      2
src/js_parser/p.rs                                  0      0      0
src/js_parser/visit/mod.rs                          0      0      0
src/js_parser/visit/visit_stmt.rs                   0      0      0
src/jsc/SavedSourceMap.rs                           0      0      0
src/jsc/bindings/webcore/Event.cpp                  0      0      0
src/jsc/bindings/webcore/EventTarget.cpp            0      0      0
src/jsc/bindings/webcore/JSCallbackData.cpp         0      0      0
src/jsc/bindings/webcore/JSPerformance.cpp          0      0      0
src/jsc/bindings/webcore/JSTextEncoder.cpp          0      0      0
src/jsc/bindings/webcore/JSURLSearchParams.cpp      0      0      0
src/jsc/bindings/webcore/Performance.cpp            0      0      0
src/jsc/bindings/webcore/PerformanceMark.cpp        0      0      0
(+ 8 more files)

@robobun

robobun commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 9:21 PM PT - Oct 1st, 2026

✅ @robobun, your commit 4cd6788d8a4b50c770aaa95afacf5481c14ba73d passed in Build #122664! 🎉


🧪   To try this PR locally:

bunx bun-pr 44363

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

bun-44363 --bun

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: oven-sh/bun/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 9e8722a8-751c-4898-a0d9-2281790990cf

📥 Commits

Reviewing files that changed from the base of the PR and between fada614 and 49fdb24.

📒 Files selected for processing (3)
  • src/jsc/bindings/webcore/EventTarget.cpp
  • src/jsc/bindings/webcore/Performance.cpp
  • src/jsc/bindings/webcore/WebSocket.cpp
💤 Files with no reviewable changes (3)
  • src/jsc/bindings/webcore/Performance.cpp
  • src/jsc/bindings/webcore/EventTarget.cpp
  • src/jsc/bindings/webcore/WebSocket.cpp

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.


Walkthrough

The pull request changes binary-name iteration, CommonJS parser handling, sourcemap name parsing and lookup, shell lexer state, and the semver string API. It also removes commented-out code from WebCore bindings.

Changes

Install Bin Iteration

Layer / File(s) Summary
Binary-name iteration
src/install/bin.rs, src/install/lockfile/printer/tree_printer.rs
NamesIterator uses its index for exhaustion and no longer enumerates directory entries. The lockfile printer initializer no longer sets the removed iterator fields.

JavaScript Parser State

Layer / File(s) Summary
CommonJS and expression handling
src/js_parser/fold.rs, src/js_parser/p.rs, src/js_parser/visit/*
The special-case analysis of module.exports = { ... } was removed. Parser and visitor code also remove saved expression data and CommonJS replacement-statement state.

Sourcemap Name Handling

Layer / File(s) Summary
Name parsing and lookup
src/jsc/SavedSourceMap.rs, src/sourcemap/Cargo.toml, src/sourcemap/Mapping.rs, src/sourcemap/lib.rs, src/sourcemap_jsc/JSSourceMap.rs
Sourcemap parsing no longer loads the JSON names array. Mapping lists no longer store parsed names, and JavaScript name lookup checks self.names.

Shell Lexer State

Layer / File(s) Summary
Quote-delimiter state
src/shell_parser/parse.rs
The lexer no longer stores or restores delimit_quote during sublexer handling, snapshots, backtracking, or word breaking.

Semver String API

Layer / File(s) Summary
String method removal
src/semver/lib.rs
The public String::init_append_if_needed method was removed.

WebCore Comment Cleanup

Layer / File(s) Summary
Removed commented-out WebCore code
src/jsc/bindings/webcore/Event.cpp, src/jsc/bindings/webcore/EventTarget.cpp, src/jsc/bindings/webcore/JSCallbackData.cpp, src/jsc/bindings/webcore/JSPerformance.cpp, src/jsc/bindings/webcore/JSTextEncoder.cpp, src/jsc/bindings/webcore/JSURLSearchParams.cpp, src/jsc/bindings/webcore/Performance.cpp, src/jsc/bindings/webcore/PerformanceMark.cpp, src/jsc/bindings/webcore/SerializedScriptValue.cpp, src/jsc/bindings/webcore/WebSocket.cpp
Commented-out includes, assertions, instrumentation, logs, and code paths were removed. Active code remains unchanged.

Possibly related PRs

  • oven-sh/bun#40610: Removes commented-out WebCore event-listener and inspector code from EventTarget.cpp.
  • oven-sh/bun#40557: Also removes commented-out WebCore code from EventTarget.cpp, Performance.cpp, and WebSocket.cpp.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 49fdb

The selected WebCore cleanup does not change runtime behavior. No actionable merge-blocking risk remains; merge after normal checks pass.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly summarizes the primary change: removing dead code across the listed subsystems and WebCore files.
Description check ✅ Passed The description explains the problem, fix, scope, behavior considerations, verification steps, test results, and known merge conflicts. It does not use the exact template headings, but it provides the…
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

@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 — every deletion checks out as dead, and no behaviour changes.

What was reviewed:

  • fold.rs: the removed module.exports = {...} block required AssignTarget::Replace and only ever reached deoptimize_common_js_named_exports(); return None; the surviving assign_target() != None check at src/js_parser/fold.rs:324-330 covers the same inputs, and the block's pre-exit evaluation was side-effect-free.
  • Grepped src/ for every removed symbol (stmt_expr_value, commonjs_replacement_stmts, restore_stmt_expr, next_in_dir, destination_node_modules, include_names, names_buffer, List::get_name, delimit_quote, bun_semver::String::init_append_if_needed) — no remaining references; the remaining init_append_if_needed hits are all StringOrTinyString.
  • Confirmed the only NamesIterator constructor (tree_printer.rs:769-779) is inside the Map | File | NamedFile arm, and JSSourceMap fills self.names from the payload and calls mapping::parse directly, so mappings.names was never populated and get_name always returned None.
  • WebCore C++ diff: every removed line is a // comment or blank; no code lines touched.
Extended reasoning...

The change is a 24-file, +11/-424 dead-code removal across js_parser (CommonJS module.exports fold block and two write-only P fields), bun_install (unreachable Tag::Dir iterator path and its fields), bun_sourcemap (never-enabled include_names/names-array path plus the bun_semver dependency and Cargo.lock edge), the shell lexer's write-only delimit_quote flag, and comment-only cleanup in ten WebCore bindings. It touches no security-sensitive surface: no input validation, auth, crypto, or allocation ownership is altered, and the only measurable effect is memory_cost dropping the always-empty names buffer contribution. Approval rests on independently verifying each deadness proof by grep and by reading the surviving control flow, the absence of any lingering constructor or match site for the removed enum field, and the C++ diff consisting solely of comment and blank lines. No CODEOWNERS entry covers the changed files, no third-party review or objection is on the timeline, and the hunt ran dry.

@robobun

robobun commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: ready for review.

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

Code review found no issues

No high-confidence issues detected in this change.

An open pull request changes the line directly below it. The removal of the
comment made the two pull requests conflict.

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

Code review found no issues

No high-confidence issues detected in this change.

This branch has not been deployed

No deployments
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