Skip to content

Commit 64a6093

Browse files
hibin-mclaude
authored andcommitted
fix(groovy): emit inherits/implements edges for extends/implements
tree-sitter-groovy exposes inheritance via the same `superclass` and `interfaces`/`type_list` fields as tree-sitter-java, but the inheritance- emitting block in `_extract_generic` was gated on `ts_module == "tree_sitter_java"`. Groovy was the only class-based JVM language in the file with no inheritance handler, so every Groovy `extends`/`implements` was silently dropped (contains/methods/imports/calls were unaffected). Widen the gate to include `tree_sitter_groovy`; the existing `_emit_java_parent_type` path handles the identical node shapes verbatim. Adds a base class + interface to the Groovy fixture and two regression tests (extends -> inherits, implements -> implements). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a19b9e9 commit 64a6093

3 files changed

Lines changed: 44 additions & 1 deletion

File tree

graphify/extract.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3513,7 +3513,7 @@ def _php_emit_base(base_name: str, rel: str, at_line: int) -> None:
35133513
context="generic_arg", metadata=metadata)
35143514

35153515
# Java-specific: extends (superclass) / implements (interfaces) / interface-extends
3516-
if config.ts_module == "tree_sitter_java":
3516+
if config.ts_module in ("tree_sitter_java", "tree_sitter_groovy"):
35173517
def _emit_java_parent(base_name: str, rel: str, at_line: int) -> None:
35183518
if not base_name:
35193519
return

tests/fixtures/sample.groovy

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,17 @@ class SampleService {
1919
processor.reset()
2020
}
2121
}
22+
23+
interface Resettable {
24+
void reset()
25+
}
26+
27+
class ExtendedService extends SampleService implements Resettable {
28+
ExtendedService(Processor processor) {
29+
super(processor)
30+
}
31+
32+
void reset() {
33+
// no-op
34+
}
35+
}

tests/test_languages.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2195,6 +2195,35 @@ def test_groovy_no_dangling_edges():
21952195
assert e["source"] in node_ids
21962196

21972197

2198+
def test_groovy_extends_edge():
2199+
"""`class X extends Base` must emit an inherits edge.
2200+
2201+
tree-sitter-groovy exposes inheritance via the same `superclass` field as
2202+
tree-sitter-java, but the inheritance handler was gated to Java only, so
2203+
Groovy extends/implements were silently dropped.
2204+
"""
2205+
r = extract_groovy(FIXTURES / "sample.groovy")
2206+
node_by_id = {n["id"]: n["label"] for n in r["nodes"]}
2207+
found = any(
2208+
"ExtendedService" in node_by_id.get(e["source"], "")
2209+
and "SampleService" in node_by_id.get(e["target"], "")
2210+
for e in r["edges"] if e["relation"] == "inherits"
2211+
)
2212+
assert found, "ExtendedService should have inherits edge to SampleService"
2213+
2214+
2215+
def test_groovy_implements_edge():
2216+
"""`class X implements Iface` must emit an implements edge."""
2217+
r = extract_groovy(FIXTURES / "sample.groovy")
2218+
node_by_id = {n["id"]: n["label"] for n in r["nodes"]}
2219+
found = any(
2220+
"ExtendedService" in node_by_id.get(e["source"], "")
2221+
and "Resettable" in node_by_id.get(e["target"], "")
2222+
for e in r["edges"] if e["relation"] == "implements"
2223+
)
2224+
assert found, "ExtendedService should have implements edge to Resettable"
2225+
2226+
21982227
def test_groovy_spock_finds_class():
21992228
r = extract_groovy(FIXTURES / "sample_spock.groovy")
22002229
assert any("SampleSpec" in l for l in _labels(r))

0 commit comments

Comments
 (0)