internal/lsp/source: fix Deref function for cyclic types

The previous fix (d1362d7) is not sufficient for all cyclic types.
This change updates Deref function to support more complex cases.
We use a map with underlying types to detect cycles.

Fixes golang/go#45510

Change-Id: I28f655a9c1d4f363cb7ae3f47db3e8567fe6e80a
GitHub-Last-Rev: 4c898741c0cf07cb765b759b1edf15e004fc1a0e
GitHub-Pull-Request: golang/tools#305
Reviewed-on: https://go-review.googlesource.com/c/tools/+/310311
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Rebecca Stambler <rstambler@golang.org>
Trust: Robert Findley <rfindley@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:
Shoshin Nikita 2021-04-16 12:50:41 +00:00 committed by Rebecca Stambler
parent cb5dc85ca1
commit fe5037164a
2 changed files with 42 additions and 11 deletions

View File

@ -393,21 +393,45 @@ func _() {
var bbbb1, bbbb2 b
var _ b = bbbb
}
type (
c *d
d *e
e **c
)
func _() {
var (
xxxxc c
xxxxd d
xxxxe e
)
var _ c = xxxx
var _ d = xxxx
var _ e = xxxx
}
`
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("main.go")
completions := env.Completion("main.go", env.RegexpSearch("main.go", `var _ a = aaaa()`))
diff := compareCompletionResults([]string{"aaaa1", "aaaa2"}, completions.Items)
if diff != "" {
t.Fatal(diff)
tests := []struct {
re string
want []string
}{
{`var _ a = aaaa()`, []string{"aaaa1", "aaaa2"}},
{`var _ b = bbbb()`, []string{"bbbb1", "bbbb2"}},
{`var _ c = xxxx()`, []string{"***xxxxd", "**xxxxe", "xxxxc"}},
{`var _ d = xxxx()`, []string{"***xxxxe", "*xxxxc", "xxxxd"}},
{`var _ e = xxxx()`, []string{"**xxxxc", "*xxxxd", "xxxxe"}},
}
completions = env.Completion("main.go", env.RegexpSearch("main.go", `var _ b = bbbb()`))
diff = compareCompletionResults([]string{"bbbb1", "bbbb2"}, completions.Items)
if diff != "" {
t.Fatal(diff)
for _, tt := range tests {
completions := env.Completion("main.go", env.RegexpSearch("main.go", tt.re))
diff := compareCompletionResults(tt.want, completions.Items)
if diff != "" {
t.Errorf("%s: %s", tt.re, diff)
}
}
})
}

View File

@ -222,17 +222,24 @@ func FormatNode(fset *token.FileSet, n ast.Node) string {
// Deref returns a pointer's element type, traversing as many levels as needed.
// Otherwise it returns typ.
//
// It can return a pointer type if the type refers to itself (see golang/go#45510).
// It can return a pointer type for cyclic types (see golang/go#45510).
func Deref(typ types.Type) types.Type {
var seen map[types.Type]struct{}
for {
p, ok := typ.Underlying().(*types.Pointer)
if !ok {
return typ
}
if typ == p.Elem() {
if _, ok := seen[p.Elem()]; ok {
return typ
}
typ = p.Elem()
if seen == nil {
seen = make(map[types.Type]struct{})
}
seen[typ] = struct{}{}
}
}