From 82861e0385a62eddb77541a9ebb0cc9fd9a53389 Mon Sep 17 00:00:00 2001 From: Rob Findley Date: Fri, 28 Jul 2023 11:09:36 -0400 Subject: [PATCH] gopls/internal/lsp/source: reinstate equalOrigin for references check This logic was dropped in CL 464902, but is necessary for correctly resolving references to variables. Fixes golang/go#61618 Change-Id: Iebe46339e5f6ac0230f7376e742d786bae2a7438 Reviewed-on: https://go-review.googlesource.com/c/tools/+/513780 Run-TryBot: Robert Findley gopls-CI: kokoro TryBot-Result: Gopher Robot Reviewed-by: Hyang-Ah Hana Kim --- gopls/internal/lsp/source/references.go | 16 ++++++-- .../marker/testdata/references/issue61618.txt | 39 +++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 gopls/internal/regtest/marker/testdata/references/issue61618.txt diff --git a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/references.go index 84193992cc6..3d923e44702 100644 --- a/gopls/internal/lsp/source/references.go +++ b/gopls/internal/lsp/source/references.go @@ -580,9 +580,12 @@ func localReferences(pkg Package, targets map[types.Object]bool, correspond bool // matches reports whether obj either is or corresponds to a target. // (Correspondence is defined as usual for interface methods.) matches := func(obj types.Object) bool { - if targets[obj] { - return true - } else if methodRecvs != nil && obj.Name() == methodName { + for target := range targets { + if equalOrigin(obj, target) { + return true + } + } + if methodRecvs != nil && obj.Name() == methodName { if orecv := effectiveReceiver(obj); orecv != nil { for _, mrecv := range methodRecvs { if concreteImplementsIntf(orecv, mrecv) { @@ -608,6 +611,13 @@ func localReferences(pkg Package, targets map[types.Object]bool, correspond bool return nil } +// equalOrigin reports whether obj1 and obj2 have equivalent origin object. +// This may be the case even if obj1 != obj2, if one or both of them is +// instantiated. +func equalOrigin(obj1, obj2 types.Object) bool { + return obj1.Pkg() == obj2.Pkg() && obj1.Pos() == obj2.Pos() && obj1.Name() == obj2.Name() +} + // effectiveReceiver returns the effective receiver type for method-set // comparisons for obj, if it is a method, or nil otherwise. func effectiveReceiver(obj types.Object) types.Type { diff --git a/gopls/internal/regtest/marker/testdata/references/issue61618.txt b/gopls/internal/regtest/marker/testdata/references/issue61618.txt new file mode 100644 index 00000000000..6027d448048 --- /dev/null +++ b/gopls/internal/regtest/marker/testdata/references/issue61618.txt @@ -0,0 +1,39 @@ +Regression test for 'references' bug golang/go#61618: +references to instantiated fields were missing. + +-- flags -- +-min_go=go1.18 + +-- go.mod -- +module example.com +go 1.18 + +-- a.go -- +package a + +// This file is adapted from the example in the issue. + +type builder[S ~[]F, F ~string] struct { + name string + elements S //@loc(def, "elements"), refs(def, def, assign, use) + elemData map[F][]ElemData[F] +} + +type ElemData[F ~string] struct { + Name F +} + +type BuilderImpl[S ~[]F, F ~string] struct{ builder[S, F] } + +func NewBuilderImpl[S ~[]F, F ~string](name string) *BuilderImpl[S, F] { + impl := &BuilderImpl[S,F]{ + builder[S, F]{ + name: name, + elements: S{}, //@loc(assign, "elements"), refs(assign, def, assign, use) + elemData: map[F][]ElemData[F]{}, + }, + } + + _ = impl.elements //@loc(use, "elements"), refs(use, def, assign, use) + return impl +}