Skip to content

fix: eliminate dead export false positives#10

Merged
bntvllnt merged 3 commits intomainfrom
fix/dead-export-false-positives
Mar 10, 2026
Merged

fix: eliminate dead export false positives#10
bntvllnt merged 3 commits intomainfrom
fix/dead-export-false-positives

Conversation

@bntvllnt
Copy link
Owner

Summary

Fixes #6find_dead_exports had ~33% false positive rate (2/6). Two root causes:

  • Duplicate imports dropped: second import to same target silently skipped due to graphology's single-edge constraint, losing symbols (e.g., import type {SearchIndex} after import {createSearchIndex, ...})
  • Same-file calls invisible: parser excluded intra-file calls, analyzer only checked import edges — so registerTools() called within startMcpServer() was flagged dead

Changes

File Change
src/graph/index.ts Merge duplicate edge symbols into existing edge (both graphology attrs + edges[] array)
src/parser/index.ts Remove declRelPath !== callerFile guard — include same-file calls in callSites
src/analyzer/index.ts Integrate call graph edges into consumed symbols check + class method normalization (ClassName.methodClassName)
src/analyzer/index.test.ts 7 regression tests

Semantics change

  • Old: dead = "no external file imports this symbol"
  • New: dead = "no import edges AND no call edges reference this symbol (including same-file)"

Verification

Self-analysis after fix:

  • SearchIndex — NOT flagged dead (was false positive)
  • registerTools — NOT flagged dead (was false positive)
  • tokenize — NOT flagged dead (same-file call by createSearchIndex)
  • detectEntryPoints — NOT flagged dead (same-file call by traceProcesses)

Test plan

  • 7 new regression tests (truly dead, type-only imports, duplicate merge, same-file calls, class methods, mixed dead/alive, edge merge sync)
  • All 185 tests pass (178 existing + 7 new)
  • Lint, typecheck, build all green
  • Self-analysis: 0 false positives on known cases

Two bugs caused ~33% false positive rate in find_dead_exports:

1. Duplicate imports to same target silently dropped — second import's
   symbols never entered consumed set (e.g., `import type {X}` after
   `import {A,B,C}` from same file)

2. Same-file calls invisible — parser excluded intra-file calls,
   analyzer only checked import edges

Fixes:
- Graph: merge duplicate edge symbols (both graphology + edges[] array)
- Parser: remove same-file call exclusion guard
- Analyzer: integrate call graph into consumed symbols check with
  class method normalization (ClassName.method → ClassName)

7 regression tests covering: truly dead exports, type-only imports,
duplicate import merge, same-file calls, class methods, mixed
dead/alive, edge merge sync.
@bntvllnt bntvllnt self-assigned this Mar 10, 2026
- graph: replace silent `if (existing)` guard with invariant assertion
- graph: extract merge values as consts for order-safe mutation
- test: assert `main` is dead in same-file call test (negative case)
- test: add call-graph-only class test (no import edge)
- test: verify all graphology attrs in edge merge sync test
@bntvllnt bntvllnt merged commit a840ef6 into main Mar 10, 2026
1 check passed
@bntvllnt bntvllnt deleted the fix/dead-export-false-positives branch March 10, 2026 23:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: dead export detection false positives (type imports + same-file calls)

1 participant