From fe5037164af22f8d3c9056e8c3b86acc1973dccf Mon Sep 17 00:00:00 2001 From: Shoshin Nikita Date: Fri, 16 Apr 2021 12:50:41 +0000 Subject: [PATCH] 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 Trust: Rebecca Stambler Trust: Robert Findley Run-TryBot: Rebecca Stambler gopls-CI: kokoro TryBot-Result: Go Bot --- .../regtest/completion/completion_test.go | 42 +++++++++++++++---- internal/lsp/source/util.go | 11 ++++- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/gopls/internal/regtest/completion/completion_test.go b/gopls/internal/regtest/completion/completion_test.go index 82f0757772..417a95f15f 100644 --- a/gopls/internal/regtest/completion/completion_test.go +++ b/gopls/internal/regtest/completion/completion_test.go @@ -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) + } } }) } diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go index d242c34dd5..69ddb17684 100644 --- a/internal/lsp/source/util.go +++ b/internal/lsp/source/util.go @@ -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{}{} } }