Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion graphify/extractors/go.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,16 @@ def walk(node) -> None:
for elem in type_body.children:
if elem.type != "type_elem":
continue
# A type_elem that is a generics type-set constraint -
# a union (`A | B`) or an approximation (`~T`) - is NOT
# interface embedding. Go only embeds a lone interface
# type; union/approximation terms can never be embedded,
# so they must not emit `embeds` heritage edges. Keep the
# type link as a `references` (type_constraint) edge.
is_type_set = any(
c.type == "|" or c.type == "negated_type"
for c in elem.children
)
refs = []
for sub in elem.children:
if sub.is_named:
Expand All @@ -285,7 +295,10 @@ def walk(node) -> None:
tgt = ensure_named_node(ref_name, elem.start_point[0] + 1)
if tgt == type_nid:
continue
if role == "type":
if is_type_set:
add_edge(type_nid, tgt, "references",
elem.start_point[0] + 1, context="type_constraint")
elif role == "type":
add_edge(type_nid, tgt, "embeds",
elem.start_point[0] + 1)
else:
Expand Down
41 changes: 41 additions & 0 deletions tests/test_languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,47 @@ def test_go_receiver_uses_pkg_scope():
assert "sample" not in server_nodes[0]["id"].split(":")[0]


def test_go_interface_embedding_emits_embeds():
"""`type ReaderLogger interface { Logger; Reader }` embeds both interfaces."""
r = extract_go(FIXTURES / "sample.go")
embeds = _edge_labels(r, "embeds")
assert ("ReaderLogger", "Logger") in embeds
assert ("ReaderLogger", "Reader") in embeds


def test_go_struct_embedding_emits_embeds():
"""`type DataProcessor struct { BaseProcessor }` embeds the base struct."""
r = extract_go(FIXTURES / "sample.go")
assert ("DataProcessor", "BaseProcessor") in _edge_labels(r, "embeds")


def test_go_interface_type_union_is_not_embedding(tmp_path):
"""A generics type-set constraint (`A | B`) is NOT interface embedding.

Go only allows *interfaces* to be embedded, and each embed is its own
element. A union of type terms (`MyInt | MyFloat`) is a constraint type
set that can never be embedded, so its terms must not emit `embeds`
heritage edges. They are reclassified as `references` (type_constraint)
so the type link is preserved without asserting false composition.
"""
source = tmp_path / "constraint.go"
source.write_text(
"package p\n"
"type MyInt int\n"
"type MyFloat float64\n"
"type Number interface {\n"
"\tMyInt | MyFloat\n"
"}\n"
)
r = extract_go(source)
embeds = _edge_labels(r, "embeds")
assert ("Number", "MyInt") not in embeds
assert ("Number", "MyFloat") not in embeds
refs = _edge_labels(r, "references", "type_constraint")
assert ("Number", "MyInt") in refs
assert ("Number", "MyFloat") in refs


# ---------------------------------------------------------------------------
# Julia
# ---------------------------------------------------------------------------
Expand Down
Loading