fix(analysis): make dead-code detection correct and self-consistent - #1578
Merged
Merged
Conversation
…600, #1559) Two implementations existed and each got something the other got wrong, so the same repository gave different answers depending which you asked. Module-level JS calls were reported as dead (#600) `find_dead_code` counted only `(caller:Function)` callers. A call at JS module level is not inside a function, so the writer persists it as a File-sourced edge — writer.py has an explicit `caller_label == "File"` branch producing `(:File)-[:CALLS]->(:Function)`. Those edges exist and were simply not counted, so any JS function called only at module level came back dead. Python escapes this because its parser synthesizes a `<module>` frame; javascript.py has no equivalent. The caller is now anonymous, matching what the report generator already did correctly. The entry-point filter hid real dead code (#1559) `NOT func.name CONTAINS 'main'` also excluded `domain_check`, `remainder` and `maintain_index`; the same applied to the 'application', 'entry' and 'entrypoint' substring tests. Genuinely unused functions were never analyzed and nothing said so — the opposite failure from the false positives #1332 is about. Now matched on the whole name. `analyze dead-code` ignored its path argument (#1559) The argument was accepted, labelled "(not yet implemented)", and dropped, so running inside one repository reported dead code from every repository in the database. Now forwarded as the repo scope. The report generator disagreed with the CLI (#1559) Its query had no name exclusions at all, so every dunder and `test_*` appeared as dead in CGC_REPORT.md. Both now share `_ENTRY_POINT_NAMES` so they cannot drift apart again. The CLI help claimed classes were analyzed (#1559) The query matches `(func:Function)` only. Corrected the help rather than implement class-level dead code, which needs its own design. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ |
Contributor
🔍 PR Code Graph Analysisfix(analysis): make dead-code detection correct and self-consistent (#1578) 📊 Interactive VisualizationView the blast radius graph: PR Reviewer Dashboard 📦 ArtifactsThe graph JSON has been uploaded as a build artifact: Generated by CodeGraphContext using FalkorDB Lite |
Shashankss1205
added a commit
that referenced
this pull request
Aug 8, 2026
Ten PRs since 0.5.6 — mostly parser correctness, all with regression tests. Parsers - Ruby nested classes no longer delete their parent; method parameters extracted at all (#1523, #1527, #1581) - C/C++ functions returning a pointer or reference are extracted, and registered in pre_scan so they resolve as call targets (#1524, #1582) - C# fields and locals parsed, restoring receiver-type inference for `var x = new T(); x.M();` (#1525, #1584) - JavaScript destructured, array and rest parameters extracted, recording the binding names rather than a placeholder (#1527, #1591) Queries and analysis - Five defects that returned wrong answers without erroring: inflated repository stats, misattributed callers, discarded graph_name, count(*) after OPTIONAL MATCH, an ignored tool argument (#1577) - Dead-code detection made correct and self-consistent, including the JS module-level false positive (#600, #1559, #1578) Indexing - add_package_to_graph on a flat module no longer indexes the whole virtualenv (#1528, #1586) - SCIP job progress no longer exceeds 100% (#1535, #1583) Docs and tests - Database Options table columns realigned (#1590) - MCP tool definition contract validation (#1580) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Fixes #600 and #1559.
Full suite: 1101 passed, 19 skipped, no failures.
Dead-code detection had two implementations —
CodeFinder.find_dead_code(CLI + MCP) andreport_generator._section_dead_code(CGC_REPORT.md) — and each got something the other got wrong. The same repository produced different answers depending on which surface you asked.#600 — module-level JS calls reported as dead
This one had sat since March with only a screenshot. The root cause:
A call at JS module level is not inside a function, so the writer persists it as a File-sourced edge —
writer.pyhas an explicitcaller_label == "File"branch producing(:File)-[:CALLS]->(:Function). Those edges were in the graph and simply not counted, socaller_count = 0and the function was reported dead.Python escapes this because its parser synthesizes a
<module>frame (languages/python.py:211);javascript.pyhas no equivalent, which is why the bug is JS-specific.The report generator already had this right with an anonymous caller. Now both do.
#1559 — four defects
① The entry-point filter hid real dead code.
NOT func.name CONTAINS 'main'also excludeddomain_check,remainder,maintain_index; same for theapplication/entry/entrypointsubstring tests. Genuinely unused functions were never analyzed, with nothing to indicate it — the opposite failure from the false positives #1332 tracks. Now matched on the whole name via a shared_ENTRY_POINT_NAMEStuple.② The CLI claimed classes were analyzed. The query matches
(func:Function)only. I corrected the help text rather than implement class-level dead code — that needs its own design (a class with no incoming edges is a different question from an uncalled function).③
analyze dead-codeignored its path argument. It was accepted, labelled"(not yet implemented)", and dropped — so running inside one repository reported dead code from every repository in the database. Now forwarded as the repo scope.④ The two implementations disagreed. The report query had no name exclusions, so every dunder and
test_*showed up as dead inCGC_REPORT.mdwhile the CLI filtered them out. Both now share the same exclusion list and the same caller shape.Tests
tests/unit/tools/test_dead_code_analysis.py— 8 tests, one per defect, each naming the failure it prevents. Reverting the source changes breaks collection, since the shared constant the report generator imports is itself part of the fix.Not fixed here
#1332 (confidence scoring / ranking) is the UX layer on top of this and stays open. What this PR does is make the underlying set correct first — scoring a set that both hides real findings and disagrees with itself would have been building on sand.
🤖 Generated with Claude Code