mirror of
https://github.com/golang/go
synced 2024-11-18 10:54:40 -07:00
internal/lsp/source: improve type switch case completion
Now we will filter out the types already used in other case statements: switch ast.Node(nil).(type) { case *ast.Ident: case *ast.I<> // don't offer "Ident" since it has been used } Note that the implementation was not able to use a map to track the seen types.Types because we build up types.Type entries dynamically when searching for completions (e.g. types.NewPointer() to make a pointer type). We must use types.Identical() instead of direct pointer equality. Change-Id: I316638bb48bfd6802e2caea671f297d640291010 Reviewed-on: https://go-review.googlesource.com/c/tools/+/247098 Run-TryBot: Muir Manders <muir@mnd.rs> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
383b97c0b5
commit
19738be007
@ -1576,6 +1576,10 @@ type typeNameInference struct {
|
||||
|
||||
// wantComparable is true if we want a comparable type.
|
||||
wantComparable bool
|
||||
|
||||
// seenTypeSwitchCases tracks types that have already been used by
|
||||
// the containing type switch.
|
||||
seenTypeSwitchCases []types.Type
|
||||
}
|
||||
|
||||
// expectedCandidate returns information about the expected candidate
|
||||
@ -1885,10 +1889,11 @@ func breaksExpectedTypeInference(n ast.Node) bool {
|
||||
// expectTypeName returns information about the expected type name at position.
|
||||
func expectTypeName(c *completer) typeNameInference {
|
||||
var (
|
||||
wantTypeName bool
|
||||
wantComparable bool
|
||||
modifiers []typeModifier
|
||||
assertableFrom types.Type
|
||||
wantTypeName bool
|
||||
wantComparable bool
|
||||
modifiers []typeModifier
|
||||
assertableFrom types.Type
|
||||
seenTypeSwitchCases []types.Type
|
||||
)
|
||||
|
||||
Nodes:
|
||||
@ -1914,6 +1919,23 @@ Nodes:
|
||||
return true
|
||||
})
|
||||
wantTypeName = true
|
||||
|
||||
// Track the types that have already been used in this
|
||||
// switch's case statements so we don't recommend them.
|
||||
for _, e := range swtch.Body.List {
|
||||
for _, typeExpr := range e.(*ast.CaseClause).List {
|
||||
// Skip if type expression contains pos. We don't want to
|
||||
// count it as already used if the user is completing it.
|
||||
if typeExpr.Pos() < c.pos && c.pos <= typeExpr.End() {
|
||||
continue
|
||||
}
|
||||
|
||||
if t := c.pkg.GetTypesInfo().TypeOf(typeExpr); t != nil {
|
||||
seenTypeSwitchCases = append(seenTypeSwitchCases, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break Nodes
|
||||
}
|
||||
return typeNameInference{}
|
||||
@ -1985,10 +2007,11 @@ Nodes:
|
||||
}
|
||||
|
||||
return typeNameInference{
|
||||
wantTypeName: wantTypeName,
|
||||
wantComparable: wantComparable,
|
||||
modifiers: modifiers,
|
||||
assertableFrom: assertableFrom,
|
||||
wantTypeName: wantTypeName,
|
||||
wantComparable: wantComparable,
|
||||
modifiers: modifiers,
|
||||
assertableFrom: assertableFrom,
|
||||
seenTypeSwitchCases: seenTypeSwitchCases,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2070,6 +2093,7 @@ func (c *candidate) anyCandType(f func(t types.Type, addressable bool) bool) boo
|
||||
}
|
||||
|
||||
// matchingCandidate reports whether cand matches our type inferences.
|
||||
// It mutates cand's score in certain cases.
|
||||
func (c *completer) matchingCandidate(cand *candidate) bool {
|
||||
if isTypeName(cand.obj) {
|
||||
return c.matchingTypeName(cand)
|
||||
@ -2289,6 +2313,14 @@ func (c *completer) matchingTypeName(cand *candidate) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Skip this type if it has already been used in another type
|
||||
// switch case.
|
||||
for _, seen := range c.inference.typeName.seenTypeSwitchCases {
|
||||
if types.Identical(candType, seen) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// We can expect a type name and have an expected type in cases like:
|
||||
//
|
||||
// var foo []int
|
||||
@ -2303,11 +2335,13 @@ func (c *completer) matchingTypeName(cand *candidate) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if typeMatches(cand.obj.Type()) {
|
||||
t := cand.obj.Type()
|
||||
|
||||
if typeMatches(t) {
|
||||
return true
|
||||
}
|
||||
|
||||
if typeMatches(types.NewPointer(cand.obj.Type())) {
|
||||
if !isInterface(t) && typeMatches(types.NewPointer(t)) {
|
||||
cand.makePointer = true
|
||||
return true
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package rank
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
func _() {
|
||||
type basket int //@item(basket, "basket", "int", "type")
|
||||
var banana string //@item(banana, "banana", "string", "var")
|
||||
@ -8,4 +13,19 @@ func _() {
|
||||
case b: //@complete(":", basket)
|
||||
b //@complete(" //", banana, basket)
|
||||
}
|
||||
|
||||
Ident //@item(astIdent, "Ident", "struct{...}", "struct")
|
||||
IfStmt //@item(astIfStmt, "IfStmt", "struct{...}", "struct")
|
||||
|
||||
switch ast.Node(nil).(type) {
|
||||
case *ast.Ident:
|
||||
case *ast.I: //@rank(":", astIfStmt, astIdent)
|
||||
}
|
||||
|
||||
Stringer //@item(fmtStringer, "Stringer", "interface{...}", "interface")
|
||||
GoStringer //@item(fmtGoStringer, "GoStringer", "interface{...}", "interface")
|
||||
|
||||
switch interface{}(nil).(type) {
|
||||
case fmt.Stringer: //@rank(":", fmtStringer, fmtGoStringer)
|
||||
}
|
||||
}
|
||||
|
2
internal/lsp/testdata/lsp/summary.txt.golden
vendored
2
internal/lsp/testdata/lsp/summary.txt.golden
vendored
@ -6,7 +6,7 @@ CompletionSnippetCount = 81
|
||||
UnimportedCompletionsCount = 6
|
||||
DeepCompletionsCount = 5
|
||||
FuzzyCompletionsCount = 8
|
||||
RankedCompletionsCount = 128
|
||||
RankedCompletionsCount = 130
|
||||
CaseSensitiveCompletionsCount = 4
|
||||
DiagnosticsCount = 44
|
||||
FoldingRangesCount = 2
|
||||
|
Loading…
Reference in New Issue
Block a user