Skip to content

Commit 4fe2c29

Browse files
committed
refactor/rename: fix renaming of aliases
This change gets the renaming of aliases right when they are materialized by GODEBUG=gotypesaliases=1. (Obviously there's no way to get it right without that feature.) And it adds a (conditional) test. Updates golang/go#65294 Change-Id: Ie242e3a0a9d012218067bdc61a901f6618fecbb5 Reviewed-on: https://go-review.googlesource.com/c/tools/+/567842 Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 71acab9 commit 4fe2c29

File tree

5 files changed

+100
-31
lines changed

5 files changed

+100
-31
lines changed

refactor/rename/check.go

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"go/types"
1414

1515
"golang.org/x/tools/go/loader"
16-
"golang.org/x/tools/internal/aliases"
1716
"golang.org/x/tools/internal/typeparams"
1817
"golang.org/x/tools/internal/typesinternal"
1918
"golang.org/x/tools/refactor/satisfy"
@@ -438,8 +437,8 @@ func (r *renamer) checkStructField(from *types.Var) {
438437
}
439438
i++
440439
}
441-
if spec, ok := path[i].(*ast.TypeSpec); ok {
442-
// This struct is also a named type.
440+
if spec, ok := path[i].(*ast.TypeSpec); ok && !spec.Assign.IsValid() {
441+
// This struct is also a defined type.
443442
// We must check for direct (non-promoted) field/field
444443
// and method/field conflicts.
445444
named := info.Defs[spec.Name].Type()
@@ -452,7 +451,7 @@ func (r *renamer) checkStructField(from *types.Var) {
452451
return // skip checkSelections to avoid redundant errors
453452
}
454453
} else {
455-
// This struct is not a named type.
454+
// This struct is not a defined type. (It may be an alias.)
456455
// We need only check for direct (non-promoted) field/field conflicts.
457456
T := info.Types[tStruct].Type.Underlying().(*types.Struct)
458457
for i := 0; i < T.NumFields(); i++ {
@@ -470,10 +469,7 @@ func (r *renamer) checkStructField(from *types.Var) {
470469
// type T int // this and
471470
// var s struct {T} // this must change too.
472471
if from.Anonymous() {
473-
// hasTypeName = {Named,Alias,TypeParam}, though
474-
// a TypeParam cannot appear as an anonymous field.
475-
// TODO(adonovan): add test of aliases.
476-
type hasTypeName interface{ Obj() *types.TypeName }
472+
// A TypeParam cannot appear as an anonymous field.
477473
if t, ok := typesinternal.Unpointer(from.Type()).(hasTypeName); ok {
478474
r.check(t.Obj())
479475
}
@@ -483,6 +479,9 @@ func (r *renamer) checkStructField(from *types.Var) {
483479
r.checkSelections(from)
484480
}
485481

482+
// hasTypeName abstracts the named types, *types.{Named,Alias,TypeParam}.
483+
type hasTypeName interface{ Obj() *types.TypeName }
484+
486485
// checkSelections checks that all uses and selections that resolve to
487486
// the specified object would continue to do so after the renaming.
488487
func (r *renamer) checkSelections(from types.Object) {
@@ -596,7 +595,7 @@ func (r *renamer) checkMethod(from *types.Func) {
596595
// Check for conflict at point of declaration.
597596
// Check to ensure preservation of assignability requirements.
598597
R := recv(from).Type()
599-
if isInterface(R) {
598+
if types.IsInterface(R) {
600599
// Abstract method
601600

602601
// declaration
@@ -613,7 +612,7 @@ func (r *renamer) checkMethod(from *types.Func) {
613612
for _, info := range r.packages {
614613
// Start with named interface types (better errors)
615614
for _, obj := range info.Defs {
616-
if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) {
615+
if obj, ok := obj.(*types.TypeName); ok && types.IsInterface(obj.Type()) {
617616
f, _, _ := types.LookupFieldOrMethod(
618617
obj.Type(), false, from.Pkg(), from.Name())
619618
if f == nil {
@@ -685,7 +684,7 @@ func (r *renamer) checkMethod(from *types.Func) {
685684
// yields abstract method I.f. This can make error
686685
// messages less than obvious.
687686
//
688-
if !isInterface(key.RHS) {
687+
if !types.IsInterface(key.RHS) {
689688
// The logic below was derived from checkSelections.
690689

691690
rtosel := rmethods.Lookup(from.Pkg(), r.to)
@@ -760,7 +759,7 @@ func (r *renamer) checkMethod(from *types.Func) {
760759
//
761760
for key := range r.satisfy() {
762761
// key = (lhs, rhs) where lhs is always an interface.
763-
if isInterface(key.RHS) {
762+
if types.IsInterface(key.RHS) {
764763
continue
765764
}
766765
rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
@@ -782,7 +781,7 @@ func (r *renamer) checkMethod(from *types.Func) {
782781
var iface string
783782

784783
I := recv(imeth).Type()
785-
if named, ok := aliases.Unalias(I).(*types.Named); ok {
784+
if named, ok := I.(hasTypeName); ok {
786785
pos = named.Obj().Pos()
787786
iface = "interface " + named.Obj().Name()
788787
} else {
@@ -850,7 +849,3 @@ func someUse(info *loader.PackageInfo, obj types.Object) *ast.Ident {
850849
}
851850
return nil
852851
}
853-
854-
// -- Plundered from golang.org/x/tools/go/ssa -----------------
855-
856-
func isInterface(T types.Type) bool { return types.IsInterface(T) }

refactor/rename/rename.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ func Main(ctxt *build.Context, offsetFlag, fromFlag, to string) error {
323323
for _, obj := range fromObjects {
324324
if obj, ok := obj.(*types.Func); ok {
325325
recv := obj.Type().(*types.Signature).Recv()
326-
if recv != nil && isInterface(recv.Type().Underlying()) {
326+
if recv != nil && types.IsInterface(recv.Type()) {
327327
r.changeMethods = true
328328
break
329329
}

refactor/rename/rename_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ func TestRewrites(t *testing.T) {
467467
ctxt *build.Context // nil => use previous
468468
offset, from, to string // values of the -from/-offset and -to flags
469469
want map[string]string // contents of updated files
470+
alias bool // requires materialized aliases
470471
}{
471472
// Elimination of renaming import.
472473
{
@@ -764,6 +765,78 @@ type T2 int
764765
type U struct{ *T2 }
765766
766767
var _ = U{}.T2
768+
`,
769+
},
770+
},
771+
// Renaming of embedded field alias.
772+
{
773+
alias: true,
774+
ctxt: main(`package main
775+
776+
type T int
777+
type A = T
778+
type U struct{ A }
779+
780+
var _ = U{}.A
781+
var a A
782+
`),
783+
offset: "/go/src/main/0.go:#68", to: "A2", // A in "U{}.A"
784+
want: map[string]string{
785+
"/go/src/main/0.go": `package main
786+
787+
type T int
788+
type A2 = T
789+
type U struct{ A2 }
790+
791+
var _ = U{}.A2
792+
var a A2
793+
`,
794+
},
795+
},
796+
// Renaming of embedded field pointer to alias.
797+
{
798+
alias: true,
799+
ctxt: main(`package main
800+
801+
type T int
802+
type A = T
803+
type U struct{ *A }
804+
805+
var _ = U{}.A
806+
var a A
807+
`),
808+
offset: "/go/src/main/0.go:#69", to: "A2", // A in "U{}.A"
809+
want: map[string]string{
810+
"/go/src/main/0.go": `package main
811+
812+
type T int
813+
type A2 = T
814+
type U struct{ *A2 }
815+
816+
var _ = U{}.A2
817+
var a A2
818+
`,
819+
},
820+
},
821+
// Renaming of alias
822+
{
823+
ctxt: main(`package main
824+
825+
type A = int
826+
827+
func _() A {
828+
return A(0)
829+
}
830+
`),
831+
offset: "/go/src/main/0.go:#49", to: "A2", // A in "A(0)"
832+
want: map[string]string{
833+
"/go/src/main/0.go": `package main
834+
835+
type A2 = int
836+
837+
func _() A2 {
838+
return A2(0)
839+
}
767840
`,
768841
},
769842
},
@@ -1247,6 +1320,14 @@ func main() {
12471320
return nil
12481321
}
12491322

1323+
if !test.alias {
1324+
t.Setenv("GODEBUG", "gotypesalias=0")
1325+
} else if !strings.Contains(fmt.Sprint(build.Default.ReleaseTags), " go1.22") {
1326+
t.Skip("skipping test that requires materialized type aliases")
1327+
} else {
1328+
t.Setenv("GODEBUG", "gotypesalias=1")
1329+
}
1330+
12501331
err := Main(ctxt, test.offset, test.from, test.to)
12511332
var prefix string
12521333
if test.offset == "" {

refactor/rename/spec.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525

2626
"golang.org/x/tools/go/buildutil"
2727
"golang.org/x/tools/go/loader"
28-
"golang.org/x/tools/internal/aliases"
28+
"golang.org/x/tools/internal/typesinternal"
2929
)
3030

3131
// A spec specifies an entity to rename.
@@ -459,17 +459,14 @@ func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) {
459459
// search within named type.
460460
obj, _, _ := types.LookupFieldOrMethod(tName.Type(), true, info.Pkg, spec.typeMember)
461461
if obj == nil {
462-
return nil, fmt.Errorf("cannot find field or method %q of %s %s.%s",
463-
spec.typeMember, typeKind(tName.Type()), info.Pkg.Path(), tName.Name())
462+
return nil, fmt.Errorf("cannot find field or method %q of %s.%s",
463+
spec.typeMember, info.Pkg.Path(), tName.Name())
464464
}
465465

466466
if spec.searchFor == "" {
467467
// If it is an embedded field, return the type of the field.
468468
if v, ok := obj.(*types.Var); ok && v.Anonymous() {
469-
switch t := aliases.Unalias(v.Type()).(type) {
470-
case *types.Pointer:
471-
return []types.Object{aliases.Unalias(t.Elem()).(*types.Named).Obj()}, nil
472-
case *types.Named:
469+
if t, ok := typesinternal.Unpointer(v.Type()).(hasTypeName); ok {
473470
return []types.Object{t.Obj()}, nil
474471
}
475472
}
@@ -482,7 +479,7 @@ func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) {
482479
spec.searchFor, objectKind(obj), info.Pkg.Path(), tName.Name(),
483480
obj.Name())
484481
}
485-
if isInterface(tName.Type()) {
482+
if types.IsInterface(tName.Type()) {
486483
return nil, fmt.Errorf("cannot search for local name %q within abstract method (%s.%s).%s",
487484
spec.searchFor, info.Pkg.Path(), tName.Name(), searchFunc.Name())
488485
}

refactor/rename/util.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func objectKind(obj types.Object) string {
2222
switch obj := obj.(type) {
2323
case *types.PkgName:
2424
return "imported package name"
25-
case *types.TypeName:
25+
case *types.TypeName: // defined type | alias | type parameter
2626
return "type"
2727
case *types.Var:
2828
if obj.IsField() {
@@ -37,10 +37,6 @@ func objectKind(obj types.Object) string {
3737
return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
3838
}
3939

40-
func typeKind(T types.Type) string {
41-
return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(T.Underlying()).String(), "*types."))
42-
}
43-
4440
// NB: for renamings, blank is not considered valid.
4541
func isValidIdentifier(id string) bool {
4642
if id == "" || id == "_" {

0 commit comments

Comments
 (0)