Skip to content

Remove dead code from bun_sema and bun_format: 6 trait impls nothing uses - #45430

Open
robobun wants to merge 1 commit into
mainfrom
robobun/bdeb2421/dead-code-sweep
Open

robobun wants to merge 1 commit into
mainfrom
robobun/bdeb2421/dead-code-sweep

Conversation

@robobun

@robobun robobun commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • 6 hand-written trait impls in bun_sema and bun_format have no caller and no bound that needs them.
  • rustc's dead_code lint and mordant's unused_pub do not check trait impls.

Fix

  • Delete them: 5 files, 46 lines. The Notes list each one.
  • A relink of the debug build with --gc-sections keeps no function of these impl blocks, and the workspace compiles without them.
  • Verified: cargo check --workspace --all-targets, rust:clippy, rust:mordant, rust:check-all (12 targets), bun bd, test/cli/format/format.test.ts, test/cli/check/check.test.ts.
  • Self-reviewed: 6 concerns raised, 4 addressed. Rejected: folding this into Remove dead code from bun_format, bun_sema, bun_install, and bun_core #45402 (another sweep run owns it), and removing the formatter's unused T: Spanned bounds (a decision for its owner).

Background

Downsides

  • None found for users. Checked: the kept symbols of the relinked build and cargo check for 12 targets.
  • An open PR that starts to use one of these impls fails to compile after this merges. Checked: the 12 open PRs that touch these crates use none.

Behaviour change: none

Notes

Removed impls, by file

  • src/format/js/ast_nodes.rs
    • impl<'a> AsAstNodes<'a> for Class<'a>
  • src/sema/hir.rs
    • impl From<Atom> for u32
  • src/sema/local.rs
    • impl MaybeLocal for ()
    • impl MaybeLocal for u8
  • src/sema/table.rs
    • impl Packed for u8
  • src/sema/types.rs
    • impl<T: Follow> Follow for Box<T>

Candidates that stayed

  • The finder listed 498 trait impl blocks with no kept function in the linux link, of 4,171 hand-written trait impls that have a function body.
  • 106 are in a hunk of a dead-code PR that was open when the run started. 219 were already in the tree at the base of Remove dead code: 49 trait impls nothing uses, across 12 crates #43664. That sweep covered them, so this run left them alone. 40 contain an #[inline(always)] function, which has no symbol of its own at opt-level 0, so the link says nothing about them. 34 are Debug (a debugging aid, kept as in Remove dead code: 49 trait impls nothing uses, across 12 crates #43664) and 15 are Drop. None of these are in this diff.
  • 82 are left. This diff removes 6.
  • 9 are now in Remove dead code from bun_format, bun_sema, bun_install, and bun_core #45402 and Remove dead code from bun_format and napi.cpp #45420, which two other sweep runs opened while this one was in review: eight Spanned impls of the formatter and Default for Session. I dropped them from this branch.
  • 23 do not compile away: a bound, a unit test or a caller names them. For some Spanned impls of the formatter that bound sits on a function that never calls .span(), see the follow-ups.
  • 4 are in Windows-only files.
  • 15 stay by choice:
    • Display for Difference (src/format/verify.rs): a public diagnostic type of --debug-check.
    • From<Vec<Doc>> for Doc (src/format/css/doc.rs): the docs! macro documents "anything that can be made a document" as its input.
    • PathCharCwd for u16 (src/node_path/lib.rs): the Windows path character.
    • Std ergonomics impls on shared containers, a design call as in Remove dead code: 49 trait impls nothing uses, across 12 crates #43664: Extend and FromIterator for IdMap and Clone for IdBitSet (60 files use them), From<Vec<T>>, Into<Box<[T]>> and PartialEq<Vec<T>> in both directions for the sema List (141 files), and From<BabyVec>, AsRef, Borrow and two IntoIterator impls for ArenaBox.
  • 25 are in bun_lint: Hash for CodePath, Segment, EsNode, Reference, Signature and TsSymbol, From for Target, TokenOrText, Value and DeclarationKinds, and Located, Locate and StaticallyNamed for some handles. They are the surface that rule ports use, and the trait docs describe Locate and Located as a table of handles. They stay for the owners of the lint crates to decide.

Method details

Self-review, the six concerns

Other scans in this run, clean or already claimed

Follow-up candidates, not in this diff

  • Five + Spanned bounds of the formatter sit on code that never calls .span(): speculate_will_break and JoinBuilder::entries_with_trailing_separator (src/format/js/builders.rs:90, :104), Format for FormatSpecifiers (print/import_declaration.rs:155), FormatNodeWithoutTrailingComments (utils/format_node_without_trailing_comments.rs:6) and write_part_before_keyword (print/try_statement.rs:13). Spanned impls that nothing calls exist to satisfy them. One example is WithSpan<T> in utils/assignment_like.rs, a wrapper that only carries a span for speculate_will_break. The bounds can also be read as a guard that the content is a node, so the owner of the formatter decides.
  • PerformanceResourceTiming is never constructed on any platform: PerformanceResourceTiming::create has no caller and performance.markResourceTiming is a stub. About 1,100 lines are behind it (ResourceTiming, NetworkLoadMetrics, ResourceLoadTiming, ServerTiming, PerformanceServerTiming::create). The two global constructors are tested API (test/js/web/web-globals.test.js, perf_hooks), so the removal is a rewrite of the wrappers and needs a maintainer decision.
  • toJSNewlyCreated for CloseEvent, CustomEvent, ErrorEvent, MessageChannel, TextEncoder and URLPattern, with the ENABLE(BINDING_INTEGRITY) vtable declarations above each, has no caller. Each one is next to the toJS hunk of Remove dead code from the WebCore bindings, the JSSink codegen, and two Rust helpers #39929.
  • WEBCORE_GENERATED_CONSTRUCTOR_GETTER in ZigGlobalObject.cpp defines a <Class>_getter for 48 classes. 12 are used.
  • 33 of the 46 "Remove dead code" PRs that were open when this run started no longer merge into main. They hold 54,877 deleted lines, and their hunks are why most candidates of this run are not in this diff.

no test proof · iteration 0 · the description declares no behaviour change, so there is no failing test to prove; the existing suite in CI is the check

@robobun

robobun commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 7:34 PM PT - Oct 15th, 2026

✅ @robobun, your commit 5d917dc57ba746ff19a17fa8378e6a0b498841b4 passed in Build #126265! 🎉


🧪   To try this PR locally:

bunx bun-pr 45430

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

bun-45430 --bun

@robobun

robobun commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: ready for review. CI is green.

How the deletions were verified:

  • Linker: a relink of the debug build with --gc-sections keeps no function of the 6 impl blocks.
  • Compiler: cargo check --workspace --all-targets, bun run rust:clippy, bun run rust:mordant and bun run rust:check-all (12 of 12 targets) pass without them.
  • Build and tests: bun bd builds. test/cli/format/format.test.ts passes (1015 pass, 0 fail). test/cli/check/check.test.ts has 592 pass and 1 fail. The failing test needs test/node_modules/@elysia/eden, which my machine does not have.
  • Open PRs: Remove dead code from bun_format, bun_sema, bun_install, and bun_core #45402 and Remove dead code from bun_format and napi.cpp #45420 remove other impls of the formatter. This diff shares no file with them, and both merged with this branch compile.

CI:

  • Buildkite build 126265 passed. Four tests failed once and passed on the retry: bun-install-non-ntfs-windows.test.ts (Windows 2019 x64), test-watch-mode-kill-signal-default.mjs (Debian 13 x64 ASAN), socket.test.ts and worker-terminate-lifetime.test.ts (macOS x64). This diff touches none of that code.
  • cargo clippy, cargo miri test, mordant, Format and the source lints pass.
  • claude-find-issues is red. That workflow has no passing run on any PR since 2026-09-15, so this diff is not the cause.
  • Main moved 23 commits past the base of this branch. On 2026-10-17 I merged main at d7562f2 into this branch locally. The sema, format and lint crates compile with cargo check --all-targets, and the merge has no conflict.

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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: Advanced
  • Run ID: b8992079-45f8-4f08-bc01-bb5f9de94e6a
📥 Commits

Reviewing files that changed from the base of the PR and between 116dd64 and 5d917dc.

📒 Files selected for processing: 5
  • src/format/js/ast_nodes.rs
  • src/sema/hir.rs
  • src/sema/local.rs
  • src/sema/table.rs
  • src/sema/types.rs
💤 Files with no reviewable changes: 5
  • src/format/js/ast_nodes.rs
  • src/sema/local.rs
  • src/sema/hir.rs
  • src/sema/table.rs
  • src/sema/types.rs

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

Walkthrough

The change removes an AST conversion implementation and several trait implementations used in semantic analysis. The MaybeLocal for bool implementation remains.

Changes

Implementation removals

Layer / File(s) Summary
Remove Class AST conversion
src/format/js/ast_nodes.rs
Removes the standalone AsAstNodes implementation for Class.
Remove semantic-analysis implementations
src/sema/hir.rs, src/sema/local.rs, src/sema/table.rs, src/sema/types.rs
Removes From<Atom> for u32, MaybeLocal for () and u8, Packed for u8, and Follow for Box<T>.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 5d917

No actionable issue is identified from the supplied change evidence; merge after normal checks.

Pre-merge checks: Passed 4
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 identifies the removal of six unused trait implementations from bun_sema and bun_format, which matches the main change.
Description check Passed The description explains the problem, lists the removed implementations, documents verification steps and test results, and states the behavior impact. It does not use the template headings exactly, b…
✨ Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 — a pure deletion of six trait impls, and each one checks out as unreferenced.

What was reviewed:

  • AsAstNodes for Class: the trait is pub(crate), has no generic bounds anywhere in src/format, and Class is not in any format_with_comments! invocation; every .as_ast_nodes() call site is on another handle.
  • From<Atom> for u32: the only Into<u32> bounds inside bun_sema are File::list and NodeKey for (FileId, I); IdList<Atom> is built from raw .0 fields in parser/ty.rs and read via From<u32>, and no ByKey/IdMap is keyed by Atom with that bound.
  • MaybeLocal for ()/u8 and Packed for u8: enumerated every ByKey key type (ID tuples, Atom, Node, relate::Key, IntersectionKey) and every ById/ByNode value type — none contains a bare u8 or (), and ByKey puts no Packed bound on its value, so relations: ByKey<_, u8> is unaffected.
  • Follow for Box<T>: every boxed field in a follow_struct!/follow_enum! type or manual Follow impl is an ArenaBox, which keeps its own impl.
Extended reasoning...

The change is a 46-line deletion across five Rust files in bun_sema and bun_format, removing six trait impls with no additions and no behavior change for any surviving call site. It touches no security-sensitive surface (no parsing of untrusted input, auth, or crypto). It was approved because each removed impl was independently traced by grep to zero users: the format trait is crate-private, the sema traits are only bounded inside bun_sema, and the concrete key/value types that could have needed the deleted impls were enumerated and found not to. No CODEOWNERS entry covers the changed paths and the timeline shows no outstanding objections.

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