From 0941294088d129f9e8931c1371f1aac9b7fa58da Mon Sep 17 00:00:00 2001 From: Muir Manders Date: Thu, 14 Apr 2022 21:30:13 -0700 Subject: [PATCH] lsp/completion: further improve generic func arg ranking In cases like: func foo[T int | string](T) {} foo[int](<>) Previously at <> we would favor int and string candidates. This is because go/types doesn't instantiate foo in this case (for some reason). Work around the issue by using types.CheckExpr to re-check the *ast.CallExpr.Fun. CheckExpr seems to do a better than a full type check in the face of errors. Updates golang/go#52291 Updates golang/go#52503 Change-Id: Ide436428f3232db2e06ea3cc22ea250edbf28685 Reviewed-on: https://go-review.googlesource.com/c/tools/+/400614 Reviewed-by: Dmitri Shuralyov Reviewed-by: Robert Findley Run-TryBot: Robert Findley gopls-CI: kokoro TryBot-Result: Gopher Robot --- internal/lsp/source/completion/completion.go | 17 ++++++++++++++++- internal/lsp/testdata/typeparams/type_params.go | 4 ++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/lsp/source/completion/completion.go b/internal/lsp/source/completion/completion.go index 089c3736ae..32d5370563 100644 --- a/internal/lsp/source/completion/completion.go +++ b/internal/lsp/source/completion/completion.go @@ -2002,7 +2002,22 @@ Nodes: break Nodes } - if sig, _ := c.pkg.GetTypesInfo().Types[node.Fun].Type.(*types.Signature); sig != nil { + sig, _ := c.pkg.GetTypesInfo().Types[node.Fun].Type.(*types.Signature) + + if sig != nil && typeparams.ForSignature(sig).Len() > 0 { + // If we are completing a generic func call, re-check the call expression. + // This allows type param inference to work in cases like: + // + // func foo[T any](T) {} + // foo[int](<>) // <- get "int" completions instead of "T" + // + // TODO: remove this after https://go.dev/issue/52503 + info := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} + types.CheckExpr(c.snapshot.FileSet(), c.pkg.GetTypes(), node.Fun.Pos(), node.Fun, info) + sig, _ = info.Types[node.Fun].Type.(*types.Signature) + } + + if sig != nil { inf = c.expectedCallParamType(inf, node, sig) } diff --git a/internal/lsp/testdata/typeparams/type_params.go b/internal/lsp/testdata/typeparams/type_params.go index 34a2a6baa5..f048027956 100644 --- a/internal/lsp/testdata/typeparams/type_params.go +++ b/internal/lsp/testdata/typeparams/type_params.go @@ -32,6 +32,6 @@ func _() { var _ int = returnTP //@snippet(" //", returnTP, "returnTP[${1:}](${2:})", "returnTP[${1:A int|float64}](${2:a A})") var aa int //@item(tpInt, "aa", "int", "var") - var ab string //@item(tpString, "ab", "string", "var") - returnTP[int](a) //@rank(")", tpInt, tpString) + var ab float64 //@item(tpFloat, "ab", "float64", "var") + returnTP[int](a) //@rank(")", tpInt, tpFloat) }