fix(go): a call on an embedded struct's method does not reach the embedder's method of that name (#2430)

Go has no inheritance. A struct that embeds another (`type Engine struct
{ RouterGroup }`) has an `extends` edge to it since #2397, and the
interface-dispatch bridge read every `extends` edge as an override, so it
linked RouterGroup.Use to Engine.Use. A call on a *RouterGroup only ever
runs RouterGroup's own Use, so a flow or impact walk through it went on
into Engine.Use and the code only the engine runs.

The bridge now skips a Go type's supertype edge unless it points at an
interface: only a call through an interface dispatches. Structs that embed
an interface keep their links, and so do the go-implements edges, so a call
through IRoutes still reaches both RouterGroup.Use and Engine.Use. Other
languages are unchanged.

Removed on gin / prometheus / etcd: 1 / 112 / 60 interface-impl edges, all
from a struct's method to a struct embedding it; nothing else in the graph
changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-10-07 14:42:45 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 4731149469
commit 883504d201
3 changed files with 257 additions and 0 deletions
+6
View File
@@ -1316,6 +1316,12 @@ async function interfaceOverrideEdges(queries: QueryBuilder, onYield: MaybeYield
for (const sup of sups) {
const base = queries.getNodeById(sup.target);
if (!base || !IFACE_OVERRIDE_LANGS.has(base.language) || base.id === cls.id) continue;
// Go has no inheritance: a Go type's supertype edge to a struct or a
// defined type is an embedding (`type Engine struct { RouterGroup }`),
// and a call on the embedded type runs its own method, never the
// embedder's of the same name. Only a call through an interface
// dispatches.
if (cls.language === 'go' && base.kind !== 'interface') continue;
const promotes = goStruct && sup.provenance === 'heuristic' && base.kind === 'interface';
// Group impl methods by name to handle OVERLOADS: an interface `list()` and
// `list(params)` are distinct nodes and a call may resolve to either, so