From d7e01c038e9a8d61983cfca05447a19fae0f5cdb Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Thu, 5 May 2022 11:28:18 -0400 Subject: [PATCH] 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 Reviewed-by: Alan Donovan gopls-CI: kokoro Run-TryBot: Robert Findley --- internal/lsp/source/completion/util.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/lsp/source/completion/util.go b/internal/lsp/source/completion/util.go index 24d595c6fc..cd7849af26 100644 --- a/internal/lsp/source/completion/util.go +++ b/internal/lsp/source/completion/util.go @@ -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()) } }