internal/lsp/source: canonicalize objects in reference/rename requests
With generics, instantiated object may have differing pointer
identities. Fix references/rename requests for instantiated
methods/fields by using a canonical object identity of (pos, pkg, name).
Fixes golang/go#51672
Change-Id: I0021ca562b8a74dadb616cf6864cb0bdd0165cc3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/392480
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/lsp/source/references.go b/internal/lsp/source/references.go
index 993b9f8..5d3eac3 100644
--- a/internal/lsp/source/references.go
+++ b/internal/lsp/source/references.go
@@ -109,10 +109,13 @@
searchPkgs = append(searchPkgs, qo.pkg)
for _, pkg := range searchPkgs {
for ident, obj := range pkg.GetTypesInfo().Uses {
- if obj != qo.obj {
- // If ident is not a use of qo.obj, skip it, with one exception: uses
- // of an embedded field can be considered references of the embedded
- // type name.
+ // For instantiated objects (as in methods or fields on instantiated
+ // types), we may not have pointer-identical objects but still want to
+ // consider them references.
+ if !equalOrigin(obj, qo.obj) {
+ // If ident is not a use of qo.obj, skip it, with one exception:
+ // uses of an embedded field can be considered references of the
+ // embedded type name
if !includeEmbeddedRefs {
continue
}
@@ -167,6 +170,13 @@
return references, 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()
+}
+
// interfaceReferences returns the references to the interfaces implemented by
// the type or method at the given position.
func interfaceReferences(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position) ([]*ReferenceInfo, error) {
diff --git a/internal/lsp/testdata/rename/generics/embedded.go b/internal/lsp/testdata/rename/generics/embedded.go
new file mode 100644
index 0000000..b44bab8
--- /dev/null
+++ b/internal/lsp/testdata/rename/generics/embedded.go
@@ -0,0 +1,10 @@
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type foo[P any] int //@rename("foo","bar")
+
+var x struct{ foo[int] }
+
+var _ = x.foo
diff --git a/internal/lsp/testdata/rename/generics/embedded.go.golden b/internal/lsp/testdata/rename/generics/embedded.go.golden
new file mode 100644
index 0000000..faa9afb
--- /dev/null
+++ b/internal/lsp/testdata/rename/generics/embedded.go.golden
@@ -0,0 +1,12 @@
+-- bar-rename --
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type bar[P any] int //@rename("foo","bar")
+
+var x struct{ bar[int] }
+
+var _ = x.bar
+
diff --git a/internal/lsp/testdata/rename/generics/generics.go b/internal/lsp/testdata/rename/generics/generics.go
new file mode 100644
index 0000000..977589c
--- /dev/null
+++ b/internal/lsp/testdata/rename/generics/generics.go
@@ -0,0 +1,25 @@
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type G[P any] struct {
+ F int
+}
+
+func (G[_]) M() {}
+
+func F[P any](P) {
+ var p P //@rename("P", "Q")
+ _ = p
+}
+
+func _() {
+ var x G[int] //@rename("G", "H")
+ _ = x.F //@rename("F", "K")
+ x.M() //@rename("M", "N")
+
+ var y G[string]
+ _ = y.F
+ y.M()
+}
diff --git a/internal/lsp/testdata/rename/generics/generics.go.golden b/internal/lsp/testdata/rename/generics/generics.go.golden
new file mode 100644
index 0000000..b46a083
--- /dev/null
+++ b/internal/lsp/testdata/rename/generics/generics.go.golden
@@ -0,0 +1,108 @@
+-- Q-rename --
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type G[P any] struct {
+ F int
+}
+
+func (G[_]) M() {}
+
+func F[Q any](Q) {
+ var p Q //@rename("P", "Q")
+ _ = p
+}
+
+func _() {
+ var x G[int] //@rename("G", "H")
+ _ = x.F //@rename("F", "K")
+ x.M() //@rename("M", "N")
+
+ var y G[string]
+ _ = y.F
+ y.M()
+}
+
+-- H-rename --
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type H[P any] struct {
+ F int
+}
+
+func (H[_]) M() {}
+
+func F[P any](P) {
+ var p P //@rename("P", "Q")
+ _ = p
+}
+
+func _() {
+ var x H[int] //@rename("G", "H")
+ _ = x.F //@rename("F", "K")
+ x.M() //@rename("M", "N")
+
+ var y H[string]
+ _ = y.F
+ y.M()
+}
+
+-- K-rename --
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type G[P any] struct {
+ K int
+}
+
+func (G[_]) M() {}
+
+func F[P any](P) {
+ var p P //@rename("P", "Q")
+ _ = p
+}
+
+func _() {
+ var x G[int] //@rename("G", "H")
+ _ = x.K //@rename("F", "K")
+ x.M() //@rename("M", "N")
+
+ var y G[string]
+ _ = y.K
+ y.M()
+}
+
+-- N-rename --
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type G[P any] struct {
+ F int
+}
+
+func (G[_]) N() {}
+
+func F[P any](P) {
+ var p P //@rename("P", "Q")
+ _ = p
+}
+
+func _() {
+ var x G[int] //@rename("G", "H")
+ _ = x.F //@rename("F", "K")
+ x.N() //@rename("M", "N")
+
+ var y G[string]
+ _ = y.F
+ y.N()
+}
+
diff --git a/internal/lsp/testdata/rename/generics/unions.go b/internal/lsp/testdata/rename/generics/unions.go
new file mode 100644
index 0000000..c737b5c
--- /dev/null
+++ b/internal/lsp/testdata/rename/generics/unions.go
@@ -0,0 +1,10 @@
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type T string //@rename("T", "R")
+
+type C interface {
+ T | ~int //@rename("T", "S")
+}
diff --git a/internal/lsp/testdata/rename/generics/unions.go.golden b/internal/lsp/testdata/rename/generics/unions.go.golden
new file mode 100644
index 0000000..4632896
--- /dev/null
+++ b/internal/lsp/testdata/rename/generics/unions.go.golden
@@ -0,0 +1,24 @@
+-- R-rename --
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type R string //@rename("T", "R")
+
+type C interface {
+ R | ~int //@rename("T", "S")
+}
+
+-- S-rename --
+//go:build go1.18
+// +build go1.18
+
+package generics
+
+type S string //@rename("T", "R")
+
+type C interface {
+ S | ~int //@rename("T", "S")
+}
+
diff --git a/internal/lsp/testdata/summary_go1.18.txt.golden b/internal/lsp/testdata/summary_go1.18.txt.golden
index 6bb0671..284ef64 100644
--- a/internal/lsp/testdata/summary_go1.18.txt.golden
+++ b/internal/lsp/testdata/summary_go1.18.txt.golden
@@ -20,7 +20,7 @@
TypeDefinitionsCount = 18
HighlightsCount = 69
ReferencesCount = 27
-RenamesCount = 41
+RenamesCount = 48
PrepareRenamesCount = 7
SymbolsCount = 5
WorkspaceSymbolsCount = 20