gopls/internal/golang: normalize instantiated fields before rename When a rename is initiated at a field selection on an instantiated generic type, go/types reports a synthetic *types.Var. The rename operation consequently updates other instantiated uses but misses the original field declaration. Normalize field targets to their origin before performing the rename. This ensures that the declaration, selector uses, and keyed composite literals are updated together. Add a marker test for a rename initiated at an instantiated field selection. Fixes golang/go#80542 Change-Id: I8cdd107e6f4de584879f896ebf27d3c823b87473 Reviewed-on: https://go-review.googlesource.com/c/tools/+/804901 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Madeline Kalil <mkalil@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/golang/rename.go b/gopls/internal/golang/rename.go index 9e04f8d..b093326 100644 --- a/gopls/internal/golang/rename.go +++ b/gopls/internal/golang/rename.go
@@ -611,6 +611,15 @@ targets = []objectAt{{obj, cur}} } + // A field selected from an instantiated generic type is represented by a + // synthetic *types.Var. Normalize it to the declared field so that a rename + // initiated at the selection updates the declaration too (golang/go#80542). + for i := range targets { + if field, ok := targets[i].obj.(*types.Var); ok && field.IsField() { + targets[i].obj = field.Origin() + } + } + // Pick a representative object arbitrarily. // (All share the same name, pos, and kind.) obj, node := targets[0].obj, targets[0].cur.Node() @@ -654,8 +663,6 @@ case *types.Func: obj = obj0.Origin() case *types.Var: - // TODO(adonovan): do vars need the origin treatment too? (issue #58462) - // Function parameter and result vars that are (unusually) // capitalized are technically exported, even though they // cannot be referenced, because they may affect downstream
diff --git a/gopls/internal/test/marker/testdata/rename/issue80542.txt b/gopls/internal/test/marker/testdata/rename/issue80542.txt new file mode 100644 index 0000000..ddf7ac5 --- /dev/null +++ b/gopls/internal/test/marker/testdata/rename/issue80542.txt
@@ -0,0 +1,63 @@ +This test verifies that a rename of a field selected from an instantiated +generic type updates the field declaration, including across packages. + +-- flags -- +-ignore_extra_diags + +-- go.mod -- +module example.com + +go 1.18 + +-- a.go -- +package a + +type box[T any] struct { + value T +} + +func (b box[T]) use() { + _ = b.value //@rename("value", "renamed", valueToRenamed) +} + +var _ = box[int]{value: 1} + +-- lib/lib.go -- +package lib + +type Box[T any] struct { + Value T +} + +-- use/use.go -- +package use + +import "example.com/lib" + +func use(b lib.Box[int]) { + _ = b.Value //@rename("Value", "Renamed", crossPackage) +} + +var _ = lib.Box[int]{Value: 1} + +-- @valueToRenamed/a.go -- +@@ -4 +4 @@ +- value T ++ renamed T +@@ -8 +8 @@ +- _ = b.value //@rename("value", "renamed", valueToRenamed) ++ _ = b.renamed //@rename("value", "renamed", valueToRenamed) +@@ -11 +11 @@ +-var _ = box[int]{value: 1} ++var _ = box[int]{renamed: 1} +-- @crossPackage/lib/lib.go -- +@@ -4 +4 @@ +- Value T ++ Renamed T +-- @crossPackage/use/use.go -- +@@ -6 +6 @@ +- _ = b.Value //@rename("Value", "Renamed", crossPackage) ++ _ = b.Renamed //@rename("Value", "Renamed", crossPackage) +@@ -9 +9 @@ +-var _ = lib.Box[int]{Value: 1} ++var _ = lib.Box[int]{Renamed: 1}