@@ -69,8 +69,10 @@ import (
69
69
"golang.org/x/tools/gopls/internal/protocol"
70
70
goplsastutil "golang.org/x/tools/gopls/internal/util/astutil"
71
71
"golang.org/x/tools/gopls/internal/util/bug"
72
+ "golang.org/x/tools/gopls/internal/util/moreiters"
72
73
"golang.org/x/tools/gopls/internal/util/safetoken"
73
74
internalastutil "golang.org/x/tools/internal/astutil"
75
+ "golang.org/x/tools/internal/astutil/cursor"
74
76
"golang.org/x/tools/internal/diff"
75
77
"golang.org/x/tools/internal/event"
76
78
"golang.org/x/tools/internal/typesinternal"
@@ -482,6 +484,7 @@ func renameOrdinary(ctx context.Context, snapshot *cache.Snapshot, f file.Handle
482
484
// computes the union across all variants.)
483
485
var targets map [types.Object ]ast.Node
484
486
var pkg * cache.Package
487
+ var cur cursor.Cursor // of selected Ident or ImportSpec
485
488
{
486
489
mps , err := snapshot .MetadataForFile (ctx , f .URI ())
487
490
if err != nil {
@@ -505,6 +508,11 @@ func renameOrdinary(ctx context.Context, snapshot *cache.Snapshot, f file.Handle
505
508
if err != nil {
506
509
return nil , err
507
510
}
511
+ var ok bool
512
+ cur , ok = pgf .Cursor .FindPos (pos , pos )
513
+ if ! ok {
514
+ return nil , fmt .Errorf ("can't find cursor for selection" )
515
+ }
508
516
objects , _ , err := objectsAt (pkg .TypesInfo (), pgf .File , pos )
509
517
if err != nil {
510
518
return nil , err
@@ -571,8 +579,34 @@ func renameOrdinary(ctx context.Context, snapshot *cache.Snapshot, f file.Handle
571
579
for obj := range targets {
572
580
objects = append (objects , obj )
573
581
}
582
+
574
583
editMap , _ , err := renameObjects (newName , pkg , objects ... )
575
- return editMap , err
584
+ if err != nil {
585
+ return nil , err
586
+ }
587
+
588
+ // If target is a receiver, also rename receivers of
589
+ // other methods of the same type that don't already
590
+ // have the target name. Quietly discard edits from
591
+ // any that can't be renamed.
592
+ //
593
+ // TODO(adonovan): UX question: require that the
594
+ // selection be the declaration of the receiver before
595
+ // we broaden the renaming?
596
+ if curDecl , ok := moreiters .First (cur .Enclosing ((* ast .FuncDecl )(nil ))); ok {
597
+ decl := curDecl .Node ().(* ast.FuncDecl ) // enclosing func
598
+ if decl .Recv != nil &&
599
+ len (decl .Recv .List ) > 0 &&
600
+ len (decl .Recv .List [0 ].Names ) > 0 {
601
+ recv := pkg .TypesInfo ().Defs [decl .Recv .List [0 ].Names [0 ]]
602
+ if recv == obj {
603
+ // TODO(adonovan): simplify the above 7 lines to
604
+ // to "if obj.(*Var).Kind==Recv" in go1.25.
605
+ renameReceivers (pkg , recv .(* types.Var ), newName , editMap )
606
+ }
607
+ }
608
+ }
609
+ return editMap , nil
576
610
}
577
611
578
612
// Exported: search globally.
@@ -632,6 +666,39 @@ func renameOrdinary(ctx context.Context, snapshot *cache.Snapshot, f file.Handle
632
666
return renameExported (pkgs , declPkgPath , declObjPath , newName )
633
667
}
634
668
669
+ // renameReceivers renames all receivers of methods of the same named
670
+ // type as recv. The edits of each successful renaming are added to
671
+ // editMap; the failed ones are quietly discarded.
672
+ func renameReceivers (pkg * cache.Package , recv * types.Var , newName string , editMap map [protocol.DocumentURI ][]diff.Edit ) {
673
+ _ , named := typesinternal .ReceiverNamed (recv )
674
+ if named == nil {
675
+ return
676
+ }
677
+
678
+ // Find receivers of other methods of the same named type.
679
+ for m := range named .Origin ().Methods () {
680
+ recv2 := m .Signature ().Recv ()
681
+ if recv2 == recv {
682
+ continue // don't re-rename original receiver
683
+ }
684
+ if recv2 .Name () == newName {
685
+ continue // no renaming needed
686
+ }
687
+ editMap2 , _ , err := renameObjects (newName , pkg , recv2 )
688
+ if err != nil {
689
+ continue // ignore secondary failures
690
+ }
691
+
692
+ // Since all methods (and their comments)
693
+ // are disjoint, and don't affect imports,
694
+ // we can safely assume that all edits are
695
+ // nonconflicting and disjoint.
696
+ for uri , edits := range editMap2 {
697
+ editMap [uri ] = append (editMap [uri ], edits ... )
698
+ }
699
+ }
700
+ }
701
+
635
702
// typeCheckReverseDependencies returns the type-checked packages for
636
703
// the reverse dependencies of all packages variants containing
637
704
// file declURI. The packages are in some topological order.
0 commit comments