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>
This commit is contained in:
Robert Findley 2022-05-05 11:28:18 -04:00
parent d299b94661
commit d7e01c038e
1 changed files with 4 additions and 8 deletions

View File

@ -9,6 +9,7 @@ import (
"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 @@ func eachField(T types.Type, fn func(*types.Var)) {
// 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 @@ func eachField(T types.Type, fn func(*types.Var)) {
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())
}
}