mirror of https://github.com/golang/go.git
internal/lsp/source: support typeDefinition for function/method's return values
Support typeDefinition for functions/methods that have only one return value of a named type. The total number of return values doesn't matter. Examples: * func foo() X * func foo() (X, bool, int) * func foo() (*float64, *X, error) Fixes golang/go#38589 Change-Id: I8840d667437300fd1250a13630e12a36601f0a60 GitHub-Last-Rev: 581d810af959f8b2c0bf62a22e5725f32947f5e4 GitHub-Pull-Request: golang/tools#311 Reviewed-on: https://go-review.googlesource.com/c/tools/+/313093 Reviewed-by: Rebecca Stambler <rstambler@golang.org> Trust: Rebecca Stambler <rstambler@golang.org> Trust: Peter Weinberger <pjw@google.com> Run-TryBot: Rebecca Stambler <rstambler@golang.org> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
parent
08a4f343fb
commit
d1ea2c78a5
|
|
@ -179,3 +179,58 @@ func main() {}
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoToTypeDefinition_Issue38589(t *testing.T) {
|
||||
const mod = `
|
||||
-- go.mod --
|
||||
module mod.com
|
||||
|
||||
go 1.12
|
||||
-- main.go --
|
||||
package main
|
||||
|
||||
type Int int
|
||||
|
||||
type Struct struct{}
|
||||
|
||||
func F1() {}
|
||||
func F2() (int, error) { return 0, nil }
|
||||
func F3() (**Struct, bool, *Int, error) { return nil, false, nil, nil }
|
||||
func F4() (**Struct, bool, *float64, error) { return nil, false, nil, nil }
|
||||
|
||||
func main() {}
|
||||
`
|
||||
|
||||
for _, tt := range []struct {
|
||||
re string
|
||||
wantError bool
|
||||
wantTypeRe string
|
||||
}{
|
||||
{re: `F1`, wantError: true},
|
||||
{re: `F2`, wantError: true},
|
||||
{re: `F3`, wantError: true},
|
||||
{re: `F4`, wantError: false, wantTypeRe: `type (Struct)`},
|
||||
} {
|
||||
t.Run(tt.re, func(t *testing.T) {
|
||||
Run(t, mod, func(t *testing.T, env *Env) {
|
||||
env.OpenFile("main.go")
|
||||
|
||||
_, pos, err := env.Editor.GoToTypeDefinition(env.Ctx, "main.go", env.RegexpSearch("main.go", tt.re))
|
||||
if tt.wantError {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil error, got %s", err)
|
||||
}
|
||||
|
||||
typePos := env.RegexpSearch("main.go", tt.wantTypeRe)
|
||||
if pos != typePos {
|
||||
t.Errorf("invalid pos: want %+v, got %+v", typePos, pos)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -707,11 +707,35 @@ func (e *Editor) GoToDefinition(ctx context.Context, path string, pos Pos) (stri
|
|||
if err != nil {
|
||||
return "", Pos{}, errors.Errorf("definition: %w", err)
|
||||
}
|
||||
if len(resp) == 0 {
|
||||
return e.extractFirstPathAndPos(ctx, resp)
|
||||
}
|
||||
|
||||
// GoToTypeDefinition jumps to the type definition of the symbol at the given position
|
||||
// in an open buffer.
|
||||
func (e *Editor) GoToTypeDefinition(ctx context.Context, path string, pos Pos) (string, Pos, error) {
|
||||
if err := e.checkBufferPosition(path, pos); err != nil {
|
||||
return "", Pos{}, err
|
||||
}
|
||||
params := &protocol.TypeDefinitionParams{}
|
||||
params.TextDocument.URI = e.sandbox.Workdir.URI(path)
|
||||
params.Position = pos.ToProtocolPosition()
|
||||
|
||||
resp, err := e.Server.TypeDefinition(ctx, params)
|
||||
if err != nil {
|
||||
return "", Pos{}, errors.Errorf("type definition: %w", err)
|
||||
}
|
||||
return e.extractFirstPathAndPos(ctx, resp)
|
||||
}
|
||||
|
||||
// extractFirstPathAndPos returns the path and the position of the first location.
|
||||
// It opens the file if needed.
|
||||
func (e *Editor) extractFirstPathAndPos(ctx context.Context, locs []protocol.Location) (string, Pos, error) {
|
||||
if len(locs) == 0 {
|
||||
return "", Pos{}, nil
|
||||
}
|
||||
newPath := e.sandbox.Workdir.URIToPath(resp[0].URI)
|
||||
newPos := fromProtocolPosition(resp[0].Range.Start)
|
||||
|
||||
newPath := e.sandbox.Workdir.URIToPath(locs[0].URI)
|
||||
newPos := fromProtocolPosition(locs[0].Range.Start)
|
||||
if !e.HasBuffer(newPath) {
|
||||
if err := e.OpenFile(ctx, newPath); err != nil {
|
||||
return "", Pos{}, errors.Errorf("OpenFile: %w", err)
|
||||
|
|
|
|||
|
|
@ -329,6 +329,26 @@ func typeToObject(typ types.Type) types.Object {
|
|||
return typeToObject(typ.Elem())
|
||||
case *types.Chan:
|
||||
return typeToObject(typ.Elem())
|
||||
case *types.Signature:
|
||||
// Try to find a return value of a named type. If there's only one
|
||||
// such value, jump to its type definition.
|
||||
var res types.Object
|
||||
|
||||
results := typ.Results()
|
||||
for i := 0; i < results.Len(); i++ {
|
||||
obj := typeToObject(results.At(i).Type())
|
||||
if obj == nil || hasErrorType(obj) {
|
||||
// Skip builtins.
|
||||
continue
|
||||
}
|
||||
if res != nil {
|
||||
// The function/method must have only one return value of a named type.
|
||||
return nil
|
||||
}
|
||||
|
||||
res = obj
|
||||
}
|
||||
return res
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ SemanticTokenCount = 3
|
|||
SuggestedFixCount = 40
|
||||
FunctionExtractionCount = 18
|
||||
DefinitionsCount = 95
|
||||
TypeDefinitionsCount = 10
|
||||
TypeDefinitionsCount = 18
|
||||
HighlightsCount = 69
|
||||
ReferencesCount = 25
|
||||
RenamesCount = 33
|
||||
|
|
|
|||
|
|
@ -36,3 +36,30 @@ func _() {
|
|||
s.x.xx.field1 //@typdef("field1", Struct)
|
||||
s.x.xx.field2 //@typdef("field2", Int)
|
||||
}
|
||||
|
||||
func F1() Int { return 0 }
|
||||
func F2() (Int, float64) { return 0, 0 }
|
||||
func F3() (Struct, int, bool, error) { return Struct{}, 0, false, nil }
|
||||
func F4() (**int, Int, bool, *error) { return nil, Struct{}, false, nil }
|
||||
func F5() (int, float64, error, Struct) { return 0, 0, nil, Struct{} }
|
||||
func F6() (int, float64, ***Struct, error) { return 0, 0, nil, nil }
|
||||
|
||||
func _() {
|
||||
F1() //@typdef("F1", Int)
|
||||
F2() //@typdef("F2", Int)
|
||||
F3() //@typdef("F3", Struct)
|
||||
F4() //@typdef("F4", Int)
|
||||
F5() //@typdef("F5", Struct)
|
||||
F6() //@typdef("F6", Struct)
|
||||
|
||||
f := func() Int { return 0 }
|
||||
f() //@typdef("f", Int)
|
||||
}
|
||||
|
||||
// https://github.com/golang/go/issues/38589#issuecomment-620350922
|
||||
func _() {
|
||||
type myFunc func(int) Int //@item(myFunc, "myFunc", "func", "type")
|
||||
|
||||
var foo myFunc
|
||||
bar := foo() //@typdef("foo", myFunc)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue