go/callgraph/vta: add flows for receiver function types

When creating type propagation graph, we don't add edges between
the interface object i in i.Foo() and receiver objects of callees
provided by the initial call graph. The intuition is that receiver
objects are of concrete type so nothing can flow to them.

This is not true when the receiver type of a callee is a named
function type. If value of such type flows to i, then it should flow
to the receiver r. Then, the callee can in principle invoke r(). We
currently resolve r() to an empty set of callees. The fix is to add
edges between i and r when r is a named function type.

Fixes golang/go#57756

Change-Id: I25427e29bad35db3ae4fe4919a72155d84c7a9f3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/461604
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Tim King <taking@google.com>
Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/go/callgraph/vta/graph.go b/go/callgraph/vta/graph.go
index 2ad0f89..ebb4971 100644
--- a/go/callgraph/vta/graph.go
+++ b/go/callgraph/vta/graph.go
@@ -327,14 +327,16 @@
 		// change type command a := A(b) results in a and b being the
 		// same value. For concrete type A, there is no interesting flow.
 		//
-		// Note: When A is an interface, most interface casts are handled
+		// When A is an interface, most interface casts are handled
 		// by the ChangeInterface instruction. The relevant case here is
 		// when converting a pointer to an interface type. This can happen
 		// when the underlying interfaces have the same method set.
-		//   type I interface{ foo() }
-		//   type J interface{ foo() }
-		//   var b *I
-		//   a := (*J)(b)
+		//
+		//	type I interface{ foo() }
+		//	type J interface{ foo() }
+		//	var b *I
+		//	a := (*J)(b)
+		//
 		// When this happens we add flows between a <--> b.
 		b.addInFlowAliasEdges(b.nodeFromVal(i), b.nodeFromVal(i.X))
 	case *ssa.TypeAssert:
@@ -588,14 +590,22 @@
 		return
 	}
 	cc := c.Common()
+	if cc.Method != nil {
+		// In principle we don't add interprocedural flows for receiver
+		// objects. At a call site, the receiver object is interface
+		// while the callee object is concrete. The flow from interface
+		// to concrete type in general does not make sense. The exception
+		// is when the concrete type is a named function type (see #57756).
+		//
+		// The flow other way around would bake in information from the
+		// initial call graph.
+		if isFunction(f.Params[0].Type()) {
+			b.addInFlowEdge(b.nodeFromVal(cc.Value), b.nodeFromVal(f.Params[0]))
+		}
+	}
 
 	offset := 0
 	if cc.Method != nil {
-		// We don't add interprocedural flows for receiver objects.
-		// At a call site, the receiver object is interface while the
-		// callee object is concrete. The flow from interface to
-		// concrete type does not make sense. The flow other way around
-		// would bake in information from the initial call graph.
 		offset = 1
 	}
 	for i, v := range cc.Args {
diff --git a/go/callgraph/vta/testdata/src/callgraph_issue_57756.go b/go/callgraph/vta/testdata/src/callgraph_issue_57756.go
new file mode 100644
index 0000000..e18f16e
--- /dev/null
+++ b/go/callgraph/vta/testdata/src/callgraph_issue_57756.go
@@ -0,0 +1,67 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// go:build ignore
+
+package testdata
+
+// Test that the values of a named function type are correctly
+// flowing from interface objects i in i.Foo() to the receiver
+// parameters of callees of i.Foo().
+
+type H func()
+
+func (h H) Do() {
+	h()
+}
+
+type I interface {
+	Do()
+}
+
+func Bar() I {
+	return H(func() {})
+}
+
+func For(g G) {
+	b := Bar()
+	b.Do()
+
+	g[0] = b
+	g.Goo()
+}
+
+type G []I
+
+func (g G) Goo() {
+	g[0].Do()
+}
+
+// Relevant SSA:
+// func Bar$1():
+//   return
+//
+// func Bar() I:
+//   t0 = changetype H <- func() (Bar$1)
+//   t1 = make I <- H (t0)
+//
+// func For():
+//   t0 = Bar()
+//   t1 = invoke t0.Do()
+//   t2 = &g[0:int]
+//   *t2 = t0
+//   t3 = (G).Goo(g)
+//
+// func (h H) Do():
+//   t0 = h()
+//
+// func (g G) Goo():
+//   t0 = &g[0:int]
+//   t1 = *t0
+//   t2 = invoke t1.Do()
+
+// WANT:
+// For: (G).Goo(g) -> G.Goo; Bar() -> Bar; invoke t0.Do() -> H.Do
+// H.Do: h() -> Bar$1
+// G.Goo: invoke t1.Do() -> H.Do
diff --git a/go/callgraph/vta/vta_test.go b/go/callgraph/vta/vta_test.go
index 4cd26a5..549c4af 100644
--- a/go/callgraph/vta/vta_test.go
+++ b/go/callgraph/vta/vta_test.go
@@ -26,6 +26,7 @@
 		"testdata/src/callgraph_fields.go",
 		"testdata/src/callgraph_field_funcs.go",
 		"testdata/src/callgraph_recursive_types.go",
+		"testdata/src/callgraph_issue_57756.go",
 	} {
 		t.Run(file, func(t *testing.T) {
 			prog, want, err := testProg(file, ssa.BuilderMode(0))