diff --git a/refactor/rename/rename.go b/refactor/rename/rename.go index 40ca480fc05..3e9f797c2ff 100644 --- a/refactor/rename/rename.go +++ b/refactor/rename/rename.go @@ -25,6 +25,7 @@ import ( "os" "os/exec" "path" + "regexp" "sort" "strconv" "strings" @@ -163,7 +164,7 @@ type renamer struct { iprog *loader.Program objsToUpdate map[types.Object]bool hadConflicts bool - to string + from, to string satisfyConstraints map[satisfy.Constraint]bool packages map[*types.Package]*loader.PackageInfo // subset of iprog.AllPackages to inspect msets typeutil.MethodSetCache @@ -314,6 +315,7 @@ func Main(ctxt *build.Context, offsetFlag, fromFlag, to string) error { r := renamer{ iprog: iprog, objsToUpdate: make(map[types.Object]bool), + from: spec.fromName, to: to, packages: make(map[*types.Package]*loader.PackageInfo), } @@ -454,6 +456,8 @@ func (r *renamer) update() error { // token.File captures this distinction; filename does not. var nidents int var filesToUpdate = make(map[*token.File]bool) + + docRegexp := regexp.MustCompile(`\b` + r.from + `\b`) for _, info := range r.packages { // Mutate the ASTs and note the filenames. for id, obj := range info.Defs { @@ -461,8 +465,15 @@ func (r *renamer) update() error { nidents++ id.Name = r.to filesToUpdate[r.iprog.Fset.File(id.Pos())] = true + // Perform the rename in doc comments too. + if doc := r.docComment(id); doc != nil { + for _, comment := range doc.List { + comment.Text = docRegexp.ReplaceAllString(comment.Text, r.to) + } + } } } + for id, obj := range info.Uses { if r.objsToUpdate[obj] { nidents++ @@ -513,6 +524,35 @@ func (r *renamer) update() error { return nil } +// docComment returns the doc for an identifier. +func (r *renamer) docComment(id *ast.Ident) *ast.CommentGroup { + _, nodes, _ := r.iprog.PathEnclosingInterval(id.Pos(), id.End()) + for _, node := range nodes { + switch decl := node.(type) { + case *ast.FuncDecl: + return decl.Doc + case *ast.Field: + return decl.Doc + case *ast.GenDecl: + return decl.Doc + // For {Type,Value}Spec, if the doc on the spec is absent, + // search for the enclosing GenDecl + case *ast.TypeSpec: + if decl.Doc != nil { + return decl.Doc + } + case *ast.ValueSpec: + if decl.Doc != nil { + return decl.Doc + } + case *ast.Ident: + default: + return nil + } + } + return nil +} + func plural(n int) string { if n != 1 { return "s" diff --git a/refactor/rename/rename_test.go b/refactor/rename/rename_test.go index 1722f7cd1ce..a02db8f9aa0 100644 --- a/refactor/rename/rename_test.go +++ b/refactor/rename/rename_test.go @@ -521,6 +521,138 @@ var _ foo.U "/go/src/foo/0.go": `package foo type U int +`, + }, + }, + // Rename package-level func plus doc + { + ctxt: main(`package main + +// Foo is a no-op. +// Calling Foo does nothing. +func Foo() { +} +`), + from: "main.Foo", to: "FooBar", + want: map[string]string{ + "/go/src/main/0.go": `package main + +// FooBar is a no-op. +// Calling FooBar does nothing. +func FooBar() { +} +`, + }, + }, + // Rename method plus doc + { + ctxt: main(`package main + +type Foo struct{} + +// Bar does nothing. +func (Foo) Bar() { +} +`), + from: "main.Foo.Bar", to: "Baz", + want: map[string]string{ + "/go/src/main/0.go": `package main + +type Foo struct{} + +// Baz does nothing. +func (Foo) Baz() { +} +`, + }, + }, + // Rename type spec plus doc + { + ctxt: main(`package main + +type ( + // Test but not Testing. + Test struct{} +) +`), + from: "main.Test", to: "Type", + want: map[string]string{ + "/go/src/main/0.go": `package main + +type ( + // Type but not Testing. + Type struct{} +) +`, + }, + }, + // Rename type in gen decl plus doc + { + ctxt: main(`package main + +// T is a test type. +type T struct{} +`), + from: "main.T", to: "Type", + want: map[string]string{ + "/go/src/main/0.go": `package main + +// Type is a test type. +type Type struct{} +`, + }, + }, + // Rename value spec with doc + { + ctxt: main(`package main + +const ( + // C is the speed of light. + C = 2.998e8 +) +`), + from: "main.C", to: "Lightspeed", + want: map[string]string{ + "/go/src/main/0.go": `package main + +const ( + // Lightspeed is the speed of light. + Lightspeed = 2.998e8 +) +`, + }, + }, + // Rename value inside gen decl with doc + { + ctxt: main(`package main + +var out *string +`), + from: "main.out", to: "discard", + want: map[string]string{ + "/go/src/main/0.go": `package main + +var discard *string +`, + }, + }, + // Rename field plus doc + { + ctxt: main(`package main + +type Struct struct { + // Field is a struct field. + Field string +} +`), + from: "main.Struct.Field", to: "Foo", + want: map[string]string{ + "/go/src/main/0.go": `package main + +type Struct struct { + // Foo is a struct field. + Foo string +} `, }, },