Skip to content

Commit 0c330b0

Browse files
muirdmstamblerre
authored andcommitted
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 <[email protected]> Run-TryBot: Rebecca Stambler <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent c07e1c6 commit 0c330b0

File tree

4 files changed

+20
-15
lines changed

4 files changed

+20
-15
lines changed

internal/lsp/source/completion_literal.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,18 @@ import (
2121
// literal generates composite literal, function literal, and make()
2222
// completion items.
2323
func (c *completer) literal(literalType types.Type, imp *importInfo) {
24-
if c.expectedType.objType == nil {
25-
return
26-
}
27-
2824
// Don't provide literal candidates for variadic function arguments.
2925
// For example, don't provide "[]interface{}{}" in "fmt.Print(<>)".
3026
if c.expectedType.variadic {
3127
return
3228
}
3329

30+
expType := c.expectedType.objType
31+
3432
// Avoid literal candidates if the expected type is an empty
3533
// interface. It isn't very useful to suggest a literal candidate of
3634
// every possible type.
37-
if isEmptyInterface(c.expectedType.objType) {
35+
if expType != nil && isEmptyInterface(expType) {
3836
return
3937
}
4038

@@ -48,8 +46,8 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
4846
//
4947
// don't offer "mySlice{}" since we have already added a candidate
5048
// of "[]int{}".
51-
if _, named := literalType.(*types.Named); named {
52-
if _, named := deref(c.expectedType.objType).(*types.Named); !named {
49+
if _, named := literalType.(*types.Named); named && expType != nil {
50+
if _, named := deref(expType).(*types.Named); !named {
5351
return
5452
}
5553
}
@@ -121,11 +119,11 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
121119
switch t := literalType.Underlying().(type) {
122120
case *types.Struct, *types.Array, *types.Slice, *types.Map:
123121
c.compositeLiteral(t, typeName, float64(score), addlEdits)
124-
case *types.Basic:
122+
case *types.Basic, *types.Signature:
125123
// Add a literal completion for basic types that implement our
126124
// expected interface (e.g. named string type http.Dir
127125
// implements http.FileSystem).
128-
if isInterface(c.expectedType.objType) {
126+
if isInterface(expType) {
129127
c.basicLiteral(t, typeName, float64(score), addlEdits)
130128
}
131129
}
@@ -147,7 +145,7 @@ func (c *completer) literal(literalType types.Type, imp *importInfo) {
147145
}
148146

149147
// If prefix matches "func", client may want a function literal.
150-
if score := c.matcher.Score("func"); !isPointer && score >= 0 {
148+
if score := c.matcher.Score("func"); !isPointer && score >= 0 && !isInterface(expType) {
151149
switch t := literalType.Underlying().(type) {
152150
case *types.Signature:
153151
c.functionLiteral(t, float64(score))

internal/lsp/source/rename_check.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -930,9 +930,9 @@ func isPackageLevel(obj types.Object) bool {
930930
return obj.Pkg().Scope().Lookup(obj.Name()) == obj
931931
}
932932

933-
// -- Plundered from golang.org/x/tools/go/ssa -----------------
934-
935-
func isInterface(T types.Type) bool { return types.IsInterface(T) }
933+
func isInterface(T types.Type) bool {
934+
return T != nil && types.IsInterface(T)
935+
}
936936

937937
// -- Plundered from go/scanner: ---------------------------------------
938938

internal/lsp/testdata/snippets/literal_snippets.go.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ func _() {
119119

120120
http.HandleFunc("", f) //@snippet(")", litFunc, "", "func(${1:rw} http.ResponseWriter, ${2:r} *http.Request) {$0\\}")
121121

122+
// no literal "func" completions
123+
http.Handle("", fun) //@complete(")")
124+
125+
http.HandlerFunc() //@item(handlerFunc, "http.HandlerFunc()", "", "var")
126+
http.Handle("", h) //@snippet(")", handlerFunc, "http.HandlerFunc($0)", "http.HandlerFunc($0)")
127+
http.Handle("", http.HandlerFunc()) //@snippet("))", litFunc, "", "func(${1:rw} http.ResponseWriter, ${2:r} *http.Request) {$0\\}")
128+
122129
var namedReturn func(s string) (b bool)
123130
namedReturn = f //@snippet(" //", litFunc, "func(s string) (b bool) {$0\\}", "func(s string) (b bool) {$0\\}")
124131

internal/lsp/testdata/summary.txt.golden

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-- summary --
2-
CompletionsCount = 214
3-
CompletionSnippetCount = 43
2+
CompletionsCount = 215
3+
CompletionSnippetCount = 45
44
UnimportedCompletionsCount = 3
55
DeepCompletionsCount = 5
66
FuzzyCompletionsCount = 7

0 commit comments

Comments
 (0)