Skip to content

Remove dead code: 49 trait impls nothing uses, across 12 crates - #43664

Merged
Jarred-Sumner merged 1 commit into
mainfrom
robobun/2c1c7e31/dead-code-sweep
Sep 21, 2026
Merged

Jarred-Sumner merged 1 commit into
mainfrom
robobun/2c1c7e31/dead-code-sweep

Conversation

@robobun

@robobun robobun commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • 49 hand-written trait impls across 12 crates have no user: no call, and no bound that needs them.
  • rustc's dead_code lint exempts trait impls, so the workspace's deny lints never report them.

Fix

  • Delete the 49 impls and hash_array_list (src/css/generics.rs), whose only caller was a removed impl. 25 files, 436 lines removed.
  • Candidates come from the linker: in a relink of the debug build with --gc-sections, an impl block with no kept function address is a candidate. The compiler decides next: an impl that a bound, a supertrait, another target, or a unit test needs does not compile away, so it stays. That kept 128 of 233 candidates.
  • 56 more stayed by choice: open PRs use them, docs describe them, or they are std ergonomics impls on shared containers. See the Notes.
  • Verified: bun run rust:check-all (12 targets), cargo check --release (3 targets), cargo check --tests, cargo clippy, bun bd, 11 test files, and a scan of all open PRs for users.

Background

  • To rustc a trait impl is always reachable: generic code could call it. Only a whole-program view shows that nothing does.
  • The debug build uses opt-level 0 and one section per function, so --gc-sections drops every function with no caller. Callers in generic instantiations and macro-expanded code count.
  • That link proves linux only. cargo check covers the other targets.
  • Remove dead code: 92 trait impls nothing calls, across 29 crates #39795 removed 92 impls this way.
Notes

Removed impls, by file

  • src/ast/e.rs
    • impl From<f64> for Number
  • src/bun_alloc/baby_vec.rs
    • impl<'a, 'b, T: Copy> Extend<&'b T> for BabyVec<'a, T>
    • impl<'a, 'b, T> IntoIterator for &'b BabyVec<'a, T>
    • impl<'a, 'b, T> IntoIterator for &'b mut BabyVec<'a, T>
    • impl<'a, T> core::borrow::Borrow<[T]> for BabyVec<'a, T>
    • impl<'a, T> AsRef<[T]> for BabyVec<'a, T>
  • src/bun_alloc/lib.rs
    • impl<const N: usize> BSSAppendable for [&[u8]; N]
  • src/bun_core/atomic_cell.rs
    • unsafe impl<U> Atom for *const U
  • src/bun_core/fmt.rs
    • impl From<InvalidCharacter> for crate::CrateError
  • src/bun_core/output.rs
    • impl<T: fmt::Display> FmtTuple for &[T]
  • src/bundler/Chunk.rs
    • impl core::ops::Index<usize> for CompileResultSlots
  • src/bundler/bundle_v2.rs
    • impl Ord for StableRef
    • impl PartialOrd for StableRef
  • src/collections/array_hash_map.rs
    • impl<K, V, C, A: MapAllocator> ArrayHashMapExt for ArrayHashMap<K, V, C, A>
  • src/css/generics.rs
    • impl<'bump, T: DeepClone<'bump>> DeepClone<'bump> for &'bump T
    • impl<T: CssEql, const N: usize> CssEql for [T; N]
    • impl CssHash for ()
    • impl<T: CssHash, const N: usize> CssHash for [T; N]
    • impl<'bump, T: CssHash> CssHash for ArrayList<'bump, T>
    • impl CssHash for bun_ast::Loc
    • impl<T: IsCompatible, const N: usize> IsCompatible for [T; N]
    • impl<'bump, T: ToCss> ToCss for ArrayList<'bump, T>
    • impl ToCss for CustomIdent
    • impl ToCss for DashedIdent
    • impl ToCss for Ident
  • src/css/media_query.rs
    • impl crate::generic::ToCss for MediaList
  • src/css/properties/mod.rs
    • impl crate::generics::ParseWithOptions for css_values::length::Length
    • impl<S, const P: u8> crate::generics::Parse for GenericBorder<S, P>
  • src/css/rules/supports.rs
    • impl crate::generics::CssEql for SupportsCondition
    • impl css::generic::ToCss for SupportsCondition
  • src/css/selectors/parser.rs
    • impl<Impl: SelectorImpl> Default for GenericSelector<Impl>
    • impl<Impl: BunSelectorImpl> CssEql for GenericSelectorList<Impl>
    • impl<Impl: BunSelectorImpl> CssHash for GenericSelectorList<Impl>
    • impl<Impl: BunSelectorImpl> CssEql for GenericComponent<Impl>
    • impl<Impl: BunSelectorImpl> CssHash for GenericComponent<Impl>
  • src/css/values/alpha.rs
    • impl crate::generics::CssHash for AlphaValue
  • src/install/lockfile/lockfile_json_stringify_for_debugging.rs
    • impl<const N: usize> JsonScalar for &[u8; N]
  • src/install/resolution.rs
    • impl Default for Tag
  • src/install_types/resolver_hooks.rs
    • impl<I: VersionInt> Default for ResolutionValue<I>
    • impl Default for Resolution
  • src/jsc/JSPropertyIterator.rs
    • impl IntoIterObject for *const JSObject
  • src/jsc/JSValue.rs
    • impl FromAny for ()
    • impl FromAny for &str
    • impl<T: FromAny> FromAny for Option<T>
  • src/jsc/host_fn.rs
    • impl<T> IntoHostConstructReturn for *mut T
  • src/react_compiler/hir/mod.rs
    • impl From<FloatValue> for f64
  • src/runtime/bake/DevServer.rs
    • impl From<OpaqueFileId> for OpaqueFileIdOrOptional
  • src/runtime/error.rs
    • impl From<Error> for bun_jsc::CrateError
  • src/tcc_sys/tcc.rs
    • impl<ErrCtx> Default for Config<ErrCtx>

Candidates that stayed (184 of 233)

Method details

  • Link: the debug link command from build.ninja plus -Wl,--gc-sections. -rdynamic, --dynamic-list and the version script stay, so the exported NAPI/V8/uv surface is still a root.
  • Liveness: llvm-nm lists the 669,524 kept function symbols and llvm-symbolizer maps each address to file:line. An impl block is a candidate when no kept address falls inside it and a rust-analyzer SCIP index shows that linux compiles every method in it.
  • Limit of the finder: it reads impl blocks from source text. Impls that a macro_rules! type list generates are not candidates, so unused arms of such lists are not in this PR.
  • Restore loop: delete all candidates, run cargo check --workspace --keep-going --message-format=json, put back each impl that an error names, repeat until clean. Targets in the loop: linux-gnu, windows-msvc, darwin, freebsd, android, musl, then --tests.
  • Method resolution: on linux the link proves that no call to a removed impl remains, so no call can resolve differently. On other targets a call that used a removed impl fails to compile.
  • Open PRs: main has no merge queue, so a green PR that uses a removed impl would break main when it merges. I fetched the heads of all 5474 open non-draft PRs, took the Rust lines each one adds against its merge base, and searched them for the types and traits of the 49 impls. No PR uses one.
  • Base: main at 18fe86e. The liveness data is from cf71211. The commits between them do not touch the 25 files, and every check above ran again on 18fe86e.
  • cargo check --workspace --tests reports one error, src/bundler/options.rs:2328 (expected ContentHash, found integer). Clean main has the same error. This PR does not touch it.
  • Three tests fail in the local debug build with and without this change: ffi.test.js "FTL-compiled call site" (5 s timeout under ASAN), and two serve.test.ts tests that depend on the container (root can bind low ports, loopback check).

Other scans in this run

  • Free functions, inherent methods, types, consts and statics: a name scan, the linker scan, and a rust-analyzer zero-reference scan for linux, windows and darwin. Every hit was platform code with a caller on another target, test-only code, or a deletion that an open PR already carries.
  • Also clean or already claimed: .rs files outside every mod tree, unused Cargo dependencies, extern declarations without a Rust caller, commented-out code in C++/TS, unused src/js modules and internal exports, linker-dropped C++ functions in src/jsc/bindings.

Follow-up candidates, not in this diff

  • 25 hand-written Debug impls that nothing formats. They are a debugging aid, so this PR keeps them.
  • Clone for JsPoster has no caller, and it is the only reader of the JsPosterVTable::clone slot. Removing both is a small refactor in src/event_loop and src/jsc/VmHandle.rs.
  • Display for ErrorLocation (src/css/error.rs) has no caller, and it is the only reader of ErrorLocation::filename.
  • The 19 std ergonomics impls above, if a maintainer wants shared containers to go through inherent methods only.
  • Bun__napi_get_version in src/jsc/bindings/napi.cpp has no caller.
  • The class generator emits <Type>Class__call shims for classes that set call: true and noConstructor: true (the seven Expect* matchers in jest.classes.ts). Nothing references those shims.

rustc's dead_code lint exempts trait impls, so the workspace lints never
report an impl that no caller and no bound needs. A relink of the debug
build with --gc-sections shows which impl methods the linker drops from
every codegen unit. Each impl here has no live code in that link, and
the workspace still compiles without it on all 12 targets, in the
release profile, and with --tests.

Also removes hash_array_list in src/css/generics.rs. Its only caller
was the removed CssHash impl for ArrayList.
@robobun

robobun commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

Status: ready for review.

How to check a removal:

  1. Relink the debug build with -Wl,--gc-sections -Wl,--print-gc-sections. Every method of the 49 impls is in the list of dropped sections, or was never instantiated.
  2. Put one impl back out of the diff and remove it again. cargo check --workspace passes both ways on every target in bun run rust:check-all.

Self-reviewed: the review asked for a smaller shape. I put back 8 impls that open PRs use (#39497, #38692, #36605, #40708, #40372), 19 std ergonomics impls on shared containers that need a maintainer decision, and 21 impls that docs describe. The PR body lists each group.

This PR is perishable. Main has no merge queue, so a PR that starts to use one of these impls after 2026-09-21 06:00 UTC would break main when it merges after this one.

@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View 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: 6190d957-b40a-49a9-80f7-d689aa87b016

📥 Commits

Reviewing files that changed from the base of the PR and between 8033122 and 85c4883.

📒 Files selected for processing (25)
  • src/ast/e.rs
  • src/bun_alloc/baby_vec.rs
  • src/bun_alloc/lib.rs
  • src/bun_core/atomic_cell.rs
  • src/bun_core/fmt.rs
  • src/bun_core/output.rs
  • src/bundler/Chunk.rs
  • src/bundler/bundle_v2.rs
  • src/collections/array_hash_map.rs
  • src/css/generics.rs
  • src/css/media_query.rs
  • src/css/properties/mod.rs
  • src/css/rules/supports.rs
  • src/css/selectors/parser.rs
  • src/css/values/alpha.rs
  • src/install/lockfile/lockfile_json_stringify_for_debugging.rs
  • src/install/resolution.rs
  • src/install_types/resolver_hooks.rs
  • src/jsc/JSPropertyIterator.rs
  • src/jsc/JSValue.rs
  • src/jsc/host_fn.rs
  • src/react_compiler/hir/mod.rs
  • src/runtime/bake/DevServer.rs
  • src/runtime/error.rs
  • src/tcc_sys/tcc.rs
💤 Files with no reviewable changes (25)
  • src/runtime/error.rs
  • src/jsc/JSPropertyIterator.rs
  • src/bun_core/output.rs
  • src/ast/e.rs
  • src/css/values/alpha.rs
  • src/collections/array_hash_map.rs
  • src/bun_core/atomic_cell.rs
  • src/install/resolution.rs
  • src/css/rules/supports.rs
  • src/css/media_query.rs
  • src/bun_core/fmt.rs
  • src/bundler/Chunk.rs
  • src/bundler/bundle_v2.rs
  • src/react_compiler/hir/mod.rs
  • src/install_types/resolver_hooks.rs
  • src/bun_alloc/lib.rs
  • src/jsc/host_fn.rs
  • src/tcc_sys/tcc.rs
  • src/runtime/bake/DevServer.rs
  • src/install/lockfile/lockfile_json_stringify_for_debugging.rs
  • src/jsc/JSValue.rs
  • src/bun_alloc/baby_vec.rs
  • src/css/selectors/parser.rs
  • src/css/generics.rs
  • src/css/properties/mod.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.


Walkthrough

This PR removes unsupported or redundant trait implementations and conversion paths across core utilities, bundler and collection types, CSS types, JavaScript bindings, installation code, and runtime code.

Changes

Trait implementation removals

Layer / File(s) Summary
Core utility and container APIs
src/ast/e.rs, src/bun_alloc/*, src/bun_core/*
Removes numeric conversion, borrowed iteration, borrowing, byte-slice array appending, const-pointer atomic operations, error conversion, and slice tuple-formatting implementations.
Bundler and collection interfaces
src/bundler/*, src/collections/array_hash_map.rs
Removes indexed access for CompileResultSlots, ordering for StableRef, and ArrayHashMapExt support.
CSS trait implementations
src/css/*
Removes redundant cloning, equality, hashing, compatibility, parsing, and CSS serialization implementations.
JavaScript binding conversions
src/jsc/*, src/react_compiler/hir/mod.rs
Removes const-pointer iteration, selected FromAny conversions, direct raw-pointer constructor returns, and FloatValue conversion into f64.
Installation and runtime defaults
src/install/*, src/install_types/*, src/runtime/*, src/tcc_sys/tcc.rs
Removes selected JSON, default, optional-file, error, and compiler configuration implementations.

Priority: ➖ Normal

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: removal of 49 unused trait implementations across 12 crates.
Description check ✅ Passed The description explains the problem, solution, scope, validation steps, retained candidates, and known test limitations. It uses alternate headings instead of the repository template headings, but it…
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.

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.

Code review found no issues

No high-confidence issues detected in this change.

@robobun

robobun commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 12:10 AM PT - Sep 21st, 2026

✅ @robobun, your commit 85c4883521d5839e3403b1eb310f5a219c193a31 passed in Build #119128! 🎉


🧪   To try this PR locally:

bunx bun-pr 43664

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

bun-43664 --bun

@Jarred-Sumner
Jarred-Sumner merged commit 8f6e9e9 into main Sep 21, 2026
11 of 12 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the robobun/2c1c7e31/dead-code-sweep branch September 21, 2026 19:57
Jarred-Sumner pushed a commit that referenced this pull request Sep 24, 2026
…hs (#43840)

### Problem
- `src/codegen/generate-classes.ts` still has the DOMJIT emitter (C++
signatures, `WithoutTypeChecks` wrappers, result-type asserts, Rust
thunks). None of it can run: `define()` in `class-definitions.ts` has
set `DOMJIT = undefined` on each field since #14005 (2024-09), and all
31 `*.classes.ts` files go through `define()`.
- Nothing is left for it to bind: #35002 deleted each Rust
`*_without_type_checks` fast path, and #36756 and #36903 removed the C++
leftovers.
- A `DOMJIT:` option in a `.classes.ts` file does nothing. Five exist.

### Fix
- Delete the emitter, the option type, the strip in `define()`, the five
ignored blocks, and the stale notes next to them: 6 files, 273 lines
removed.
- The generated output is the same except for 94 empty `#if
BUN_DEBUG`/`#endif` pairs in `ZigGeneratedClasses.h` and three
DOMJIT-only `#include`s in `ZigGeneratedClasses.cpp`.
`generated_classes.rs` is byte-identical.
- Verified: the generator run before and after, `bun bd`, `tsc -p src`,
and the tests in the Notes.
- Self-reviewed: 13 concerns raised, 13 addressed (Notes).

### Background
- DOMJIT is the JavaScriptCore fast path that lets the JIT call a host
function with unboxed, type-checked arguments. The generator could emit
a C++ signature and a Rust thunk for each method.
- The hand-written DOMJIT users (`Buffer.alloc`, `performance.now`,
`bun:ffi`) do not use this generator. They stay.
- Earlier sweeps (#39249, #41169) called this removal a design call.
This PR asks for that call alone.

### Downsides
- To turn generated DOMJIT on again, a person must restore these paths
from git history and write the Rust fast paths again.
- No runtime cost found. Checked: the generated `.cpp`, `.h` and `.rs`
differ only as Fix says.

<details><summary>Notes</summary>

**Removed**
- `generate-classes.ts`: `DOMJITName`, `argTypeName`, `DOMJITType`,
`DOMJITFunctionDeclaration`, `DOMJITFunctionDefinition`,
`domJITTypeCheckFields`, `RustDOMJITArgType`. Also the `DOMJIT` branches
in `zigExportName`, `propRow`, `renderDecls`, the `expectedResultType`
asserts in the host-function wrapper, both Rust thunk loops, and the
`DOMJITAbstractHeap.h`, `FrameTracers.h`, `DFGAbstractHeap.h` includes
of the generated prologue. The destructures that named `DOMJIT` also
lose the unused `cache` and `value` bindings.
- `class-definitions.ts`: the `DOMJIT?:` option type and the two
`.map()` calls in `define()` that erased it.
- Ignored live blocks: `Crypto.randomUUID`, `Crypto.timingSafeEqual`
(`crypto.classes.ts`), `ServerWebSocket.publishText`, `publishBinary`
(`server.classes.ts`), `TextDecoder.decode` (`encoding.classes.ts`).
- Stale notes: the commented-out `// DOMJIT: {` blocks with their crash
notes on `sendText`, `sendBinary` (2023) and `getRandomValues` (#13470,
2024-08), and three orphan "DOMJIT fast path" comments in
`src/runtime/webcore/Crypto.rs` whose functions #35002 deleted.

**Self-review, and what changed because of it**
- The first draft mixed this design call with 22 log scopes and seven
fields. It now ships alone, with its history in the body.
- The leftover DOMJIT notes (`Crypto.rs`, the commented-out blocks) are
folded in.
- Three deletions that open PRs carry were dropped (#40232, #41385).
- Five items that open PRs use were taken out of the held branch
(#43283, #31855, #42819, #39222, #37518). Two `builtins.d.ts` lines were
dropped too: `src/codegen/replacements.ts` defines
`$ImportKindLabelToId`, so that declaration is live.

**History**
- #13470 (2024-08) turned DOMJIT off for `getRandomValues`. #14005
(2024-09) added the strip in `define()` as the repair for the #14001
segfault. #35224 found the cause (the wrappers returned `{ result }`
with a null exception slot) and tried to repair the generated wrappers.
A stale-PR cleanup closed it with no maintainer comment. #35002 deleted
the Rust fast paths, so the option cannot come back without new native
code.

**Kept on purpose**
- The hand-written `DomCall` path for `bun:ffi` (`src/jsc/host_fn.rs`,
`src/runtime/ffi`), the C++ DOMJIT signatures in `JSBuffer.cpp`,
`JSPerformance.cpp`, `NodeVM.cpp`, `JSSQLStatement.cpp`, and
`test/js/bun/jsc/domjit.test.ts`.

**Tests (debug build)**
- `test/js/web/encoding/text-decoder.test.js` 127 pass,
`test/js/web/web-globals.test.js` 23 pass,
`test/js/bun/util/randomUUIDv5.test.ts` 40 pass,
`test/js/bun/websocket/websocket-server.test.ts -t sendBinary` 5 pass.
- `websocket-server.test.ts -t "publish|send"`: 44 pass, 4 time out near
19 s under debug+ASAN. A debug binary built from main fails the same 4.
- `test/js/bun/jsc/domjit.test.ts`: 40 pass, 10 time out at the
100k-iteration sizes. A debug binary built from main gives the same
40/10.

**The rest of this sweep**
- Relink of the debug build with `-Wl,--gc-sections`, then the DWARF
line table of the result: 2,290 of 31,118 Rust `fn`s have no live line.
After `cargo check` on six targets only three `pub fn`s had no caller
anywhere. The 96 trait impls with no caller are the ones #43664 kept on
purpose.
- clang `-fsyntax-only -Wunused-function -Wunused-template
-Wunused-member-function -Wunused-macros` over bun's 177 C/C++
translation units (the build passes `-Wno-unused-function`): ten hits.
Open PRs delete them, or an `#if` uses them.
- oxlint `no-unused-vars` over `src/js`, `src/codegen`, `scripts`,
`packages`. cargo's `unused_dependencies` lint over four targets. A scan
for commented-out blocks (62 lines in the repo). Nothing new that is
certain.
- Each deletion was compared with the diffs of the 36 open dead-code
PRs. Left out because an open PR has it: `Bun__napi_get_version`
(#40232), two unused generator locals (#41385).

**Verified and held for the next run** (branch
`robobun/9a0817f5/dead-code-scopes-fields`, 25 files, 81 lines removed)
- 20 `declare_scope!` scopes that nothing logs to: `JSC`, `STR`,
`Bundle` and `scan_counter` (outer pair), `Store`, `hot_reloader`,
`CLI`, `LibUVBackend`, `ResolveInfoRequest`, `GetHostByAddrInfoRequest`,
`CAresNameInfo`, `GetNameInfoRequest`, `CAresReverse`, `CAresLookup`,
`quic_session`, `PathWatcherManager`, `S3Client`, `S3Stat`, `AWS`, `uws`
(`uws_sys/socket.rs`). rustc does not lint an item that another crate's
macro expands.
- Fields: `Runtime::Features.jsx_optimization_inline` with the local
`can_be_inlined`, `DebugOptions.output_file`, `ArchiveIterator.filter`,
`PackageManager.total_scripts`, `CommandLineArguments.lockfile`,
`ArgumentsSlice::_vm`. Also `struct_Channeldata` and two empty modules
in `napi_body.rs`.
- It passed `rust:check-all` (12 targets), release and `--cfg bun_debug
--cfg bun_asan` checks on linux, windows and darwin, and `cargo check
--tests` before the trim below. The trimmed commit passes `cargo check`
on linux.
- Taken out because an open PR uses the item:
`BundleOptions.code_coverage` (#43283 reads it), scope `ModuleLoader`
(#31855), scope `PROCESS` (#42819, #39222), `impl Clone for JsPoster`
and its vtable slot (#37518 rewrites the vtable).

**Follow-up candidates, not verified dead**
- `Parser Options.preserve_unused_imports_ts` is never `true`. tsconfig
`importsNotUsedAsValues` is parsed into
`preserve_imports_not_used_as_values` but never reaches the parser, in
the released binary too. This looks like a missing feature.
- `completions/bun-cli.json` (4,513 lines) and
`misctools/generate-cli-completions.ts` (728 lines): nothing in the repo
reads the JSON, but feature PRs still edit it by hand.
- `bench/snippets/runner-entrypoint.js` (244 lines): no reference, first
line says "this isn't done yet", last real change 2023-05.
- Ten `impl_timer_owner!` accessors have no caller because `dispatch.rs`
recovers the owner with its own `owner!` macro. Which mechanism stays is
a design call.
- `mordant-baseline.toml` still counts about 170 `unused_pub` findings
(`sys/lib.rs` 56, `libuv_sys/libuv.rs` 41, `errno/windows_errno.rs` 31).
`bun run rust:mordant` names them.

</details>
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