Net-new over v8 (verified @6478eb71, 0.9.80)
v8 today has no Go receiver typing at all: no _resolve_go_member_calls and no
go_member_calls registration in graphify/extract.py; extractors/go.py carries no
receiver name/type through function_bodies and stamps no _callable/_callable_class
on Go nodes; and go is absent from _LANG_SUFFIXES in cross_repo_calls.py (which
lists only cpp/csharp/java/swift). The recent member-call work on v8 is Python (#4198,
#2860), C# (#3797) and PowerShell (#4163) — none of it Go.
Still missing, and what the paired PR adds: resolve recv.M() through the receiver's
declared type to a method in another file, the go.py plumbing + _callable_class
markers that requires, and parking the call so a merged graph can finish it (#3152).
Fully net-new; nothing here is pre-empted.
What happens
A Go method call on a receiver resolves only when the bare method name happens to
match a symbol declared in the caller's own file. Anything else produces no edge:
// svc/greeter.go
package svc
type Greeter struct{}
func (g *Greeter) Greet() {}
// svc/app.go
package svc
type App struct {
greeter *Greeter
}
func (a *App) Run() { a.greeter.Greet() } // no `calls` edge
extract_go reads the receiver's identifier and discards it unless it names an
imported package, so the shape Go dependency injection actually takes — a struct
field used from a method — contributes nothing to the call graph. The shared
cross-file pass then skips member callees outright (obj.log() → "log" has no
import evidence), and no _resolve_go_* resolver is registered to pick them up.
No test in the repo asserts that a cross-file Go receiver method call yields an
edge; test_go_builtin_call_targets.py states the omission explicitly.
Merging two repos loses the same edges for the same reason, plus one more: Go
nodes never carry the _callable / _callable_class markers (#2438), because
extract_go is the one extractor outside the tree-sitter engine and the engine is
the only place that stamps them. link_cross_repo_member_calls requires
_callable_class on the declaring type, so a Go type could not answer a parked
call even if one existed.
What it should do
Bind the call through the receiver's declared type, as the Swift (#1356), C++
(#1547), ObjC (#1556) and C# (#1609) resolvers already do for their languages —
and when the type is declared nowhere in the build, park the call so a merged
graph can finish it (#3152).
Go writes the receiver's type down in four places, all of them cheap to read:
| shape |
source |
type App struct { greeter *Greeter } |
struct field |
func (s *Server) M() / func f(g *Greeter) |
parameter |
var g Greeter |
var declaration |
g := Greeter{} / g := &Greeter{} |
composite literal |
The single-definition guard has to stay: a Go type node id folds in the package
directory, so two packages declaring Greeter are two nodes and without import
evidence neither one is the answer.
Why it matters
Go is a backend language and services are split across repositories; the call
graph across that boundary is the reason to merge two graphs at all. Together with
the ObjC (#3384), TS/JS (#3386), Kotlin (#3388) and PHP (#3390) gaps, this covers
the languages a mobile-plus-backend org actually ships.
PR to follow.
Net-new over v8 (verified @6478eb71, 0.9.80)
v8 today has no Go receiver typing at all: no
_resolve_go_member_callsand nogo_member_callsregistration ingraphify/extract.py;extractors/go.pycarries noreceiver name/type through
function_bodiesand stamps no_callable/_callable_classon Go nodes; and
gois absent from_LANG_SUFFIXESincross_repo_calls.py(whichlists only cpp/csharp/java/swift). The recent member-call work on v8 is Python (#4198,
#2860), C# (#3797) and PowerShell (#4163) — none of it Go.
Still missing, and what the paired PR adds: resolve
recv.M()through the receiver'sdeclared type to a method in another file, the
go.pyplumbing +_callable_classmarkers that requires, and parking the call so a merged graph can finish it (#3152).
Fully net-new; nothing here is pre-empted.
What happens
A Go method call on a receiver resolves only when the bare method name happens to
match a symbol declared in the caller's own file. Anything else produces no edge:
extract_goreads the receiver's identifier and discards it unless it names animported package, so the shape Go dependency injection actually takes — a struct
field used from a method — contributes nothing to the call graph. The shared
cross-file pass then skips member callees outright (
obj.log()→"log"has noimport evidence), and no
_resolve_go_*resolver is registered to pick them up.No test in the repo asserts that a cross-file Go receiver method call yields an
edge;
test_go_builtin_call_targets.pystates the omission explicitly.Merging two repos loses the same edges for the same reason, plus one more: Go
nodes never carry the
_callable/_callable_classmarkers (#2438), becauseextract_gois the one extractor outside the tree-sitter engine and the engine isthe only place that stamps them.
link_cross_repo_member_callsrequires_callable_classon the declaring type, so a Go type could not answer a parkedcall even if one existed.
What it should do
Bind the call through the receiver's declared type, as the Swift (#1356), C++
(#1547), ObjC (#1556) and C# (#1609) resolvers already do for their languages —
and when the type is declared nowhere in the build, park the call so a merged
graph can finish it (#3152).
Go writes the receiver's type down in four places, all of them cheap to read:
type App struct { greeter *Greeter }func (s *Server) M()/func f(g *Greeter)var g Greeterg := Greeter{}/g := &Greeter{}The single-definition guard has to stay: a Go type node id folds in the package
directory, so two packages declaring
Greeterare two nodes and without importevidence neither one is the answer.
Why it matters
Go is a backend language and services are split across repositories; the call
graph across that boundary is the reason to merge two graphs at all. Together with
the ObjC (#3384), TS/JS (#3386), Kotlin (#3388) and PHP (#3390) gaps, this covers
the languages a mobile-plus-backend org actually ships.
PR to follow.