Skip to content

fix(jvm): resolve mixed Java/Kotlin callgraph edges#684

Closed
nguyentamdat wants to merge 1 commit into
DeusData:mainfrom
nguyentamdat:java-kotlin-callgraph
Closed

fix(jvm): resolve mixed Java/Kotlin callgraph edges#684
nguyentamdat wants to merge 1 commit into
DeusData:mainfrom
nguyentamdat:java-kotlin-callgraph

Conversation

@nguyentamdat

@nguyentamdat nguyentamdat commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • infer JVM namespaces from src/main/java, src/main/kotlin, src/test/java, and src/test/kotlin roots for Java/Kotlin files
  • preserve inferred namespace metadata in LSP definition cache entries so same-package JVM definitions survive cross-file filtering
  • compare textual callees by leaf method name when applying LSP overrides, including the parallel lsp_idx path
  • add a mixed Java/Kotlin regression that asserts strategy: lsp edges for Java -> Kotlin and Kotlin -> Java calls

Verification

  • make -f Makefile.cbm test -> 5726 passed

Notes

Split out from the original bundled branch so this PR now contains only the JVM callgraph fix. The low-RAM indexing change is now separate in #685.

Before: Java/Kotlin cross-file LSP emitted package-shaped JVM names while graph nodes stayed path-derived, and member calls like receiver.method did not consume LSP overrides.

After: Java and Kotlin share package-indexed JVM defs across mixed Gradle/Maven source roots, normalize JVM defs to declared packages for LSP registries, consume qualified member-call overrides by leaf name, and assert both Java-to-Kotlin and Kotlin-to-Java lsp CALLS edges.

Why: /home/dat/ica/server mixes src/main/java and src/main/kotlin in the same JVM packages; path-prefix filtering missed same-package defs and fallback suffix matching produced weaker or wrong call edges.

Verify: make -f Makefile.cbm test -> 5705 passed.
Signed-off-by: nguyentamdat <nguyentamdat@gmail.com>
@nguyentamdat nguyentamdat force-pushed the java-kotlin-callgraph branch from e6f8c2a to f33f068 Compare June 29, 2026 07:01
@DeusData

Copy link
Copy Markdown
Owner

Huge thanks for opening this PR and for the work you put into it.

The maintainer shop is currently full, so this may sit for a bit before it gets a proper review. We will come back to this as soon as possible with real feedback; I wanted to make sure it did not sit unacknowledged in the meantime.

@DeusData DeusData added bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency. labels Jun 29, 2026
@DeusData

DeusData commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Thank you — the root cause is real and the fix uses the correct convention: the declared package is JVM ground truth, and mixed src/main/java + src/main/kotlin roots genuinely can't be joined by path-derived module QNs. Your red-first test (asserting strategy:lsp both directions AND absence of the weaker suffix strategy) is exactly how we like guards written, and splitting the low-RAM change into #685 was appreciated. One question before merge, not a blocker: the cbm_pipeline_lsp_target_node tail-match fallback applies to all languages, not just JVM — ambiguity returns NULL and the full suite is green, so the residual risk is a single wrong-module tail coincidence in a non-JVM language. Would you gate it to JVM languages, or do you have a case where the global fallback recovers real edges elsewhere? Happy either way with a one-line rationale.


Update: to get this into the next release, if we don't hear back on the gating question in the next couple of days we'll default to gating the tail-match fallback to JVM languages in a small follow-up on top of your branch and merge — with your commits landing as-is and full credit. If you have a case where the global fallback recovers real edges, just say so and we'll merge it ungated instead.

@DeusData

DeusData commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Thank you — the root cause was real and your fix used the correct convention: the declared package is JVM ground truth, and your red-first test (asserting strategy:lsp both directions AND the absence of the weaker suffix strategy) is exactly how we like guards written. We carried it over the line with one addition per the review thread: the new tail-match fallbacks are now gated to JVM languages (rationale: unique Class.method leaf matching is safe under JVM class-per-file package semantics; elsewhere one wrong-module coincidence would fabricate an edge), with a guard test for the gate itself. Merged as 5f6461e (PR #818) with you credited as co-author. Splitting the low-RAM change into #685 earlier was appreciated — that one stays open on its own merits. Thanks again!

@DeusData DeusData closed this Jul 4, 2026
vvenegasv pushed a commit to vvenegasv/codebase-memory-mcp that referenced this pull request Jul 4, 2026
…ages

Mixed Gradle/Maven source roots (src/main/java + src/main/kotlin) share
JVM packages that path-derived module QNs cannot join: cross-file LSP
emitted package-shaped names while graph nodes stayed path-derived, and
same-package defs across roots were filtered out, so Java<->Kotlin calls
fell back to weak or wrong textual edges.

The declared package is JVM ground truth. Normalize JVM defs to their
declared package (inferring it from conventional source roots only when
no package was extracted), index defs by declared namespace alongside
the path module so same-package defs survive per-file filtering, restrict
JVM callers to JVM defs, register cross-def return-type signatures for
Kotlin receiver typing, and consume qualified member-call overrides by
leaf name in the parallel lsp_idx fast path.

Distilled from DeusData#684 with one design change: the new unique-Class.method
tail-match fallbacks in lsp_resolve.h are gated to JVM callers
(cbm_pipeline_lsp_allow_tail_match, Java/Kotlin only). Tail-matching by
unique leaf is safe where class-per-file package semantics hold; in
other languages a single wrong-module coincidence would fabricate a
CALLS edge. The leaf-key lsp_idx lookup stays global: it aligns the
fast path with the build-side key that already used leaf names.

Tests: carried mixed Java/Kotlin regression (red on main, green here)
plus a gate guard asserting the tail fallbacks stay off for non-JVM
callers and on for JVM.

Co-authored-by: nguyentamdat <nguyentamdat@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@nguyentamdat

Copy link
Copy Markdown
Contributor Author

Thanks for carrying this over the line and for preserving the credit. JVM-only gating for the tail-match fallback makes sense to me: the fallback was intended to recover declared-package/class-method drift in Java/Kotlin, and I don’t have a concrete non-JVM case that needs the global behavior. Appreciate the clear rationale and the extra guard test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants