Repository navigation
follow-up: receiver-type-aware invoked-property matching to reduce dead-code false negatives #2088
Description
Activity
- addedfollow-upDeferred work from PR reviews that needs trackingDeferred work from PR reviews that needs tracking
on Jul 11, 2026 Investigated as part of a
/fixerpass.Confirmed the issue's characterization is still accurate against current
main(post-#2087):collectInvokedPropertyNames(src/domain/graph/builder/call-resolver.ts) and its Rust mirrorcollect_invoked_property_names(crates/codegraph-core/src/domain/graph/builder/stages/build_edges.rs) still add a property name to the evidence set whenever ANY call anywhere in scope has a non-emptyreceiver, with zero correlation between that receiver's inferred type and the specific object-literal/dispatch-table variable the property check is actually being evaluated against.Why this isn't implemented in this pass
This is a genuine dataflow-precision feature, not a bug fix or plumbing change — and it's asymmetric with the other #2034-deferred follow-ups (#2086, #2087) in an important way: those were correctness bugs (the registry produced actively wrong — false-positive — call edges). This one is the opposite: the current behavior is conservative (a false negative for dead-code detection; it can never misclassify genuinely live code as dead), so nothing downstream is producing incorrect results today. It's a recall improvement to an advisory command (
roles --role dead), not a soundness bug in the graph itself.Actually closing the gap requires real algorithm design, not a wiring fix:
- Extending the points-to/type-map machinery to track, per call site, which object-literal/dispatch-table instance a receiver plausibly refers to (not just its nominal class/type name — a bare
{ resolve: fn }returned from a factory has no name to match against, so this needs value-identity tracking through the existing points-to solver, or a new mechanism). - Correlating that instance-level binding back to the specific literal declaring the property under test.
- The usual dual-engine parity burden: identical semantics in both the WASM/TS points-to solver and the native Rust one, each independently verified.
I looked for a narrower, bounded partial fix (e.g. an exclusion list for common non-dispatch-table receiver names like
promise/logger) and decided against it — that trades one unprincipled heuristic for another (a hardcoded name list needing perpetual tuning, exactly the kind of ad-hoc special-casing this repo's conventions steer away from) without actually implementing the receiver-type correlation the issue asks for.No change made. Leaving open — the fix direction in the issue body remains accurate and actionable for whoever picks this up with room to design the points-to extension properly.
- Extending the points-to/type-map machinery to track, per call site, which object-literal/dispatch-table instance a receiver plausibly refers to (not just its nominal class/type name — a bare
- addedblockedCannot be worked right now (excluded from /fixer's queue)Cannot be worked right now (excluded from /fixer's queue)
on Aug 7, 2026 Applying the
blockedlabel so this issue stops consuming a /fixer batch slot on every future pass while it remains what it was diagnosed as above: a genuine points-to/dataflow algorithm-design project, not a bounded bug fix or plumbing change. Remove the label whenever someone is ready to design the receiver-instance correlation extension described above.- removedblockedCannot be worked right now (excluded from /fixer's queue)Cannot be worked right now (excluded from /fixer's queue)
on Aug 23, 2026 claiming issue-2088 (plan, via /oversee) — docs/plan-issue-2088
- added 9 commits that reference this issue
on Aug 24, 2026 claiming issue-2088 (execute, via /oversee — approved plan #2612) — feat/2088-correlated-invoked-property-evidence
execute PR opened: #2654 (feat/2088-correlated-invoked-property-evidence). Not merging — human merge only.
- added a commit that references this issue
on Sep 6, 2026
Deferred from PR #2034 review.
Original reviewer comment: inline comment — #2034 (comment)
Context:
collectInvokedPropertyNames(src/domain/graph/builder/call-resolver.ts, mirrored ascollect_invoked_property_namesincrates/codegraph-core/src/domain/graph/builder/stages/build_edges.rs) adds a property name to its evidence set whenever ANY call in the processed files has a non-emptyreceiverand thatname— with no check that the receiver is plausibly the same dispatch-table object referenced by the object-literal being evaluated. A completely unrelatedlogger.resolve()orpromise.resolve()call anywhere in the codebase will addresolveto the set and prevent{ resolve: neverCalled }from being flagged dead, even if the actual dispatch table is never read.This is the conservative error direction (false negative for dead-code — it can never misclassify genuinely live code as dead), so it was accepted as-is for #2034: that PR's stated scope was "require any invocation evidence" as a deliberately coarse "one hop further" heuristic (see the doc comment on
collectInvokedPropertyNames), not precise receiver-type correlation. Tightening it now would require:That's a nontrivial dataflow precision improvement, not a small tweak, so it's being tracked separately rather than folded into #2034's narrower "any evidence at all" fix.
What the fix entails: teach
collectInvokedPropertyNames/resolveFallbackTargets(and their Rust mirrors) to only count ax.name(...)call as evidence for a given object-literal's{ name: fn }property whenx's resolved type/binding plausibly refers to that same object (e.g. via the existingtypeMap/points-to infrastructure), rather than matching on bare property name alone.