Skip to content

Commit

Permalink
refactor/rename: perform renaming in doc comments
Browse files Browse the repository at this point in the history
Attempt to update doc comments when renaming an identifier.
This reduces the amount of manual steps that need to be taken
when using gorename.

All occurrences of the old identifier are updated in the doc.
The update is done  using a regex to ensure that we replace
whole word matches only.

Fixes golang/go#17994

Change-Id: I4265021b5b34cf7d70bf43ad6ceee74ec132f185
Reviewed-on: https://go-review.googlesource.com/33452
Reviewed-by: Alan Donovan <[email protected]>
  • Loading branch information
zmb3 authored and adonovan committed Nov 29, 2016
1 parent 955e2ae commit cbfb669
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 1 deletion.
42 changes: 41 additions & 1 deletion refactor/rename/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"os"
"os/exec"
"path"
"regexp"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
}
Expand Down Expand Up @@ -454,15 +456,24 @@ 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 {
if r.objsToUpdate[obj] {
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++
Expand Down Expand Up @@ -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"
Expand Down
132 changes: 132 additions & 0 deletions refactor/rename/rename_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
`,
},
},
Expand Down

0 comments on commit cbfb669

Please sign in to comment.