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.
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.