internal/lsp: rename identifiers in test packages

Support renaming of identifiers in test packages. The packages for
all of the references must be checked and the changes need to be
deduped, since both a package and its test package contain some of the
same files.

Fixes golang/go#32974

Change-Id: Ie51e19716faae77ce7e5254eeb3956faa42c2a09
Reviewed-on: https://go-review.googlesource.com/c/tools/+/185277
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Suzy Mueller 2019-07-08 18:53:01 -07:00
parent f82b303b69
commit 7b25e351ac
10 changed files with 146 additions and 51 deletions

View File

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"go/token" "go/token"
"os/exec" "os/exec"
"path/filepath"
"sort" "sort"
"strings" "strings"
"testing" "testing"
@ -540,35 +541,41 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
continue continue
} }
_, m, err := getSourceFile(ctx, r.server.session.ViewOf(uri), uri) var res []string
if err != nil { for uri, edits := range *workspaceEdits.Changes {
t.Error(err) spnURI := span.URI(uri)
_, m, err := getSourceFile(ctx, r.server.session.ViewOf(span.URI(spnURI)), spnURI)
if err != nil {
t.Error(err)
}
sedits, err := FromProtocolEdits(m, edits)
if err != nil {
t.Error(err)
}
filename := filepath.Base(m.URI.Filename())
contents := applyEdits(string(m.Content), sedits)
res = append(res, fmt.Sprintf("%s:\n%s", filename, contents))
} }
changes := *workspaceEdits.Changes // Sort on filename
if len(changes) != 1 { // Renames must only affect a single file in these tests. sort.Strings(res)
t.Errorf("rename failed for %s, edited %d files, wanted 1 file", newText, len(*workspaceEdits.Changes))
continue var got string
for i, val := range res {
if i != 0 {
got += "\n"
}
got += val
} }
edits := changes[string(uri)] renamed := string(r.data.Golden(tag, filename, func() ([]byte, error) {
if edits == nil {
t.Errorf("rename failed for %s, did not edit %s", newText, filename)
continue
}
sedits, err := FromProtocolEdits(m, edits)
if err != nil {
t.Error(err)
}
got := applyEdits(string(m.Content), sedits)
gorenamed := string(r.data.Golden(tag, filename, func() ([]byte, error) {
return []byte(got), nil return []byte(got), nil
})) }))
if gorenamed != got { if renamed != got {
t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, gorenamed, got) t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, renamed, got)
} }
} }
} }

View File

@ -20,6 +20,7 @@ type ReferenceInfo struct {
Range span.Range Range span.Range
ident *ast.Ident ident *ast.Ident
obj types.Object obj types.Object
pkg Package
isDeclaration bool isDeclaration bool
} }
@ -34,17 +35,6 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro
if i.decl.obj == nil { if i.decl.obj == nil {
return nil, fmt.Errorf("no references for an import spec") return nil, fmt.Errorf("no references for an import spec")
} }
if i.decl.wasImplicit {
// The definition is implicit, so we must add it separately.
// This occurs when the variable is declared in a type switch statement
// or is an implicit package name. Both implicits are local to a file.
references = append(references, &ReferenceInfo{
Name: i.decl.obj.Name(),
Range: i.decl.rng,
obj: i.decl.obj,
isDeclaration: true,
})
}
pkgs := i.File.GetPackages(ctx) pkgs := i.File.GetPackages(ctx)
for _, pkg := range pkgs { for _, pkg := range pkgs {
@ -55,6 +45,19 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro
if info == nil { if info == nil {
return nil, fmt.Errorf("package %s has no types info", pkg.PkgPath()) return nil, fmt.Errorf("package %s has no types info", pkg.PkgPath())
} }
if i.decl.wasImplicit {
// The definition is implicit, so we must add it separately.
// This occurs when the variable is declared in a type switch statement
// or is an implicit package name. Both implicits are local to a file.
references = append(references, &ReferenceInfo{
Name: i.decl.obj.Name(),
Range: i.decl.rng,
obj: i.decl.obj,
pkg: pkg,
isDeclaration: true,
})
}
for ident, obj := range info.Defs { for ident, obj := range info.Defs {
if obj == nil || !sameObj(obj, i.decl.obj) { if obj == nil || !sameObj(obj, i.decl.obj) {
continue continue
@ -65,6 +68,7 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro
Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()), Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()),
ident: ident, ident: ident,
obj: obj, obj: obj,
pkg: pkg,
isDeclaration: true, isDeclaration: true,
}}, references...) }}, references...)
} }
@ -76,6 +80,7 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro
Name: ident.Name, Name: ident.Name,
Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()), Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()),
ident: ident, ident: ident,
pkg: pkg,
obj: obj, obj: obj,
}) })
} }

View File

@ -73,7 +73,9 @@ func (i *IdentifierInfo) Rename(ctx context.Context, newName string) (map[span.U
to: newName, to: newName,
packages: make(map[*types.Package]Package), packages: make(map[*types.Package]Package),
} }
r.packages[i.pkg.GetTypes()] = i.pkg for _, from := range refs {
r.packages[from.pkg.GetTypes()] = from.pkg
}
// Check that the renaming of the identifier is ok. // Check that the renaming of the identifier is ok.
for _, from := range refs { for _, from := range refs {
@ -89,6 +91,7 @@ func (i *IdentifierInfo) Rename(ctx context.Context, newName string) (map[span.U
// Rename all references to the identifier. // Rename all references to the identifier.
func (r *renamer) update() (map[span.URI][]TextEdit, error) { func (r *renamer) update() (map[span.URI][]TextEdit, error) {
result := make(map[span.URI][]TextEdit) result := make(map[span.URI][]TextEdit)
seen := make(map[span.Span]bool)
docRegexp, err := regexp.Compile(`\b` + r.from + `\b`) docRegexp, err := regexp.Compile(`\b` + r.from + `\b`)
if err != nil { if err != nil {
@ -99,6 +102,10 @@ func (r *renamer) update() (map[span.URI][]TextEdit, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if seen[refSpan] {
continue
}
seen[refSpan] = true
// Renaming a types.PkgName may result in the addition or removal of an identifier, // Renaming a types.PkgName may result in the addition or removal of an identifier,
// so we deal with this separately. // so we deal with this separately.

View File

@ -9,6 +9,7 @@ import (
"context" "context"
"fmt" "fmt"
"os/exec" "os/exec"
"path/filepath"
"sort" "sort"
"strings" "strings"
"testing" "testing"
@ -503,29 +504,40 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
continue continue
} }
if len(changes) != 1 { // Renames must only affect a single file in these tests. var res []string
t.Errorf("rename failed for %s, edited %d files, wanted 1 file", newText, len(changes)) for editSpn, edits := range changes {
continue f, err := r.view.GetFile(ctx, editSpn)
if err != nil {
t.Fatalf("failed for %v: %v", spn, err)
}
data, _, err := f.Handle(ctx).Read(ctx)
if err != nil {
t.Error(err)
continue
}
filename := filepath.Base(editSpn.Filename())
contents := applyEdits(string(data), edits)
res = append(res, fmt.Sprintf("%s:\n%s", filename, contents))
} }
edits := changes[spn.URI()] // Sort on filename
if edits == nil { sort.Strings(res)
t.Errorf("rename failed for %s, did not edit %s", newText, spn.URI())
continue var got string
} for i, val := range res {
data, _, err := f.Handle(ctx).Read(ctx) if i != 0 {
if err != nil { got += "\n"
t.Error(err) }
continue got += val
} }
got := applyEdits(string(data), edits) renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
gorenamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
return []byte(got), nil return []byte(got), nil
})) }))
if gorenamed != got { if renamed != got {
t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, gorenamed, got) t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, renamed, got)
} }
} }
} }

View File

@ -1,4 +1,5 @@
-- GetSum-rename -- -- GetSum-rename --
random.go:
package a package a
import ( import (
@ -43,6 +44,7 @@ func sw() {
} }
-- fmt2-rename -- -- fmt2-rename --
random.go:
package a package a
import ( import (
@ -87,6 +89,7 @@ func sw() {
} }
-- format-rename -- -- format-rename --
random.go:
package a package a
import ( import (
@ -131,6 +134,7 @@ func sw() {
} }
-- log-rename -- -- log-rename --
random.go:
package a package a
import ( import (
@ -175,6 +179,7 @@ func sw() {
} }
-- myX-rename -- -- myX-rename --
random.go:
package a package a
import ( import (
@ -219,6 +224,7 @@ func sw() {
} }
-- pos-rename -- -- pos-rename --
random.go:
package a package a
import ( import (
@ -263,6 +269,7 @@ func sw() {
} }
-- y0-rename -- -- y0-rename --
random.go:
package a package a
import ( import (
@ -307,6 +314,7 @@ func sw() {
} }
-- y1-rename -- -- y1-rename --
random.go:
package a package a
import ( import (
@ -351,6 +359,7 @@ func sw() {
} }
-- y2-rename -- -- y2-rename --
random.go:
package a package a
import ( import (
@ -395,6 +404,7 @@ func sw() {
} }
-- y3-rename -- -- y3-rename --
random.go:
package a package a
import ( import (
@ -439,6 +449,7 @@ func sw() {
} }
-- z-rename -- -- z-rename --
random.go:
package a package a
import ( import (

View File

@ -0,0 +1,6 @@
package testy
type tt int //@rename("tt", "testyType")
func a() {
}

View File

@ -0,0 +1,9 @@
-- testyType-rename --
testy.go:
package testy
type testyType int //@rename("tt", "testyType")
func a() {
}

View File

@ -0,0 +1,8 @@
package testy
import "testing"
func TestSomething(t *testing.T) {
var x int //@rename("x", "testyX")
a() //@rename("a", "b")
}

View File

@ -0,0 +1,30 @@
-- b-rename --
testy.go:
package testy
type tt int //@rename("tt", "testyType")
func b() {
}
testy_test.go:
package testy
import "testing"
func TestSomething(t *testing.T) {
var x int //@rename("x", "testyX")
b() //@rename("a", "b")
}
-- testyX-rename --
testy_test.go:
package testy
import "testing"
func TestSomething(t *testing.T) {
var testyX int //@rename("x", "testyX")
a() //@rename("a", "b")
}

View File

@ -34,7 +34,7 @@ const (
ExpectedTypeDefinitionsCount = 2 ExpectedTypeDefinitionsCount = 2
ExpectedHighlightsCount = 2 ExpectedHighlightsCount = 2
ExpectedReferencesCount = 5 ExpectedReferencesCount = 5
ExpectedRenamesCount = 13 ExpectedRenamesCount = 16
ExpectedSymbolsCount = 1 ExpectedSymbolsCount = 1
ExpectedSignaturesCount = 21 ExpectedSignaturesCount = 21
ExpectedLinksCount = 2 ExpectedLinksCount = 2