-
Notifications
You must be signed in to change notification settings - Fork 5
fix: recognize same-file constant consumption in dead code detector #859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
16785c1
f91fde0
3e21c99
f73d979
d95a135
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -582,6 +582,15 @@ function classifyNodeRolesFull(db: BetterSqlite3Database, emptySummary: RoleSumm | |||||||||||||||||||||||||
| prodFanInMap.set(r.target_id, r.cnt); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Files with at least one callable (non-constant) connected to the graph. | ||||||||||||||||||||||||||
| // Constants in these files are likely consumed locally via identifier reference. | ||||||||||||||||||||||||||
| const activeFiles = new Set<string>(); | ||||||||||||||||||||||||||
| for (const r of rows) { | ||||||||||||||||||||||||||
| if ((r.fan_in > 0 || r.fan_out > 0) && r.kind !== 'constant') { | ||||||||||||||||||||||||||
| activeFiles.add(r.file); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Delegate classification to the pure-logic classifier | ||||||||||||||||||||||||||
| const classifierInput = rows.map((r) => ({ | ||||||||||||||||||||||||||
| id: String(r.id), | ||||||||||||||||||||||||||
|
|
@@ -592,6 +601,7 @@ function classifyNodeRolesFull(db: BetterSqlite3Database, emptySummary: RoleSumm | |||||||||||||||||||||||||
| fanOut: r.fan_out, | ||||||||||||||||||||||||||
| isExported: exportedIds.has(r.id), | ||||||||||||||||||||||||||
| productionFanIn: prodFanInMap.get(r.id) || 0, | ||||||||||||||||||||||||||
| hasActiveFileSiblings: r.kind === 'constant' ? activeFiles.has(r.file) : undefined, | ||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||
|
Comment on lines
594
to
605
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is harmless today, but it makes the intent of the flag harder to reason about and could mislead a future reader into thinking the flag affects non-constant classification. Consider scoping the assignment so the flag is only passed when actually meaningful:
Suggested change
The same applies to the incremental path (lines 740–750). Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed — scoped the assignment with a ternary: |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const roleMap = classifyRoles(classifierInput); | ||||||||||||||||||||||||||
|
|
@@ -720,6 +730,13 @@ function classifyNodeRolesIncremental( | |||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // 5. Classify affected nodes using global medians | ||||||||||||||||||||||||||
| const activeFiles = new Set<string>(); | ||||||||||||||||||||||||||
| for (const r of rows) { | ||||||||||||||||||||||||||
| if ((r.fan_in > 0 || r.fan_out > 0) && r.kind !== 'constant') { | ||||||||||||||||||||||||||
| activeFiles.add(r.file); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const classifierInput = rows.map((r) => ({ | ||||||||||||||||||||||||||
| id: String(r.id), | ||||||||||||||||||||||||||
| name: r.name, | ||||||||||||||||||||||||||
|
|
@@ -729,6 +746,7 @@ function classifyNodeRolesIncremental( | |||||||||||||||||||||||||
| fanOut: r.fan_out, | ||||||||||||||||||||||||||
| isExported: exportedIds.has(r.id), | ||||||||||||||||||||||||||
| productionFanIn: prodFanInMap.get(r.id) || 0, | ||||||||||||||||||||||||||
| hasActiveFileSiblings: r.kind === 'constant' ? activeFiles.has(r.file) : undefined, | ||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const roleMap = classifyRoles(classifierInput, globalMedians); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fan_out > 0criterion misses pure-sink siblingsThe
activeFilesheuristic only rescues constants whose file contains at least one callable with outbound calls. It does not rescue constants in files where all sibling functions are pure sinks (i.e.fan_in > 0butfan_out === 0).Concrete example:
Here
validatehasfan_out === 0, sovalidators.tsis never added toactiveFiles, andMAX_LENGTHremainsdead-leafeven though it is consumed.Widening the criterion to
(r.fan_in > 0 || r.fan_out > 0) && r.kind !== 'constant'would cover this common pattern without meaningfully increasing false-negative rate — any callable that is actually connected to the graph (either called or calling) is a good proxy for an "active" file.The same change applies to the symmetric block in
classifyNodeRolesIncremental(lines 733–737).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Widened the criterion to
(r.fan_in > 0 || r.fan_out > 0) && r.kind !== 'constant'in both the full and incremental paths, so pure-sink siblings (likevalidatewithfan_in=10, fan_out=0) now correctly mark their file as active. Added a dedicated test for this pattern. Fixed in f91fde0.