internal/lsp/source/completion: use typeutil.Map for short-circuiting
While working on golang/go#52715, I discovered an infinite recursion in
gopls' completion logic: eachField assumes a finiteness of type pointers.
It is almost certainly a go/types bug that type-checked types expand
infinitely, but nevertheless we should use the more accurate
typeutil.Map for short-circuiting our search.
Change-Id: Ib1c7125e624f42882869acd4e0476e317d4da056
Reviewed-on: https://go-review.googlesource.com/c/tools/+/404335
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/source/completion/util.go b/internal/lsp/source/completion/util.go
index 24d595c..cd7849a 100644
--- a/internal/lsp/source/completion/util.go
+++ b/internal/lsp/source/completion/util.go
@@ -9,6 +9,7 @@
"go/token"
"go/types"
+ "golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/lsp/diff"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
@@ -33,12 +34,12 @@
// types.NewSelectionSet should do that for us.
// for termination on recursive types
- var seen map[*types.Struct]bool
+ var seen typeutil.Map
var visit func(T types.Type)
visit = func(T types.Type) {
if T, ok := source.Deref(T).Underlying().(*types.Struct); ok {
- if seen[T] {
+ if seen.At(T) != nil {
return
}
@@ -46,12 +47,7 @@
f := T.Field(i)
fn(f)
if f.Anonymous() {
- if seen == nil {
- // Lazily create "seen" since it is only needed for
- // embedded structs.
- seen = make(map[*types.Struct]bool)
- }
- seen[T] = true
+ seen.Set(T, true)
visit(f.Type())
}
}