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 +}