fix(extract): Java extends/implements + resolve cross-file imports#482
Closed
Goodday12 wants to merge 1 commit into
Closed
fix(extract): Java extends/implements + resolve cross-file imports#482Goodday12 wants to merge 1 commit into
Goodday12 wants to merge 1 commit into
Conversation
Two issues with Java extraction that silently dropped all Java-type edges:
1. Inheritance relations (extends / implements) were never extracted. The
class handler in `_extract_generic` had language-specific blocks for
Python (inherits), Swift (inherits), and C# (inherits) but nothing
for Java, even though tree-sitter-java exposes the `superclass`,
`interfaces`, and `extends_interfaces` fields.
2. Java imports had dangling targets. `_import_java` emitted edges whose
target was just the class name (`_make_id(module_name)` e.g. `list`),
while real node ids use `{stem}_{classname}`. Those edges failed to
match any node in `build_from_json` and were silently discarded,
leaving large Java corpora with effectively zero type-level edges.
## Changes
- `extract.py`: new `tree_sitter_java` block in the class handler emits
`extends` for `class X extends Y`, `implements` for
`class X implements A, B`, and `extends` for interface-to-interface
inheritance (`interface X extends A, B`). When the parent is defined
outside the current file, a synthetic external node is added (same
pattern as the existing Python, Swift, and C# blocks).
- `extract.py`: new `_resolve_cross_file_java_imports` helper runs a
second pass on Java corpora and rewrites file-level imports into
resolved edges pointing at real class nodes elsewhere in the corpus.
Wildcard imports (`a.b.*`) are skipped; stdlib / third-party names
left unresolved produce no edge (they would be dropped anyway).
- `extract()`: calls the Java resolver after the existing Python one.
## Tests
- `test_languages.py::test_java_finds_extends`
- `test_languages.py::test_java_finds_implements`
- `test_languages.py::test_java_finds_interface_extends`
- `test_multilang.py::test_extract_resolves_java_cross_file_imports`
New fixtures `sample_inherit.java` and `sample_imported.java` exercise
single-inheritance, multiple-interface implementation, interface-extends-
interface, and cross-file import resolution.
## Verified on a real Java corpus
On a private Spring / JPA / Lombok codebase of 5,727 .java files:
before this patch: `extends` = 0, `implements` = 0, `imports` = 0
(all dropped silently at build time)
after this patch: `extends` = 2,072, `implements` = 1,285,
`imports` = 58,019 resolved internally
Full upstream test suite (437 tests) passes with no regressions.
WHALE88
approved these changes
Apr 22, 2026
jaygandhi129
approved these changes
Apr 22, 2026
Collaborator
|
Implemented in v0.4.31 — Java |
safishamsi
added a commit
that referenced
this pull request
Apr 23, 2026
matzls
pushed a commit
to matzls/graphify
that referenced
this pull request
May 10, 2026
…ify-Labs#490: legacy schema canonicalization, Java inheritance, aggregated HTML viz, check-update subcommand
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.
Closes #435 (Java inheritance support). Also fixes a related Java-imports bug that would have blocked #435 from actually working end-to-end.
Summary
Two issues in Java AST extraction that silently drop all Java-type edges from the graph. On any real Java corpus, the
structural_typelayer (imports / extends / implements) comes out empty.Issue 1 — Java inheritance was never extracted
The class handler inside
_extract_generichas language-specific blocks for Python (inherits), Swift (inherits), and C# (inherits) — but nothing for Java, even thoughtree-sitter-javaexposes thesuperclass/interfaces/extends_interfacesfields.Result: for a Java corpus of 5,727 files,
extends= 0 andimplements= 0.Issue 2 — Java import edges had dangling targets
_import_javaemits edges whosetargetis just the bare class name (_make_id(module_name)→ e.g.listforjava.util.List), while real node ids use{stem}_{classname}. Those edges never match a node id, sobuild_from_jsonsilently drops every single one at line 75-76 ofbuild.py.Result:
imports= 0 in the built graph, even though_import_javaran.What this PR changes
graphify/extract.pyNew
tree_sitter_javablock in the class handler (alongside the existing Python / Swift / C# blocks). Emits:extendsedge forclass X extends Y(vianode.child_by_field_name(\"superclass\"))implementsedges forclass X implements A, B(vianode.child_by_field_name(\"interfaces\") → super_interfaces → type_list)extendsedges forinterface X extends A, B(via theextends_interfaceschild)New
_resolve_cross_file_java_importshelper mirroring the existing_resolve_cross_file_imports(Python). Runs a second pass on Java corpora, builds a{ClassName: [node_id, …]}index across all nodes, re-parses each file's imports, and emits resolvedimportsedges pointing at real class nodes elsewhere in the corpus.a.b.*) are skipped — the target is ambiguous.staticimports strip thestatickeyword and resolve the class name (not the method).extract()now calls the Java resolver after the existing Python one, in the same try/except style so an import failure intree_sitter_javaonly logs a warning.Tests
All four new tests pass locally; the full upstream suite (437 tests) passes with no regressions.
tests/test_languages.py::test_java_finds_extendstests/test_languages.py::test_java_finds_implementstests/test_languages.py::test_java_finds_interface_extendstests/test_multilang.py::test_extract_resolves_java_cross_file_importsTwo new fixtures:
sample_inherit.java(single inheritance + multi-interface + interface-extends-interface) andsample_imported.java(defines a class imported cross-file).Validated on a real Java corpus
Spring / JPA / Lombok monorepo of 5,727
.javafiles parsed with no errors:extendsimplementsimports(internal)Before the fix, the entire
structural_typelayer of this corpus had 11 edges (all from README.md via the LLM pass, none from AST). After the fix it has 61,376 real AST edges.Test plan
graphify/extract.pypytest tests/— 437 passed, 0 failedNotes
_import_javais left in place. The edges it produces still reachbuild_from_jsonand still get dropped for stdlib / third-party imports — that is the expected behaviour for dangling externals. The new cross-file pass now re-resolves the internal ones correctly.extends/implementsare used as distinct relation names (rather than theinheritsused by Python / Swift / C#) because Java separates the two cleanly and the distinction is useful downstream. If you'd prefer to unify underinherits, happy to adjust.