fix(dataflow): barrel-only tie-break misclassifies hybrid files, drops their call edges - #2488
Merged
Merged
Conversation
…s their call edges Issue #2339: `is_barrel_file`/`isBarrelFile` classified a file as barrel-only using `reexports >= ownDefs`. A file with exactly one reexport and exactly one own definition hit that `>=` and got misclassified as barrel-only, even though it's a genuine hybrid (real logic plus a reexport), not a pure barrel. Once misclassified, the call-edge builder skips ALL of that file's own outgoing call/receiver edges, so on any incremental build where the file gets pulled into the barrel-candidate reparse (because something imports from it), its own call edges are silently dropped and never re-emitted. Changed the tie-break to strict `>` in both engines. This is orthogonal to #1848's fix, which scopes *which files* are even eligible for this check (only transient barrel-candidate reparses, never a file genuinely part of the current build's changed set) — not the comparison itself; verified no existing test exercises the exact reexports==ownDefs tie, so this couldn't have silently broken an already-covered case. Also fixes a second, previously-hidden bug discovered while writing this issue's regression test: the JS/WASM engine's `reparseBarrelFiles` deleted a barrel candidate's outgoing edges without first clearing `dataflow` rows that reference them via `call_edge_id`, so the delete threw a FOREIGN KEY constraint error whenever the candidate's own call edges were also tracked by interprocedural dataflow — silently swallowed by the surrounding try/catch's `debug()` call (a no-op unless verbose), aborting the entire barrel reparse for that file. The Rust engine already carries this exact fix (#979); the JS engine never got the mirror. Without it, the new regression test couldn't even reach the `>=`/`>` code path on the WASM engine, since barrel reparsing silently failed before ever classifying the file. Confirmed the fix is necessary by verifying the test fails without it (edges genuinely go missing) and passes with it. New tests/integration/issue-2339-barrel-tiebreak-incremental.test.ts, dual-engine (mirrors issue-1174-chained-barrel-incremental.test.ts's structure): a hybrid.js at the exact reexports==ownDefs==1 tie, whose own call edge to helper.js must survive an incremental rebuild that pulls it into the barrel-candidate reparse. Confirmed this fails without either fix and passes with both. tests/integration/issue-1174-chained-barrel- incremental.test.ts (parser.js has 1 reexport vs. 4 defs, never at the tie) is unaffected — still passes. Closes #2339 docs check acknowledged: internal engine bug fix + regression test, no new user-facing behavior or CLI surface change. Impact: 6 functions changed, 0 affected
Contributor
Contributor
Greptile SummaryThe PR corrects barrel-only classification for hybrid files at the reexport/definition tie and mirrors Rust’s foreign-key-safe edge cleanup in the JS/WASM pipeline.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
A["Incremental build changes app.js"] --> B["Side-load and reparse hybrid.js"]
B --> C["Delete dataflow rows referencing old outgoing edges"]
C --> D["Delete old outgoing edges"]
D --> E{"reexports > own definitions?"}
E -->|No: 1 equals 1| F["Treat as hybrid"]
E -->|Yes| G["Treat as barrel-only"]
F --> H["Re-emit hybrid.js call edges"]
Reviews (2): Last reviewed commit: "fix(dataflow): barrel-only tie-break mis..." | Re-trigger Greptile |
Contributor
Codegraph Impact Analysis2 functions changed → 23 callers affected across 3 files
|
Contributor
Author
|
@greptileai please re-review — replied to the dataflow-regeneration finding above with an investigation showing it's a pre-existing, dual-engine-consistent characteristic (not a regression from this PR), and filed #2489 to track it separately. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
is_barrel_file/isBarrelFileclassified a file as barrel-only usingreexports >= ownDefs. A file with exactly one reexport and exactly one own definition hit that>=and got misclassified as barrel-only, even though it's a genuine hybrid (real logic plus a reexport), not a pure barrel. Once misclassified, the call-edge builder skips ALL of that file's own outgoing call/receiver edges, so on any incremental build where the file gets pulled into the barrel-candidate reparse (because something imports from it), its own call edges are silently dropped and never re-emitted.Fix
Changed the tie-break to strict
>in both engines. This is orthogonal to #1848's fix, which scopes which files are even eligible for this check (only transient barrel-candidate reparses, never a file genuinely part of the current build's changed set) — not the comparison itself. Verified no existing test exercises the exactreexports==ownDefstie, so this couldn't have silently broken an already-covered case (e.g.issue-1174-chained-barrel-incremental.test.ts's hybrid file has 1 reexport vs. 4 defs, never at the tie — still passes).A second bug found and fixed along the way
While writing this issue's regression test, discovered the JS/WASM engine's
reparseBarrelFilesdeletes a barrel candidate's outgoing edges without first clearingdataflowrows that reference them viacall_edge_id— so the delete throws a FOREIGN KEY constraint error whenever the candidate's own call edges are also tracked by interprocedural dataflow. This was silently swallowed by the surrounding try/catch'sdebug()call (a no-op unless verbose), silently aborting the entire barrel reparse for that file. The Rust engine already carries this exact fix (#979); the JS engine never got the mirror.Without this fix, the new regression test couldn't even reach the
>=/>code path on the WASM engine — barrel reparsing was silently failing before ever classifying the file. Confirmed both fixes are necessary by temporarily reverting each independently and watching the test fail for the expected reason, then confirming it passes with both applied.Test plan
tests/integration/issue-2339-barrel-tiebreak-incremental.test.ts, dual-engine (mirrorsissue-1174-chained-barrel-incremental.test.ts's structure): a hybrid file at the exactreexports==ownDefs==1tie, whose own call edge must survive an incremental rebuild that pulls it into the barrel-candidate reparsecargo test --workspace→ 1011 passedcargo fmt --check/cargo clippy --workspace --all-targets -- -D warnings→ cleanissue-1174-chained-barrel-incremental.test.ts(no regression)npm run lintcleannode dist/cli.js diff-impact origin/main→ contained to the 2 functions actually touched (reparseBarrelFiles,isBarrelFile)Closes #2339