go/types/typeutil: normalize instantiated callees

Callee and StaticCallee promise to return the generic declaration
for instantiated functions and methods. Generic method selections
instead return the synthetic method on the instantiated receiver.

Normalize function results to their Origin so both helpers honor
that contract. Update unusedresult diagnostics to use the generic
receiver name.

Fixes golang/go#80533

Change-Id: Ic0ccc9d09e3aaf5ed8ab8f3dd55fb501a17fc630
GitHub-Last-Rev: 75869bc2c0ab94131bfd25f65aeed139e7d9b072
GitHub-Pull-Request: golang/tools#660
Reviewed-on: https://go-review.googlesource.com/c/tools/+/804560
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/go/analysis/passes/unusedresult/testdata/src/typeparams/typeparams.go b/go/analysis/passes/unusedresult/testdata/src/typeparams/typeparams.go
index 0add516..b6b3a3b 100644
--- a/go/analysis/passes/unusedresult/testdata/src/typeparams/typeparams.go
+++ b/go/analysis/passes/unusedresult/testdata/src/typeparams/typeparams.go
@@ -30,10 +30,10 @@
 	_ = userdefs.MustUse[int](2)
 
 	s := userdefs.SingleTypeParam[int]{X: 1}
-	s.String() // want `result of \(\*typeparams/userdefs.SingleTypeParam\[int\]\).String call not used`
+	s.String() // want `result of \(\*typeparams/userdefs.SingleTypeParam\[T\]\).String call not used`
 	_ = s.String()
 
 	m := userdefs.MultiTypeParam[int, string]{X: 1, Y: "one"}
-	m.String() // want `result of \(\*typeparams/userdefs.MultiTypeParam\[int, string\]\).String call not used`
+	m.String() // want `result of \(\*typeparams/userdefs.MultiTypeParam\[T, U\]\).String call not used`
 	_ = m.String()
 }
diff --git a/go/types/typeutil/callee_test.go b/go/types/typeutil/callee_test.go
index 3f96533..74d31cc 100644
--- a/go/types/typeutil/callee_test.go
+++ b/go/types/typeutil/callee_test.go
@@ -167,3 +167,59 @@
 		}
 	}
 }
+
+// TestCalleeReturnsOrigin ensures that Callee and StaticCallee
+// return the generic (origin) symbol, not the instance.
+func TestCalleeReturnsOrigin(t *testing.T) {
+	const src = `package p
+
+func F[T any]() {}
+
+type G[T any] struct{}
+
+func (G[T]) M() {}
+
+func calls() {
+	F[int]()
+	G[int]{}.M()
+}
+`
+
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "test.go", src, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	calls := f.Decls[3].(*ast.FuncDecl).Body.List
+	call := func(index int) *ast.CallExpr {
+		return calls[index].(*ast.ExprStmt).X.(*ast.CallExpr)
+	}
+
+	info := &types.Info{
+		Types: make(map[ast.Expr]types.TypeAndValue),
+		Uses:  make(map[*ast.Ident]types.Object),
+	}
+	pkg, err := new(types.Config).Check("p", fset, []*ast.File{f}, info)
+	if err != nil {
+		t.Fatal(err)
+	}
+	fObj := pkg.Scope().Lookup("F")
+	mObj := pkg.Scope().Lookup("G").Type().(*types.Named).Method(0)
+
+	tests := []struct {
+		name string
+		call *ast.CallExpr
+		want types.Object
+	}{
+		{"F[int]()", call(0), fObj},
+		{"G[int]{}.M()", call(1), mObj},
+	}
+	for _, test := range tests {
+		if got := typeutil.Callee(info, test.call); got != test.want {
+			t.Errorf("call %s: Callee returned %v, want declaration %v", test.name, got, test.want)
+		}
+		if got := typeutil.StaticCallee(info, test.call); got != test.want {
+			t.Errorf("call %s: StaticCallee returned %v, want declaration %v", test.name, got, test.want)
+		}
+	}
+}
diff --git a/internal/typesinternal/classify_call.go b/internal/typesinternal/classify_call.go
index 14f616f..d5c40a2 100644
--- a/internal/typesinternal/classify_call.go
+++ b/internal/typesinternal/classify_call.go
@@ -168,6 +168,9 @@
 	if _, ok := obj.(*types.TypeName); ok {
 		return nil
 	}
+	if fn, ok := obj.(*types.Func); ok {
+		return fn.Origin()
+	}
 	return obj
 }
 
@@ -185,7 +188,7 @@
 	if fn == nil || isInterfaceMethod(fn) {
 		return nil
 	}
-	return fn
+	return fn.Origin()
 }
 
 // isInterfaceMethod reports whether its argument is a method of an interface.