mirror of
https://github.com/safishamsi/graphify.git
synced 2026-08-28 09:17:02 +00:00
fix(extract): emit Java generic parent relationships (#1510)
A generic parent — `class Foo extends Bar<T>` or `implements List<T>` — is a `generic_type` node in the tree, but the inheritance extractor only fired on a bare `type_identifier`, so those inherits/implements edges were silently dropped. The parent type is now unwrapped to its base (`Bar<T>` -> `Bar`) for the inherits/implements edge, and the type arguments are emitted as `generic_arg` references. Ported from PR #1511 by @oleksii-tumanov. Known pre-existing limitation (not introduced here, worth a separate follow-up): the extractor has no type-parameter tracking, so a bare parameter like `T` in `extends Container<T>` still produces a `generic_arg` reference to `T`; the inherits/implements edge itself always targets the real base type, never `T`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
safishamsi
co-authored by
Claude Opus 4.8
parent
940cb53f4d
commit
1f3f1c1ca6
+20
-6
@@ -2780,11 +2780,25 @@ def _extract_generic(
|
||||
seen_ids.add(base_nid)
|
||||
add_edge(class_nid, base_nid, rel, at_line)
|
||||
|
||||
def _emit_java_parent_type(type_node, rel: str, at_line: int) -> None:
|
||||
refs: list[tuple[str, str]] = []
|
||||
_java_collect_type_refs(type_node, source, False, refs)
|
||||
parent_emitted = False
|
||||
for ref_name, role in refs:
|
||||
if role == "type" and not parent_emitted:
|
||||
_emit_java_parent(ref_name, rel, at_line)
|
||||
parent_emitted = True
|
||||
elif role == "generic_arg":
|
||||
target_nid = ensure_named_node(ref_name, at_line)
|
||||
if target_nid != class_nid:
|
||||
add_edge(class_nid, target_nid, "references", at_line,
|
||||
context="generic_arg")
|
||||
|
||||
sup = node.child_by_field_name("superclass")
|
||||
if sup is not None:
|
||||
for sub in sup.children:
|
||||
if sub.type == "type_identifier":
|
||||
_emit_java_parent(_read_text(sub, source), "inherits", line)
|
||||
if sub.is_named:
|
||||
_emit_java_parent_type(sub, "inherits", line)
|
||||
break
|
||||
|
||||
ifs = node.child_by_field_name("interfaces")
|
||||
@@ -2792,8 +2806,8 @@ def _extract_generic(
|
||||
for sub in ifs.children:
|
||||
if sub.type == "type_list":
|
||||
for tid in sub.children:
|
||||
if tid.type == "type_identifier":
|
||||
_emit_java_parent(_read_text(tid, source), "implements", line)
|
||||
if tid.is_named:
|
||||
_emit_java_parent_type(tid, "implements", line)
|
||||
|
||||
if t == "interface_declaration":
|
||||
for child in node.children:
|
||||
@@ -2801,8 +2815,8 @@ def _extract_generic(
|
||||
for sub in child.children:
|
||||
if sub.type == "type_list":
|
||||
for tid in sub.children:
|
||||
if tid.type == "type_identifier":
|
||||
_emit_java_parent(_read_text(tid, source), "inherits", line)
|
||||
if tid.is_named:
|
||||
_emit_java_parent_type(tid, "inherits", line)
|
||||
|
||||
for anno_name in _java_annotation_names(node, source):
|
||||
target_nid = ensure_named_node(anno_name, line)
|
||||
|
||||
@@ -357,6 +357,28 @@ def test_java_normalizes_inherits_and_implements():
|
||||
assert ("DataProcessor", "Processor") in _edge_labels(result, "implements")
|
||||
|
||||
|
||||
def test_java_generic_parents_include_type_argument_references(tmp_path):
|
||||
source = tmp_path / "GenericParents.java"
|
||||
source.write_text(
|
||||
"class Dependency {}\n"
|
||||
"interface Event {}\n"
|
||||
"class Base<T> {}\n"
|
||||
"interface Handler<T> {}\n"
|
||||
"interface DerivedHandler extends Handler<Event> {}\n"
|
||||
"class Service extends Base<Dependency> implements Handler<Event> {}\n"
|
||||
)
|
||||
|
||||
result = extract_java(source)
|
||||
|
||||
assert ("Service", "Base") in _edge_labels(result, "inherits")
|
||||
assert ("Service", "Handler") in _edge_labels(result, "implements")
|
||||
refs = _edge_labels(result, "references", "generic_arg")
|
||||
assert ("Service", "Dependency") in refs
|
||||
assert ("Service", "Event") in refs
|
||||
assert ("DerivedHandler", "Handler") in _edge_labels(result, "inherits")
|
||||
assert ("DerivedHandler", "Event") in refs
|
||||
|
||||
|
||||
def test_java_parameter_return_generic_and_attribute_contexts():
|
||||
result = extract_java(FIXTURES / "sample.java")
|
||||
assert ("build", "HttpClient") in _edge_labels(result, "references", "parameter_type")
|
||||
|
||||
Reference in New Issue
Block a user