ast/resolver/bundler: collapse dependent bool pairs into enums - #36765
Conversation
S::Local.{was_ts_import_equals, was_commonjs_export} are set on disjoint
parse/visit paths and never both true; fold into LocalOrigin.
ResultFlags IS_EXTERNAL_AND_REWRITE_IMPORT_PATH is only ever set together
with IS_EXTERNAL; expose the tri-state as ExternalKind with a single
set_external_kind() writer so the invalid (rewrite && !external) state is
no longer expressible through the accessor API.
BakeExtra.{is_route, fully_static} are only written together (fully_static
is assigned inside the is_route branch) and is_route itself is never read;
fold into BakeRouteKind.
No behavior change.
|
Warning Review limit reached
Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughThe change replaces boolean metadata with ChangesTyped state metadata
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/resolver/result.rs`:
- Line 155: Remove the redundant “Convenience accessors with field-style names.”
comment above the accessor definitions in the result implementation, leaving the
accessor code unchanged.
- Around line 162-170: Update ResultFlags::external_kind so the
REWRITE_IMPORT_PATH and IS_EXTERNAL invariant is enforced in release builds,
replacing the debug-only assertion with an unconditional check or otherwise
preventing rewrite-only flag values. Preserve the existing ExternalRewritePath,
External, and NotExternal classification for valid flag combinations.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b8b4cb08-4006-4128-9671-a80919de2359
📒 Files selected for processing (16)
src/ast/s.rssrc/bundler/OutputFile.rssrc/bundler/bundle_v2.rssrc/bundler/linker_context/convertStmtsForChunk.rssrc/bundler/linker_context/generateChunksInParallel.rssrc/bundler/linker_context/generateCodeForFileInChunkJS.rssrc/js_parser/p.rssrc/js_parser/parse/parse_entry.rssrc/js_parser/parse/parse_typescript.rssrc/js_parser/scan/scan_imports.rssrc/js_parser/visit/mod.rssrc/js_parser/visit/visit_stmt.rssrc/resolver/lib.rssrc/resolver/resolver.rssrc/resolver/result.rssrc/runtime/bake/production.rs
There was a problem hiding this comment.
LGTM — clean type-narrowing refactor that makes invalid state combinations unrepresentable.
What was reviewed:
- Grepped for every removed identifier (
was_ts_import_equals,was_commonjs_export,set_is_external,is_external_and_rewrite_import_path,fully_static,is_route) — no stale references; theis_routehits inincremental_graph.rsare on an unrelated dev-server type. - Verified
can_merge_withstill compares only the commonjs-export bit, soTsImportEqualslocals continue to merge withNormalas before. - Checked
set_external_kindwrites both bits and that directResultFlags::IS_EXTERNALstruct literals elsewhere inresolver.rsremain valid states. - Confirmed
writeOutputFilesToDisk.rsonly usesBakeExtra::default(), so the newBakeRouteKind::NotRoutedefault preserves the oldis_route: false, fully_static: false.
Extended reasoning...
Overview
This PR collapses three pairs of dependent booleans into tri-state enums across 16 files in the AST (S::Local), resolver (ResultFlags), and bundler (BakeExtra). Each pair had the invariant that one bool implied the other (or they were mutually exclusive), so only three of four states were reachable. The refactor is a 1:1 mechanical translation of every read and write site.
Security risks
None. This is an internal type refactor with no user-facing surface, no parsing of untrusted input, and no change to control flow.
Level of scrutiny
Medium. The touched files are core (parser, resolver, bundler linker), but the change is purely representational — the compiler enforces that every construction and match site was updated. The real risk in a bool→enum collapse is a subtle semantic drift at a comparison site; the PR explicitly calls out the one such site (can_merge_with compares only is_commonjs_export(), not full origin equality) and I verified the truth table matches the old behavior for all origin pairs.
I grepped for every removed field/method name and confirmed zero remaining references. The two .is_route hits in dev_server/incremental_graph.rs are on a different struct entirely. The six ResultFlags::IS_EXTERNAL struct-literal initializers in resolver.rs (unchanged by this PR) produce ExternalKind::External under the new decoder, which is correct — they never set the rewrite bit before either.
Other factors
- The
..Default::default()removals at the twoS::Localconstruction sites are safe: the struct now has exactly four fields, all listed explicitly, so a missing field would be a compile error. set_external_kind(NotExternal)clears both bits, matching the oldset_is_external(false); set_is_external_and_rewrite_import_path(false)sequence even if the flags had been set earlier on the sameresult.- The PR description names the existing test files that exercise each of the three paths; no new test is warranted since there is no input that behaves differently before vs. after.
- No prior reviews or outstanding comments on the PR.
external_kind() now returns NotExternal whenever IS_EXTERNAL is clear, regardless of the rewrite bit, so it agrees with is_external() for every bit pattern in release builds. debug_assert still flags the invalid (rewrite && !external) combination that set_external_kind never produces. Also drop a stale comment and fix the FullyStaticRoute doc reference.
external_kind() now checks IS_EXTERNAL before REWRITE_IMPORT_PATH so it agrees with is_external() for every bit pattern in release builds; the debug_assert still flags the (rewrite && !external) combination that set_external_kind never produces. Enum doc comments reduced to single-line variant docs.
|
Status: ready for review. Self-review traced every write and read site of the three bool pairs against Existing coverage exercised and passing locally: No new regression test is included: this is a representation-only refactor, so by construction there is no input whose output differs between |
There was a problem hiding this comment.
LGTM — mechanical type-narrowing refactor; my earlier note about the unstaged result.rs hunks in 8f50cb5 is resolved by f44ee83.
What was reviewed:
- Traced every read/write of the three replaced bool pairs;
can_merge_withstill compares only the commonjs-export bit,merge_adjacent_local_stmtspreservesorigin, andexternal_kind()now agrees withis_external()for every bit pattern. - Confirmed no stale callers of
was_ts_import_equals/was_commonjs_export/set_is_external/is_external_and_rewrite_import_path/fully_staticremain; the.is_routehits inincremental_graph.rsare on an unrelated dev-server struct. - Checked the two
S::Localliterals that dropped..Default::default()— both now spell out all four fields, so no default is silently changed. - Verified all six literal
flags: ResultFlags::IS_EXTERNALconstruction sites inresolver.rsset onlyIS_EXTERNAL(=ExternalKind::External), consistent with the new invariant.
Extended reasoning...
Overview
This PR collapses three pairs of dependent booleans into tri-state enums across 16 files in the parser (src/js_parser/), AST (src/ast/s.rs), resolver (src/resolver/), bundler (src/bundler/), and bake runtime (src/runtime/bake/production.rs). Each pair had the invariant that at most one member could be true; the enum makes that structural. The three replacements are S::Local::{was_ts_import_equals, was_commonjs_export} → LocalOrigin, ResultFlags::{IS_EXTERNAL, IS_EXTERNAL_AND_REWRITE_IMPORT_PATH} → ExternalKind (still bit-packed), and BakeExtra::{is_route, fully_static} → BakeRouteKind.
Security risks
None. This is a representation-only change to internal data structures with no user-facing surface, no parsing of untrusted input, and no auth/crypto/permissions involvement.
Level of scrutiny
Medium — the touched files are core (parser, resolver, bundler linker), but every hunk is a mechanical field-rename or accessor swap. Per REVIEW.md's "treat every refactor as guilty until proven behavior-preserving," I traced each read and write site:
LocalOrigin:can_merge_withcomparesorigin.is_commonjs_export()on both sides, matching the old behavior wherewas_ts_import_equalswas not compared (soTsImportEqualsstill merges withNormal). The two construction sites inparse_typescript.rsandvisit_stmt.rsdropped..Default::default()but now explicitly list all fourS::Localfields, so nothing is silently defaulted.merge_adjacent_local_stmtsandparse_entry.rscopyoriginwhere they previously copied both bools. All five read sites (convertStmtsForChunk,p.rs,parse_typescript,scan_imports,visit/mod) use the accessor matching the bool they previously read.ExternalKind: The threeresolver.rssites that previously wrote both flags with the same value now callset_external_kind(if is_external { ExternalRewritePath } else { NotExternal }), which sets the same bits. The sole reader inbundle_v2.rsis already insideif is_external(), andexternal_kind()keys onIS_EXTERNALfirst (post-f44ee83), so it agrees withis_external()for every bit pattern. The six literalResultFlags::IS_EXTERNALconstructions are unchanged and decode toExternal.BakeRouteKind:is_routewas write-only (grepped — no readers anywhere), so dropping it is safe.fully_staticwas only written whenis_route = trueand only read via.bake_extra.fully_staticinproduction.rs, now.route.is_fully_static().
Other factors
All bot feedback (comment-cop paragraph-length comments, CodeRabbit's accessor-comment and external-flag-invariant notes) is resolved and marked as such in the timeline. My own prior inline comment about 8f50cb5's commit message describing unstaged result.rs edits was answered — f44ee83 landed those hunks, and the current diff matches. The PR description lists the existing test files exercised locally; no new test is included, which is appropriate for a representation-only refactor with no input that could behave differently. Grepped the whole src/ tree for the removed identifiers and found none. The .is_route/.fully_static-looking hits in incremental_graph.rs are fields on an unrelated dev-server file struct, not BakeExtra.
Three pairs of booleans whose members are never independently true, collapsed into tri-state enums so the invalid combinations are unrepresentable.
S::Localoriginwas_ts_import_equalsis set only when parsing TSimport x = ...(parse_typescript.rs), andwas_commonjs_exportis set only when the visitor rewritesexports.x = ...(visit_stmt.rs). Both construct a freshS::Localvia struct literal +Default, so a single local is never both. Folded into:can_merge_withcontinues to compare only the commonjs-export bit (origin.is_commonjs_export()) to keep merging ofTsImportEqualswithNormallocals unchanged.ResultFlagsexternal kindEvery site that sets
IS_EXTERNAL_AND_REWRITE_IMPORT_PATHdoes so with the same value it just wrote toIS_EXTERNAL(resolver.rs browser/alias remap paths), and the only reader checks it insideif is_external(). Replaced the pair of setters with a singleset_external_kind(ExternalKind); storage stays packed in the existingResultFlags: u8.BakeExtraroute kindgenerateChunksInParallelonly assignsfully_staticinside theis_route = truebranch, andis_routeitself is never read anywhere. Folded into:Why
Each pair of bools encodes four states where only three are reachable. The enum makes the invariant structural instead of incidental, so future call sites cannot produce the nonsensical combination (
rewrite && !external,fully_static && !is_route, both origin bits set). ForBakeExtrathis also drops the write-onlyis_routefield.Verification
No behavior change. Verified against existing coverage:
test/bundler/bundler_cjs2esm.test.ts(commonjs-export rewrite path)test/bundler/esbuild/ts.test.ts(TS import-equals)test/bundler/esbuild/packagejson.test.ts,test/bundler/bundler_browser.test.ts(browser remap / external rewrite)test/bundler/bundler_edgecase.test.tstest/bake/dev-and-prod.test.ts,test/bake/framework-router.test.tsThis is a type-narrowing refactor; there is no input that behaves differently before vs. after, so no new regression test is included.