Skip to content

lint: hold the kind of the configuration file as one enum - #45664

Open
robobun wants to merge 1 commit into
mainfrom
robobun/d84c04d0/lint-config-kind-enum
Open

robobun wants to merge 1 commit into
mainfrom
robobun/d84c04d0/lint-config-kind-enum

Conversation

@robobun

@robobun robobun commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • bun lint holds the kind of the configuration file as five bools on Reader and Config (src/lint/linter/config). Six constructors make four of the 32 combinations.
  • The mordant lint bool_cluster reports three of them: "linter::config::flat::Semantics has 3 bool fields (keeps_options, accepts_all_plugins, is_legacy), and the crate only makes 3 of their 8 combinations".

Fix

  • Semantics is an enum: Flat, BuiltIn, Legacy, Rc. Each former bool is a predicate with an exhaustive match. Each constructor names its kind, and Config stores it.
  • Not changed: ResolvedConfig and LanguageOptions keep their kind bools. Both are public and are also built without a Config (see Notes).
  • Removes the baseline line "bool_cluster:src/lint/linter/config/flat.rs" = 1.
  • Verified on 9b4284f: bun run rust:mordant (564c8da, three targets) exits 0. Eight test/cli/lint files (debug build): 2615 pass, 1 fail. main fails that test the same way (see Notes).

Background

  • The kinds: an eslint.config.* (Flat), the ESLint 8 files (Legacy), an .oxlintrc.json (Rc), and the configuration for a project with none (BuiltIn).
  • Designs weighed: an enum only as the argument of Reader::finish quiets the lint, but Config keeps three bools. Three variants leave two bools. Sharing the driver's Flavor adds public surface.

Downsides

  • Release .text of bun_lint grew by 119 bytes on x86-64 and 28 on arm64, and the per-file path by 1 instruction. Measured on the first base (5866b5f), not again after the rebase.
  • Debug builds do not inline the five predicates: 232 bytes, 43 call sites.
Notes

What stays, and why:

  • ResolvedConfig keeps skips_unknown_rules, prefers_typescript_rules, has_every_rule and understands_oxlint_comments (src/lint/linter/resolved.rs). Config::merge copies the kind into them and the linter decides by them. The bool_cluster finding is on the private types of linter::config. ResolvedConfig is a public type with a second producer that has no Config (ResolvedConfig::from_json).
  • LanguageOptions keeps rejects_what_parser_rejects, is_oxlint, has_no_foreign_config and has_oxc_scopes (src/lint/language.rs). Config::merge is one of several producers. bun fmt (src/lint/driver/fmt/mod.rs) and the module graph (src/lint/graph/lib.rs) build one without a Config. For oxfmt, bun fmt sets rejects_what_parser_rejects to false with is_oxlint false, a combination that no Semantics variant gives.
  • The driver keeps its own Flavor beside the Config. from_flat keeps its private has_every_rule: bool parameter and picks Flat or BuiltIn from it. eslint_major stays a field because the driver can set it after construction.

Truth table, checked against the literals of the constructors on 9b4284f (T true, F false):

Kind Constructor keeps_options accepts_all_plugins is_legacy prefers_typescript_rules has_every_rule
Flat from_flat_json, from_flat_json_with_plugins T F F F F
BuiltIn without_config_file T F F F T
Legacy from_legacy T T T F F
Rc from_rc_json, from_rc_json_with_plugins F T F T F

Three reads became a match on the kind: builtin_plugin in Reader::rules, the seed of eslint_major in Reader::finish, and env_tables in Config::merge. The other 43 are a predicate call in place of a field read.

Verification on the commit of this PR (base 9b4284f):

  • bun run rust:mordant exits 0 and prints no finding over the baseline. cargo clippy -p bun_lint --no-deps prints no warning, and cargo fmt --check is clean.
  • bun bd test --max-concurrency=4 --timeout=120000, one file at a time: config-files (842 pass, 260 skip), bunfig (406), defaults (261), lint (711 pass, 14 skip, 17 todo, 1 fail), js-plugins (146 pass, 1 skip), explain (19), native-rules (193), oxc-names (37). conformance.test.ts was not run.
  • Why those flags: the machine has 12 cores and 16 GB. With the defaults, many of these tests need more than 5 s on a debug build, and the concurrent bun lint runs use all of the memory.
  • The one failure is the test "an eslint comment nested about as deep as the stack allows is reported as one error", which bun check: fix errors lost since #45800, and 9 to 37% faster #46001 added. Its bun lint run needs 140 s on this debug build, and the test helper ends a child after 100 s (test/cli/children.ts). Run by hand, the command exits 1 and reports one error for each of the 274 files, which is what the test expects.
  • A debug build of main (9b4284f, without this change) behaves the same on the same machine: 146 s by hand with byte-identical output, and the same failure in the test.
  • No test/ change: the diff changes no behaviour, so no test can fail before it and pass after it. The check that the finding is gone is the mordant job on this PR. That job is advisory, so read its result directly.

Measurements. These were taken on the first base (5866b5f), with and without the change as it was then. They were not taken again after the rebase:

  • size_of: Config 848 -> 840 bytes, Reader 272 -> 272 bytes, Semantics 3 -> 1 byte (x86-64).
  • Release .text of bun_lint (llvm-size, codegen-units=1): 2,711,095 -> 2,711,214 bytes on x86-64, 2,517,448 -> 2,517,476 bytes on arm64.
  • Release asm, whole crate: 629,452 -> 629,474 instructions on x86-64, 629,372 -> 629,379 on arm64. No Semantics predicate exists as a symbol, and nothing calls one.
  • Release asm per function, as instructions / conditional jumps / calls, x86-64: Within::get 640/87/30 -> 640/87/30. Config::get_with_frameworks 340/27/10 -> 341/27/10. Config::merge 5036/616/337 -> 5066/609/338. Config::js_rule 285/32/17 -> 285/32/17. Reader::native_rule 78/9/3 -> 86/9/3. Reader::rules 998/135/69 -> 1001/134/69. Reader::finish 1263/127/54 -> 1261/128/54.
  • The same on arm64: Within::get 642/83/34 -> 641/83/34. Config::get_with_frameworks 186/25/10 -> 187/25/10. Config::merge 5231/662/376 -> 5225/657/376. Config::js_rule 267/33/22 -> 269/33/22. Reader::native_rule 72/9/3 -> 76/10/3. Reader::rules 910/145/70 -> 925/149/71. Reader::finish 1192/136/51 -> 1177/136/51.
  • Debug build: the five predicates are 40, 40, 40, 56 and 56 bytes.

Self-review: five concerns, all about this description and none about the code. State the two exclusions with their true reasons (two concerns). Promise no follow-up. Write the verification to what ran on the commit that ships. Name the overlap with other pull requests.

Overlap, as of 2026-10-21:

Behaviour change: none


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

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 2 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: oven-sh/bun/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: f5b0cd6b-b77d-411d-b2d0-2157eb4cc9bb
📥 Commits

Reviewing files that changed from the base of the PR and between 71bc818 and fd87712.

📒 Files selected for processing: 5
  • mordant-baseline.toml
  • src/lint/linter/config/eslintrc.rs
  • src/lint/linter/config/flat.rs
  • src/lint/linter/config/mod.rs
  • src/lint/linter/config/rc.rs
  • 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.

@robobun

robobun commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: ready for review. Rebased onto 9b4284f on 2026-10-21.

@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 mechanical, behavior-preserving collapse of five kind bools into one enum.

What was reviewed:

  • Each Semantics predicate in src/lint/linter/config/flat.rs against the literals the four constructors used to pass (Legacy T/T/T/F/F, Rc F/T/F/T/F, Flat T/F/F/F/F, BuiltIn T/F/F/F/T) — all match.
  • The two rewritten matches (written_for in Reader::rules, the globals Tables pick in Config::merge) and the eslint_major seed in finish() — each arm maps to the same result the old guard/tuple match produced, given has_every_rule and prefers_typescript_rules were never both true.
  • Grepped src/lint/linter/config for leftover reads of the removed fields — none; Semantics was already imported in eslintrc.rs and rc.rs.
Extended reasoning...

The change touches only the bun lint configuration loader (src/lint/linter/config/{flat,mod,eslintrc,rc}.rs) and drops one mordant baseline line; it replaces five booleans on Reader and Config with a single Semantics enum whose predicates reproduce the old per-constructor values. It touches no security-sensitive surface (no auth, crypto, path handling, or untrusted-input parsing changes). It was approved because every predicate and both rewritten matches were checked against the pre-change literals and found equivalent, the diff is self-contained within one private module, no CODEOWNERS entry covers these paths, and the bug hunt ran dry with no findings.

@robobun

robobun commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 5:34 AM PT - Oct 21st, 2026

❌ @robobun, your commit fd87712 has 1 failures in Build #129904 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 45664

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

bun-45664 --bun

The kind of the configuration file was spelled by five bools on the
Reader and on Config: keeps_options, accepts_all_plugins, is_legacy,
prefers_typescript_rules and has_every_rule. The six constructors make
four of their 32 combinations, one per kind: a flat configuration, the
built-in configuration, the files of ESLint 8, and an .oxlintrc.json.

Semantics is now an enum with those four variants. The Reader of each
constructor names its kind once, Reader::finish takes no argument, and
Config stores the kind in place of its five bools. Each bool is a
predicate of the enum with an exhaustive match, so a fifth kind stops
at every one of them.

Removes the mordant baseline line
"bool_cluster:src/lint/linter/config/flat.rs" = 1.
@robobun
robobun force-pushed the robobun/d84c04d0/lint-config-kind-enum branch from b7c3c12 to fd87712 Compare October 21, 2026 05:55

@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 mechanical bool-cluster-to-enum refactor that reproduces the old flag combinations exactly.

What was reviewed:

  • Checked each Semantics predicate against the old Reader/finish literals of from_flat (both has_every_rule values), from_legacy and from_rc_json — all four rows match.
  • Checked the three rewritten matches (builtin_plugin in read_rules, eslint_major in finish, env_tables in merge) — each is equivalent to the old bool conditions, since has_every_rule holds only for BuiltIn and prefers_typescript_rules only for Rc.
  • Grepped for leftover reads of the removed fields: the remaining .has_every_rule/.prefers_typescript_rules uses are on ResolvedConfig, which keeps its bools; no other Reader constructor exists.
Extended reasoning...

The change touches only src/lint/linter/config (flat.rs, mod.rs, eslintrc.rs, rc.rs) and drops one mordant-baseline.toml line; it replaces five bools on Reader and Config with a single pub(super) enum and turns field reads into method calls. It touches no security-sensitive surface (no parsing of untrusted input changes, no auth or crypto). The approve decision rests on having verified the per-variant truth tables and the three rewritten matches against the pre-change constructor literals, the absence of any remaining constructor or reader of the removed fields, no CODEOWNERS coverage of these paths, and the bug hunt ending on a dry streak with no objections visible in the timeline.

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