Repository navigation
Description
What happens
A method call on new(T) links to a same-named method of a different type. Written as (&T{}).M(), or through a local (r := new(T); r.M()), the same call links correctly.
// go.mod
module example.com/p
go 1.22
// a_router.go
package p
type Router struct{}
func (r *Router) Host(h string) *Router { return r }
// b_route.go
package p
type Route struct{}
func (r *Route) Host(h string) *Route { return r }
// use.go
package p
func viaNew() *Route {
return new(Route).Host("x") // Route.Host
}
func viaLiteral() *Route {
return (&Route{}).Host("y") // Route.Host
}
func viaVar() *Route {
r := new(Route)
return r.Host("z") // Route.Host
}- Expected: all three call
b_route.goRoute::Host. - Actual:
viaNewcallsa_router.goRouter::Host(resolvedBy: exact-match).viaLiteralandviaVarlinkRoute::Host(instance-method).
The new call itself is left as an unresolved new reference, and the method is matched by its bare name, so which Host it gets depends on file order: with the two type files renamed (b_router.go, a_route.go), viaNew lands on Route::Host by luck.
To reproduce: put the four files in an empty directory, run codegraph init -y, then codegraph callers Host:
method Router::Host (go) — a_router.go:5 (1):
function viaNew
use.go:4
method Route::Host (go) — b_route.go:5 (2):
function viaLiteral
use.go:8
function viaVar
use.go:13
Real case in gorilla/mux at db9d1d0: mux_test.go:60 new(Route).Host("aaa.bbb.ccc") links Router.Host in mux.go, not Route.Host in route.go:366. The test table uses new(Route).Host / .Path / .Queries / ... throughout, so 117 edges in that repo land on Router's methods.
Found by diffing codegraph's call edges against scip-go.
Environment: main at f72c9d2 (2026-11-01), built from source, Node 22.22.2, Linux x64, WASM engine (CODEGRAPH_KERNEL=0), fresh codegraph init. A 60b0d7a build (2026-10-29) gives the same edges.
Cause (inferred): the receiver new(Route) is not kept on the reference, so resolution has only the name. TS/JS has a shape for the same idea, new C().m (NEW_RECEIVER_SHAPE), which resolves only through the class written at the call. Go's new(T) (and a &T{} receiver, which already works) give the type just as directly.
Related: #2782 fixed a call further along a Go chain; #750 tracks chained factory calls.
Edited 2026-11-04: made the snippet a complete module (go.mod, package p) so it runs as pasted, added the reproduce command with codegraph callers output, and noted that the wrong target depends on file order (checked by renaming the files).
Activity