1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:14:41 -07:00

internal/lsp: fix hover for implicit type switch variable declarations

There was a bug in the hover for type switch variables. For example:

var x interface{}
switch y := x.(type) {
    case string:
    case int:
}

Hovering over y would previously show "var y string", because y's object
would be mapped to the first types.Object in the type switch. Now we
show the hover for y as "var y interface{}", since it's not yet in the
cases.

Change-Id: Ia9bd0afc4ddbb9d33bbd0c78fa32ffa75836a326
Reviewed-on: https://go-review.googlesource.com/c/tools/+/244497
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Rebecca Stambler 2020-07-23 01:36:12 -04:00
parent cbb3c69a37
commit 7017fd6b13
8 changed files with 59 additions and 37 deletions

View File

@ -96,7 +96,13 @@ func HoverIdentifier(ctx context.Context, i *IdentifierInfo) (*HoverInformation,
}
h.Signature = b.String()
case types.Object:
h.Signature = objectString(x, i.qf)
// If the variable is implicitly declared in a type switch, we need to
// manually generate its object string.
if v, ok := x.(*types.Var); ok && i.Declaration.typeSwitchImplicit {
h.Signature = fmt.Sprintf("var %s %s", v.Name(), types.TypeString(i.enclosing, i.qf))
} else {
h.Signature = objectString(x, i.qf)
}
}
if obj := i.Declaration.obj; obj != nil {
h.SingleLine = objectString(obj, i.qf)

View File

@ -43,6 +43,10 @@ type Declaration struct {
MappedRange []mappedRange
node ast.Node
obj types.Object
// typeSwitchImplicit indicates that the declaration is in an implicit
// type switch.
typeSwitchImplicit bool
}
// Identifier returns identifier information for a position
@ -152,9 +156,10 @@ func findIdentifier(ctx context.Context, s Snapshot, pkg Package, file *ast.File
result.Declaration.obj = pkg.GetTypesInfo().ObjectOf(result.ident)
if result.Declaration.obj == nil {
// If there was no types.Object for the declaration, there might be an implicit local variable
// declaration in a type switch.
// If there was no types.Object for the declaration, there might be an
// implicit local variable declaration in a type switch.
if objs := typeSwitchImplicits(pkg, path); len(objs) > 0 {
result.Declaration.typeSwitchImplicit = true
// There is no types.Object for the declaration of an implicit local variable,
// but all of the types.Objects associated with the usages of this variable can be
// used to connect it back to the declaration.
@ -244,6 +249,15 @@ func searchForEnclosing(pkg Package, path []ast.Node) types.Type {
return t.Type()
}
}
case *ast.TypeSwitchStmt:
// The right-hand side of a type switch should only have one
// element, and we need to track its type in order to generate
// hover information for implicit type switch variables.
if assign, ok := n.Assign.(*ast.AssignStmt); ok && len(assign.Rhs) == 1 {
if rhs := assign.Rhs[0].(*ast.TypeAssertExpr); ok {
return pkg.GetTypesInfo().TypeOf(rhs.X)
}
}
}
}
return nil
@ -269,12 +283,10 @@ func objToDecl(ctx context.Context, v View, srcPkg Package, obj types.Object) (a
if err != nil {
return nil, err
}
posToDecl, err := ph.PosToDecl(ctx)
if err != nil {
return nil, err
}
return posToDecl[obj.Pos()], nil
}

View File

@ -514,7 +514,7 @@ func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
return []byte(hover), nil
}))
if hover != expectHover {
t.Errorf("for %v got %q want %q", d.Src, hover, expectHover)
t.Errorf("hover for %s failed:\n%s", d.Src, tests.Diff(expectHover, hover))
}
}
if !d.OnlyHover {

View File

@ -5,11 +5,11 @@ import "fmt"
func TypeStuff() { //@Stuff
var x string
switch y := interface{}(x).(type) { //@mark(switchY, "y"),mark(switchStringY,"y"),godef("y", switchY)
case int:
fmt.Printf("%v", y) //@godef("y", switchY)
case string:
fmt.Printf("%v", y) //@godef("y", switchStringY)
switch y := interface{}(x).(type) { //@mark(switchY, "y"),godef("y", switchY)
case int: //@mark(intY, "int")
fmt.Printf("%v", y) //@hover("y", intY)
case string: //@mark(stringY, "string")
fmt.Printf("%v", y) //@hover("y", stringY)
}
}

View File

@ -1,32 +1,14 @@
-- switchStringY-definition --
godef/a/f.go:8:9-10: defined here as ```go
var y string
-- intY-hover --
```go
var y int
```
-- switchStringY-definition-json --
{
"span": {
"uri": "file://godef/a/f.go",
"start": {
"line": 8,
"column": 9,
"offset": 76
},
"end": {
"line": 8,
"column": 10,
"offset": 77
}
},
"description": "```go\nvar y string\n```"
}
-- switchStringY-hover --
-- stringY-hover --
```go
var y string
```
-- switchY-definition --
godef/a/f.go:8:9-10: defined here as ```go
var y int
var y interface{}
```
-- switchY-definition-json --
{
@ -43,10 +25,10 @@ var y int
"offset": 77
}
},
"description": "```go\nvar y int\n```"
"description": "```go\nvar y interface{}\n```"
}
-- switchY-hover --
```go
var y int
var y interface{}
```

View File

@ -19,3 +19,13 @@ godef(bMember, Member)
godef(bVar, Other)
godef(bFunc, Things)
*/
func _() {
var x interface{} //@mark(eInterface, "interface{}")
switch x := x.(type) { //@hover("x", eInterface)
case string: //@mark(eString, "string")
fmt.Println(x) //@hover("x", eString)
case int: //@mark(eInt, "int")
fmt.Println(x) //@hover("x", eInt)
}
}

View File

@ -130,3 +130,15 @@ func a.Things(val []string) []a.Thing
```
[`a.Things` on pkg.go.dev](https://pkg.go.dev/golang.org/x/tools/internal/lsp/godef/a#Things)
-- eInt-hover --
```go
var x int
```
-- eInterface-hover --
```go
var x interface{}
```
-- eString-hover --
```go
var x string
```

View File

@ -13,7 +13,7 @@ FormatCount = 6
ImportCount = 8
SuggestedFixCount = 31
FunctionExtractionCount = 5
DefinitionsCount = 53
DefinitionsCount = 56
TypeDefinitionsCount = 2
HighlightsCount = 69
ReferencesCount = 11