Skip to content

fix(go): don't emit embeds for interface type-set constraints#1818

Open
Synvoya wants to merge 1 commit into
Graphify-Labs:v8from
Synvoya:fix/go-interface-type-union-not-embeds
Open

fix(go): don't emit embeds for interface type-set constraints#1818
Synvoya wants to merge 1 commit into
Graphify-Labs:v8from
Synvoya:fix/go-interface-type-union-not-embeds

Conversation

@Synvoya

@Synvoya Synvoya commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Problem

The Go extractor treats every interface type_elem as an embedded interface.
But a Go type-set constraint (generics) is not embedding — a union
A | B or an approximation ~T is a set of type terms, and Go only ever
embeds a lone interface type. Because the branch walked union terms the same
way it walks an embedded interface, each non-predeclared term produced a
spurious embeds heritage edge.

Reproduction on v8 HEAD

package p
type MyInt int
type MyFloat float64
type Number interface {
    MyInt | MyFloat
}

extract_go emits:

Number --embeds--> MyInt
Number --embeds--> MyFloat

embeds is a heritage relation (grouped with inherits / implements /
mixes_in in extract.py), so this asserts a composition/method-promotion
relationship that does not exist — Number cannot embed the concrete types
MyInt/MyFloat; they are constraint terms. This pollutes heritage queries
over the graph with false edges.

Fix

Detect a type-set type_elem (it has a | or a negated_type child — both
tokens appear only in constraint position, never in embedding) and reclassify
its terms as references (context="type_constraint") instead of embeds.
This keeps the type link but drops the false heritage claim. The edge count is
unchanged; only the relation/context is corrected.

Genuine interface embedding is untouched — a lone interface term
(Reader, io.Reader, Sortable[int]) has no |/~, so it still emits
embeds.

Tests

Added to tests/test_languages.py:

  • test_go_interface_type_union_is_not_embeddingnegative control:
    union terms must not be embeds, and are present as
    references/type_constraint. Fails on HEAD (asserts the spurious
    Number->MyInt embed away), passes with the fix.
  • test_go_interface_embedding_emits_embeds /
    test_go_struct_embedding_emits_embeds — positive guards that genuine
    embedding on the existing sample.go fixture (ReaderLogger embeds
    Logger/Reader; DataProcessor embeds BaseProcessor) still works.

python3 -m pytest tests/test_languages.py -q317 passed, 13 skipped
(the 13 skips are the optional tree-sitter-dm grammar). No behavior change
outside Go interface type-set constraints.

An interface type_elem that is a generics type-set constraint - a union
(`A | B`) or an approximation (`~T`) - was walked like an embedded
interface, so each non-predeclared term produced an `embeds` heritage
edge (e.g. `type Number interface { MyInt | MyFloat }` emitted
Number->MyInt and Number->MyFloat embeds).

Go only embeds a lone interface type, and each embed is its own element;
union/approximation terms are a constraint type set that can never be
embedded, so those edges assert composition that does not exist. Detect
type-set elems (a `|` or `negated_type` child) and reclassify their
terms as `references` (context=type_constraint), preserving the type
link without the false heritage edge. Genuine interface embedding
(single/qualified/generic interface terms) is unchanged.

Adds regression coverage: positive guards for struct and interface
embedding, plus a negative control asserting union terms are not embeds.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant