1
0
mirror of https://github.com/golang/go synced 2024-09-30 22:58:34 -06:00

internal/lsp: improve literal func completion candidates

Previously we were erroneously suggesting a "func() {}" literal in
cases like:

http.Handle("/", <>)

This was happening because saw that the http.HandlerFunc type
satisfied the http.Handler interface, and that http.HandlerFunc is a
function type. However, of course, you can't pass a function literal
to http.Handle().

Make a few tweaks to address the problem:

1. Don't suggest literal "func () {}" candidates if the expected type
   is an interface type.

2. Suggest named function types that implement an interface. This
   causes us to suggest "http.HandlerFunc()" in the above example.

3. Suggest a func literal candidate inside named function type
   conversions. This will suggest "func() {}" when completing
   "http.HandlerFunc(<>)".

This way the false positive func literal is gone, and you still get
literal candidates that help you use an http.HandlerFunc as an
http.Handler. Note that this particular example is not very compelling
in light of http.HandleFunc() which can take a func literal directly,
but such a convenience function may not exist in other analogous
situations.

Change-Id: Ia68097b9a5b8351921349340d18acd8876554691
Reviewed-on: https://go-review.googlesource.com/c/tools/+/205137
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Muir Manders 2019-11-03 17:47:21 -08:00 committed by Rebecca Stambler
parent c07e1c6ef6
commit 0c330b00b1
4 changed files with 20 additions and 15 deletions

View File

@ -21,20 +21,18 @@ import (
// literal generates composite literal, function literal, and make()
// completion items.
func (c *completer) literal(literalType types.Type, imp *importInfo) {
if c.expectedType.objType == nil {
return
}
// Don't provide literal candidates for variadic function arguments.
// For example, don't provide "[]interface{}{}" in "fmt.Print(<>)".
if c.expectedType.variadic {
return
}
expType := c.expectedType.objType
// Avoid literal candidates if the expected type is an empty
// interface. It isn't very useful to suggest a literal candidate of
// every possible type.
if isEmptyInterface(c.expectedType.objType) {
if expType != nil && isEmptyInterface(expType) {
return
}
@ -48,8 +46,8 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
//
// don't offer "mySlice{}" since we have already added a candidate
// of "[]int{}".
if _, named := literalType.(*types.Named); named {
if _, named := deref(c.expectedType.objType).(*types.Named); !named {
if _, named := literalType.(*types.Named); named && expType != nil {
if _, named := deref(expType).(*types.Named); !named {
return
}
}
@ -121,11 +119,11 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
switch t := literalType.Underlying().(type) {
case *types.Struct, *types.Array, *types.Slice, *types.Map:
c.compositeLiteral(t, typeName, float64(score), addlEdits)
case *types.Basic:
case *types.Basic, *types.Signature:
// Add a literal completion for basic types that implement our
// expected interface (e.g. named string type http.Dir
// implements http.FileSystem).
if isInterface(c.expectedType.objType) {
if isInterface(expType) {
c.basicLiteral(t, typeName, float64(score), addlEdits)
}
}
@ -147,7 +145,7 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
}
// If prefix matches "func", client may want a function literal.
if score := c.matcher.Score("func"); !isPointer && score >= 0 {
if score := c.matcher.Score("func"); !isPointer && score >= 0 && !isInterface(expType) {
switch t := literalType.Underlying().(type) {
case *types.Signature:
c.functionLiteral(t, float64(score))

View File

@ -930,9 +930,9 @@ func isPackageLevel(obj types.Object) bool {
return obj.Pkg().Scope().Lookup(obj.Name()) == obj
}
// -- Plundered from golang.org/x/tools/go/ssa -----------------
func isInterface(T types.Type) bool { return types.IsInterface(T) }
func isInterface(T types.Type) bool {
return T != nil && types.IsInterface(T)
}
// -- Plundered from go/scanner: ---------------------------------------

View File

@ -119,6 +119,13 @@ func _() {
http.HandleFunc("", f) //@snippet(")", litFunc, "", "func(${1:rw} http.ResponseWriter, ${2:r} *http.Request) {$0\\}")
// no literal "func" completions
http.Handle("", fun) //@complete(")")
http.HandlerFunc() //@item(handlerFunc, "http.HandlerFunc()", "", "var")
http.Handle("", h) //@snippet(")", handlerFunc, "http.HandlerFunc($0)", "http.HandlerFunc($0)")
http.Handle("", http.HandlerFunc()) //@snippet("))", litFunc, "", "func(${1:rw} http.ResponseWriter, ${2:r} *http.Request) {$0\\}")
var namedReturn func(s string) (b bool)
namedReturn = f //@snippet(" //", litFunc, "func(s string) (b bool) {$0\\}", "func(s string) (b bool) {$0\\}")

View File

@ -1,6 +1,6 @@
-- summary --
CompletionsCount = 214
CompletionSnippetCount = 43
CompletionsCount = 215
CompletionSnippetCount = 45
UnimportedCompletionsCount = 3
DeepCompletionsCount = 5
FuzzyCompletionsCount = 7