fix(go): a call through a func-typed struct field reaches the functions put in it (#2745)

A Go struct can hold a function in a field and call it through the field.
Prometheus' service discovery runs every provider that way: refresh.Discovery
calls d.refreshf(ctx), NewDiscovery copies refreshf from Options.RefreshF,
and each provider passes its own method (RefreshF: d.refresh). Nothing
answered d.refreshf, so a flow from Discovery.Run stopped at the field; #2430
rightly dropped the interface-impl edges that had stood in for it.

goFuncFieldEdges, a Go-only synthesis pass, links a call x.F(...) through a
func field of a struct to each function or method value put in that field:
by a composite literal (keyed or positional, found through its instantiates
edge), by an assignment x.F = v, or by one level of field-to-field copy
(refreshf: opts.RefreshF). A value is what the resolver already linked it
to, else x's method m for x.m, an interface's method for an interface value.
Receivers are typed through the resolver's own Go scope reader
(goLocalDeclarationAt over goLocalDecl, #2706), or by a local's composite
literal, new(T), type assertion or resolved call result. Channels are keyed
by package, struct and field, so a generic field name never crosses between
unrelated structs; one package's test wiring links no other package's test;
an edge cites a function's production wiring; generated files are not read.

Edges are calls, provenance heuristic, synthesizedBy go-func-field, with
field, via (package-qualified when the copied field is another package's)
and registeredAt; explore, the node trail and ContextBuilder read them as
"func field `Discovery.refreshf` (set from `Options.RefreshF`)". The pass
sits after the Go framework passes, with a pool cost hint, and every Go file
now counts as a synthesis input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-10-21 13:04:48 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent e24827011e
commit 6e24711590
7 changed files with 1298 additions and 4 deletions
+4
View File
@@ -75,6 +75,10 @@ export function describeSynthesizedHop(edge: Edge | null | undefined): Synthesiz
return hop(`fn-pointer ${m.via ? String(m.via) : ''}`);
case 'goframe-route':
return hop(`GoFrame route ${m.route ? String(m.route) : ''}`);
case 'go-func-field':
// Go: a call through a struct field holding a function, `d.refreshf(ctx)`;
// `via` is the field it was copied from, which the wiring site writes.
return hop(`func field ${quoted(m.field, 'a func field')}${m.via ? ` (set from \`${String(m.via)}\`)` : ''}`);
default:
// redux-thunk, cpp-override, gin-middleware-chain, flutter-build, …
return hop(by.replace(/-/g, ' '));