You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
--emit neo4j: J_CALLS materializes only ~14% of the call graph (edges absent though both endpoint nodes exist) #158
Is your feature request related to a problem? Please describe.
--emit neo4j materializes only a small fraction of the call graph: on daytrader8 the emitted graph has 287 J_CALLS edges, while the analysis.jsoncall_graph from an equivalent level-2 run has ~2077 edges. Edges are missing even when both endpoint :JCallable nodes exist in the graph, so it is not (only) the external-target gating.
Any consumer reading the call graph from Neo4j (e.g. the CLDK SDK's read-only Java backend) sees ~14% of the call edges the in-memory / analysis.json backend reports — callers/callees, class call graphs, and reachability are all substantially incomplete.
Describe the solution you'd like
Not stated in the original issue.
Describe alternatives you've considered
Not stated in the original issue.
Additional context
Environment
codeanalyzer-java 2.4.0 (--analysis-level 2, live Bolt push)
Evidence
The call graph is deterministic: two --emit json runs gave 2077 and 2076 call_graph edges.
The same project --emit neo4j produced 287J_CALLS edges.
Of 1855 fully-resolved edges in analysis.json (both endpoints are app callables present in the symbol table), only ~287 unique have a J_CALLS edge in the graph; ~1523 are absent despite both endpoint :JCallable nodes existing:
# for an absent edge, both nodes resolve:MATCH (a:JCallable {id:$sid}) RETURNa//existsMATCH (b:JCallable {id:$tid}) RETURNb//existsMATCH (:JCallable {id:$sid})-[r:J_CALLS]->(:JCallable {id:$tid}) RETURNcount(r) //0
Example absent edges (both endpoints present as nodes): ...web.websocket.ActionMessage#... -> ...util.Log#error(...), ...web.prims.PingWebSocketJson#... -> ...web.websocket.JsonMessage#....
Likely area
neo4j/GraphProjector.java, projectCallGraph → b.edgeIfBothResolved("J_CALLS", new NodeRef("JSymbol","id",from), new NodeRef("JSymbol","id",to), props) with from/to = type_declaration + "#" + signature. Since both endpoint nodes exist with exactly those ids, edgeIfBothResolved should retain them — so either the resolution key used during projection differs from the node id actually emitted (a normalization mismatch between the call_graph vertices' type_declaration/signature and the symbol-table ids), or the --emit neo4j path is projecting a much smaller call graph than --emit json. Worth confirming whether the two emit paths build the SDG identically.
Verified fixed in v2.4.1 (PR #159). Rebuilt the fatJar at the v2.4.1 tag and re-ran on daytrader8 (level 2): J_CALLS went from 287 → 1702 edges. The vertexId fix (keying off callable_declaration instead of the call-graph signature, which rewrote <init>/<clinit>) resolves the gating. The small residual vs the analysis.json call graph (~1761) is run-to-run WALA variance between the separate --emit json and --emit neo4j invocations, not the projection. Good to close.
Activity
Merge pull request #159 from codellm-devkit/fix/issues-156-157-158
rahlk commentedon Jun 23, 2026
Verified fixed in v2.4.1 (PR #159). Rebuilt the
fatJarat thev2.4.1tag and re-ran on daytrader8 (level 2):J_CALLSwent from 287 → 1702 edges. ThevertexIdfix (keying offcallable_declarationinstead of the call-graphsignature, which rewrote<init>/<clinit>) resolves the gating. The small residual vs theanalysis.jsoncall graph (~1761) is run-to-run WALA variance between the separate--emit jsonand--emit neo4jinvocations, not the projection. Good to close.