Skip to content

Go member calls resolve to nothing across files, and cannot cross a repo boundary at all #3393

Description

@xiongjianxu

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions