+++ /dev/null
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package rename
-
-import (
- "bytes"
- "fmt"
- "go/build"
- "go/token"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "regexp"
- "runtime"
- "strings"
- "testing"
-
- "golang.org/x/tools/go/buildutil"
- "golang.org/x/tools/internal/testenv"
-)
-
-// TODO(adonovan): test reported source positions, somehow.
-
-func TestConflicts(t *testing.T) {
- defer func(savedWriteFile func(string, []byte) error, savedReportError func(token.Position, string)) {
- writeFile = savedWriteFile
- reportError = savedReportError
- }(writeFile, reportError)
- writeFile = func(string, []byte) error { return nil }
-
- var ctxt *build.Context
- for _, test := range []struct {
- ctxt *build.Context // nil => use previous
- offset, from, to string // values of the -offset/-from and -to flags
- want string // regexp to match conflict errors, or "OK"
- }{
- // init() checks
- {
- ctxt: fakeContext(map[string][]string{
- "fmt": {`package fmt; type Stringer interface { String() }`},
- "main": {`
-package main
-
-import foo "fmt"
-
-var v foo.Stringer
-
-func f() { v.String(); f() }
-`,
- `package main; var w int`},
- }),
- from: "main.v", to: "init",
- want: `you cannot have a var at package level named "init"`,
- },
- {
- from: "main.f", to: "init",
- want: `renaming this func "f" to "init" would make it a package initializer.*` +
- `but references to it exist`,
- },
- {
- from: "/go/src/main/0.go::foo", to: "init",
- want: `"init" is not a valid imported package name`,
- },
-
- // Export checks
- {
- from: "fmt.Stringer", to: "stringer",
- want: `renaming this type "Stringer" to "stringer" would make it unexported.*` +
- `breaking references from packages such as "main"`,
- },
- {
- from: "(fmt.Stringer).String", to: "string",
- want: `renaming this method "String" to "string" would make it unexported.*` +
- `breaking references from packages such as "main"`,
- },
-
- // Lexical scope checks
- {
- // file/package conflict, same file
- from: "main.v", to: "foo",
- want: `renaming this var "v" to "foo" would conflict.*` +
- `with this imported package name`,
- },
- {
- // file/package conflict, same file
- from: "main::foo", to: "v",
- want: `renaming this imported package name "foo" to "v" would conflict.*` +
- `with this package member var`,
- },
- {
- // file/package conflict, different files
- from: "main.w", to: "foo",
- want: `renaming this var "w" to "foo" would conflict.*` +
- `with this imported package name`,
- },
- {
- // file/package conflict, different files
- from: "main::foo", to: "w",
- want: `renaming this imported package name "foo" to "w" would conflict.*` +
- `with this package member var`,
- },
- {
- ctxt: main(`
-package main
-
-var x, z int
-
-func f(y int) {
- print(x)
- print(y)
-}
-
-func g(w int) {
- print(x)
- x := 1
- print(x)
-}`),
- from: "main.x", to: "y",
- want: `renaming this var "x" to "y".*` +
- `would cause this reference to become shadowed.*` +
- `by this intervening var definition`,
- },
- {
- from: "main.g::x", to: "w",
- want: `renaming this var "x" to "w".*` +
- `conflicts with var in same block`,
- },
- {
- from: "main.f::y", to: "x",
- want: `renaming this var "y" to "x".*` +
- `would shadow this reference.*` +
- `to the var declared here`,
- },
- {
- from: "main.g::w", to: "x",
- want: `renaming this var "w" to "x".*` +
- `conflicts with var in same block`,
- },
- {
- from: "main.z", to: "y", want: "OK",
- },
-
- // Label checks
- {
- ctxt: main(`
-package main
-
-func f() {
-foo:
- goto foo
-bar:
- goto bar
- func(x int) {
- wiz:
- goto wiz
- }(0)
-}
-`),
- from: "main.f::foo", to: "bar",
- want: `renaming this label "foo" to "bar".*` +
- `would conflict with this one`,
- },
- {
- from: "main.f::foo", to: "wiz", want: "OK",
- },
- {
- from: "main.f::wiz", to: "x", want: "OK",
- },
- {
- from: "main.f::x", to: "wiz", want: "OK",
- },
- {
- from: "main.f::wiz", to: "foo", want: "OK",
- },
-
- // Struct fields
- {
- ctxt: main(`
-package main
-
-type U struct { u int }
-type V struct { v int }
-
-func (V) x() {}
-
-type W (struct {
- U
- V
- w int
-})
-
-func f() {
- var w W
- print(w.u) // NB: there is no selection of w.v
- var _ struct { yy, zz int }
-}
-`),
- // field/field conflict in named struct declaration
- from: "(main.W).U", to: "w",
- want: `renaming this field "U" to "w".*` +
- `would conflict with this field`,
- },
- {
- // rename type used as embedded field
- // => rename field
- // => field/field conflict
- // This is an entailed renaming;
- // it would be nice if we checked source positions.
- from: "main.U", to: "w",
- want: `renaming this field "U" to "w".*` +
- `would conflict with this field`,
- },
- {
- // field/field conflict in unnamed struct declaration
- from: "main.f::zz", to: "yy",
- want: `renaming this field "zz" to "yy".*` +
- `would conflict with this field`,
- },
-
- // Now we test both directions of (u,v) (u,w) (v,w) (u,x) (v,x).
- // Too bad we don't test position info...
- {
- // field/field ambiguity at same promotion level ('from' selection)
- from: "(main.U).u", to: "v",
- want: `renaming this field "u" to "v".*` +
- `would make this reference ambiguous.*` +
- `with this field`,
- },
- {
- // field/field ambiguity at same promotion level ('to' selection)
- from: "(main.V).v", to: "u",
- want: `renaming this field "v" to "u".*` +
- `would make this reference ambiguous.*` +
- `with this field`,
- },
- {
- // field/method conflict at different promotion level ('from' selection)
- from: "(main.U).u", to: "w",
- want: `renaming this field "u" to "w".*` +
- `would change the referent of this selection.*` +
- `of this field`,
- },
- {
- // field/field shadowing at different promotion levels ('to' selection)
- from: "(main.W).w", to: "u",
- want: `renaming this field "w" to "u".*` +
- `would shadow this selection.*` +
- `of the field declared here`,
- },
- {
- from: "(main.V).v", to: "w",
- want: "OK", // since no selections are made ambiguous
- },
- {
- from: "(main.W).w", to: "v",
- want: "OK", // since no selections are made ambiguous
- },
- {
- // field/method ambiguity at same promotion level ('from' selection)
- from: "(main.U).u", to: "x",
- want: `renaming this field "u" to "x".*` +
- `would make this reference ambiguous.*` +
- `with this method`,
- },
- {
- // field/field ambiguity at same promotion level ('to' selection)
- from: "(main.V).x", to: "u",
- want: `renaming this method "x" to "u".*` +
- `would make this reference ambiguous.*` +
- `with this field`,
- },
- {
- // field/method conflict at named struct declaration
- from: "(main.V).v", to: "x",
- want: `renaming this field "v" to "x".*` +
- `would conflict with this method`,
- },
- {
- // field/method conflict at named struct declaration
- from: "(main.V).x", to: "v",
- want: `renaming this method "x" to "v".*` +
- `would conflict with this field`,
- },
-
- // Methods
- {
- ctxt: main(`
-package main
-type C int
-func (C) f()
-func (C) g()
-type D int
-func (*D) f()
-func (*D) g()
-type I interface { f(); g() }
-type J interface { I; h() }
-var _ I = new(D)
-var _ interface {f()} = C(0)
-`),
- from: "(main.I).f", to: "g",
- want: `renaming this interface method "f" to "g".*` +
- `would conflict with this method`,
- },
- {
- from: `("main".I).f`, to: "h", // NB: exercises quoted import paths too
- want: `renaming this interface method "f" to "h".*` +
- `would conflict with this method.*` +
- `in named interface type "J"`,
- },
- {
- // type J interface { h; h() } is not a conflict, amusingly.
- from: "main.I", to: "h",
- want: `OK`,
- },
- {
- from: "(main.J).h", to: "f",
- want: `renaming this interface method "h" to "f".*` +
- `would conflict with this method`,
- },
- {
- from: "(main.C).f", to: "e",
- want: `renaming this method "f" to "e".*` +
- `would make main.C no longer assignable to interface{f..}.*` +
- `(rename interface{f..}.f if you intend to change both types)`,
- },
- {
- from: "(main.D).g", to: "e",
- want: `renaming this method "g" to "e".*` +
- `would make \*main.D no longer assignable to interface I.*` +
- `(rename main.I.g if you intend to change both types)`,
- },
- {
- from: "(main.I).f", to: "e",
- want: `OK`,
- },
- // Indirect C/I method coupling via another concrete type D.
- {
- ctxt: main(`
-package main
-type I interface { f() }
-type C int
-func (C) f()
-type D struct{C}
-var _ I = D{}
-`),
- from: "(main.C).f", to: "F",
- want: `renaming this method "f" to "F".*` +
- `would make main.D no longer assignable to interface I.*` +
- `(rename main.I.f if you intend to change both types)`,
- },
- // Renaming causes promoted method to become shadowed; C no longer satisfies I.
- {
- ctxt: main(`
-package main
-type I interface { f() }
-type C struct { I }
-func (C) g() int
-var _ I = C{}
-`),
- from: "main.I.f", to: "g",
- want: `renaming this method "f" to "g".*` +
- `would change the g method of main.C invoked via interface main.I.*` +
- `from \(main.I\).g.*` +
- `to \(main.C\).g`,
- },
- // Renaming causes promoted method to become ambiguous; C no longer satisfies I.
- {
- ctxt: main(`
-package main
-type I interface{f()}
-type C int
-func (C) f()
-type D int
-func (D) g()
-type E struct{C;D}
-var _ I = E{}
-`),
- from: "main.I.f", to: "g",
- want: `renaming this method "f" to "g".*` +
- `would make the g method of main.E invoked via interface main.I ambiguous.*` +
- `with \(main.D\).g`,
- },
- } {
- var conflicts []string
- reportError = func(posn token.Position, message string) {
- conflicts = append(conflicts, message)
- }
- if test.ctxt != nil {
- ctxt = test.ctxt
- }
- err := Main(ctxt, test.offset, test.from, test.to)
- var prefix string
- if test.offset == "" {
- prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to)
- } else {
- prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to)
- }
- if err == ConflictError {
- got := strings.Join(conflicts, "\n")
- if false {
- t.Logf("%s: %s", prefix, got)
- }
- pattern := "(?s:" + test.want + ")" // enable multi-line matching
- if !regexp.MustCompile(pattern).MatchString(got) {
- t.Errorf("%s: conflict does not match pattern:\n"+
- "Conflict:\t%s\n"+
- "Pattern: %s",
- prefix, got, test.want)
- }
- } else if err != nil {
- t.Errorf("%s: unexpected error: %s", prefix, err)
- } else if test.want != "OK" {
- t.Errorf("%s: unexpected success, want conflicts matching:\n%s",
- prefix, test.want)
- }
- }
-}
-
-func TestInvalidIdentifiers(t *testing.T) {
- ctxt := fakeContext(map[string][]string{
- "main": {`
-package main
-
-func f() { }
-`}})
-
- for _, test := range []struct {
- from, to string // values of the -offset/-from and -to flags
- want string // expected error message
- }{
- {
- from: "main.f", to: "_",
- want: `-to "_": not a valid identifier`,
- },
- {
- from: "main.f", to: "123",
- want: `-to "123": not a valid identifier`,
- },
- {
- from: "main.f", to: "for",
- want: `-to "for": not a valid identifier`,
- },
- {
- from: "switch", to: "v",
- want: `-from "switch": invalid expression`,
- },
- } {
- err := Main(ctxt, "", test.from, test.to)
- prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to)
- if err == nil {
- t.Errorf("%s: expected error %q", prefix, test.want)
- } else if err.Error() != test.want {
- t.Errorf("%s: unexpected error\nwant: %s\n got: %s", prefix, test.want, err.Error())
- }
- }
-}
-
-func TestRewrites(t *testing.T) {
- defer func(savedWriteFile func(string, []byte) error) {
- writeFile = savedWriteFile
- }(writeFile)
-
- var ctxt *build.Context
- for _, test := range []struct {
- ctxt *build.Context // nil => use previous
- offset, from, to string // values of the -from/-offset and -to flags
- want map[string]string // contents of updated files
- }{
- // Elimination of renaming import.
- {
- ctxt: fakeContext(map[string][]string{
- "foo": {`package foo; type T int`},
- "main": {`package main
-
-import foo2 "foo"
-
-var _ foo2.T
-`},
- }),
- from: "main::foo2", to: "foo",
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-import "foo"
-
-var _ foo.T
-`,
- },
- },
- // Introduction of renaming import.
- {
- ctxt: fakeContext(map[string][]string{
- "foo": {`package foo; type T int`},
- "main": {`package main
-
-import "foo"
-
-var _ foo.T
-`},
- }),
- offset: "/go/src/main/0.go:#36", to: "foo2", // the "foo" in foo.T
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-import foo2 "foo"
-
-var _ foo2.T
-`,
- },
- },
- // Renaming of package-level member.
- {
- from: "foo.T", to: "U",
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-import "foo"
-
-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
-}
-`,
- },
- },
- // Label renamings.
- {
- ctxt: main(`package main
-func f() {
-loop:
- loop := 0
- go func() {
- loop:
- goto loop
- }()
- loop++
- goto loop
-}
-`),
- offset: "/go/src/main/0.go:#25", to: "loop2", // def of outer label "loop"
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-func f() {
-loop2:
- loop := 0
- go func() {
- loop:
- goto loop
- }()
- loop++
- goto loop2
-}
-`,
- },
- },
- {
- offset: "/go/src/main/0.go:#70", to: "loop2", // ref to inner label "loop"
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-func f() {
-loop:
- loop := 0
- go func() {
- loop2:
- goto loop2
- }()
- loop++
- goto loop
-}
-`,
- },
- },
- // Renaming of type used as embedded field.
- {
- ctxt: main(`package main
-
-type T int
-type U struct { T }
-
-var _ = U{}.T
-`),
- from: "main.T", to: "T2",
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type T2 int
-type U struct{ T2 }
-
-var _ = U{}.T2
-`,
- },
- },
- // Renaming of embedded field.
- {
- ctxt: main(`package main
-
-type T int
-type U struct { T }
-
-var _ = U{}.T
-`),
- offset: "/go/src/main/0.go:#58", to: "T2", // T in "U{}.T"
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type T2 int
-type U struct{ T2 }
-
-var _ = U{}.T2
-`,
- },
- },
- // Renaming of pointer embedded field.
- {
- ctxt: main(`package main
-
-type T int
-type U struct { *T }
-
-var _ = U{}.T
-`),
- offset: "/go/src/main/0.go:#59", to: "T2", // T in "U{}.T"
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type T2 int
-type U struct{ *T2 }
-
-var _ = U{}.T2
-`,
- },
- },
-
- // Lexical scope tests.
- {
- ctxt: main(`package main
-
-var y int
-
-func f() {
- print(y)
- y := ""
- print(y)
-}
-`),
- from: "main.y", to: "x",
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-var x int
-
-func f() {
- print(x)
- y := ""
- print(y)
-}
-`,
- },
- },
- {
- from: "main.f::y", to: "x",
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-var y int
-
-func f() {
- print(y)
- x := ""
- print(x)
-}
-`,
- },
- },
- // Renaming of typeswitch vars (a corner case).
- {
- ctxt: main(`package main
-
-func f(z interface{}) {
- switch y := z.(type) {
- case int:
- print(y)
- default:
- print(y)
- }
-}
-`),
- offset: "/go/src/main/0.go:#46", to: "x", // def of y
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-func f(z interface{}) {
- switch x := z.(type) {
- case int:
- print(x)
- default:
- print(x)
- }
-}
-`},
- },
- {
- offset: "/go/src/main/0.go:#81", to: "x", // ref of y in case int
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-func f(z interface{}) {
- switch x := z.(type) {
- case int:
- print(x)
- default:
- print(x)
- }
-}
-`},
- },
- {
- offset: "/go/src/main/0.go:#102", to: "x", // ref of y in default case
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-func f(z interface{}) {
- switch x := z.(type) {
- case int:
- print(x)
- default:
- print(x)
- }
-}
-`},
- },
-
- // Renaming of embedded field that is a qualified reference.
- // (Regression test for bug 8924.)
- {
- ctxt: fakeContext(map[string][]string{
- "foo": {`package foo; type T int`},
- "main": {`package main
-
-import "foo"
-
-type _ struct{ *foo.T }
-`},
- }),
- offset: "/go/src/main/0.go:#48", to: "U", // the "T" in *foo.T
- want: map[string]string{
- "/go/src/foo/0.go": `package foo
-
-type U int
-`,
- "/go/src/main/0.go": `package main
-
-import "foo"
-
-type _ struct{ *foo.U }
-`,
- },
- },
-
- // Renaming of embedded field that is a qualified reference with the '-from' flag.
- // (Regression test for bug 12038.)
- {
- ctxt: fakeContext(map[string][]string{
- "foo": {`package foo; type T int`},
- "main": {`package main
-
-import "foo"
-
-type V struct{ *foo.T }
-`},
- }),
- from: "(main.V).T", to: "U", // the "T" in *foo.T
- want: map[string]string{
- "/go/src/foo/0.go": `package foo
-
-type U int
-`,
- "/go/src/main/0.go": `package main
-
-import "foo"
-
-type V struct{ *foo.U }
-`,
- },
- },
- {
- ctxt: fakeContext(map[string][]string{
- "foo": {`package foo; type T int`},
- "main": {`package main
-
-import "foo"
-
-type V struct{ foo.T }
-`},
- }),
- from: "(main.V).T", to: "U", // the "T" in *foo.T
- want: map[string]string{
- "/go/src/foo/0.go": `package foo
-
-type U int
-`,
- "/go/src/main/0.go": `package main
-
-import "foo"
-
-type V struct{ foo.U }
-`,
- },
- },
-
- // Interface method renaming.
- {
- ctxt: fakeContext(map[string][]string{
- "main": {`
-package main
-type I interface {
- f()
-}
-type J interface { f(); g() }
-type A int
-func (A) f()
-type B int
-func (B) f()
-func (B) g()
-type C int
-func (C) f()
-func (C) g()
-var _, _ I = A(0), B(0)
-var _, _ J = B(0), C(0)
-`,
- },
- }),
- offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- F()
-}
-type J interface {
- F()
- g()
-}
-type A int
-
-func (A) F()
-
-type B int
-
-func (B) F()
-func (B) g()
-
-type C int
-
-func (C) F()
-func (C) g()
-
-var _, _ I = A(0), B(0)
-var _, _ J = B(0), C(0)
-`,
- },
- },
- {
- offset: "/go/src/main/0.go:#59", to: "F", // abstract method J.f
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- F()
-}
-type J interface {
- F()
- g()
-}
-type A int
-
-func (A) F()
-
-type B int
-
-func (B) F()
-func (B) g()
-
-type C int
-
-func (C) F()
-func (C) g()
-
-var _, _ I = A(0), B(0)
-var _, _ J = B(0), C(0)
-`,
- },
- },
- {
- offset: "/go/src/main/0.go:#64", to: "G", // abstract method J.g
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- f()
-}
-type J interface {
- f()
- G()
-}
-type A int
-
-func (A) f()
-
-type B int
-
-func (B) f()
-func (B) G()
-
-type C int
-
-func (C) f()
-func (C) G()
-
-var _, _ I = A(0), B(0)
-var _, _ J = B(0), C(0)
-`,
- },
- },
- // Indirect coupling of I.f to C.f from D->I assignment and anonymous field of D.
- {
- ctxt: fakeContext(map[string][]string{
- "main": {`
-package main
-type I interface {
- f()
-}
-type C int
-func (C) f()
-type D struct{C}
-var _ I = D{}
-`,
- },
- }),
- offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- F()
-}
-type C int
-
-func (C) F()
-
-type D struct{ C }
-
-var _ I = D{}
-`,
- },
- },
- // Interface embedded in struct. No conflict if C need not satisfy I.
- {
- ctxt: fakeContext(map[string][]string{
- "main": {`
-package main
-type I interface {
- f()
-}
-type C struct{I}
-func (C) g() int
-var _ int = C{}.g()
-`,
- },
- }),
- offset: "/go/src/main/0.go:#34", to: "g", // abstract method I.f
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- g()
-}
-type C struct{ I }
-
-func (C) g() int
-
-var _ int = C{}.g()
-`,
- },
- },
- // A type assertion causes method coupling iff signatures match.
- {
- ctxt: fakeContext(map[string][]string{
- "main": {`package main
-type I interface{
- f()
-}
-type J interface{
- f()
-}
-var _ = I(nil).(J)
-`,
- },
- }),
- offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- g()
-}
-type J interface {
- g()
-}
-
-var _ = I(nil).(J)
-`,
- },
- },
- // Impossible type assertion: no method coupling.
- {
- ctxt: fakeContext(map[string][]string{
- "main": {`package main
-type I interface{
- f()
-}
-type J interface{
- f()int
-}
-var _ = I(nil).(J)
-`,
- },
- }),
- offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- g()
-}
-type J interface {
- f() int
-}
-
-var _ = I(nil).(J)
-`,
- },
- },
- // Impossible type assertion: no method coupling C.f<->J.f.
- {
- ctxt: fakeContext(map[string][]string{
- "main": {`package main
-type I interface{
- f()
-}
-type C int
-func (C) f()
-type J interface{
- f()int
-}
-var _ = I(C(0)).(J)
-`,
- },
- }),
- offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-type I interface {
- g()
-}
-type C int
-
-func (C) g()
-
-type J interface {
- f() int
-}
-
-var _ = I(C(0)).(J)
-`,
- },
- },
- // Progress after "soft" type errors (Go issue 14596).
- {
- ctxt: fakeContext(map[string][]string{
- "main": {`package main
-
-func main() {
- var unused, x int
- print(x)
-}
-`,
- },
- }),
- offset: "/go/src/main/0.go:#54", to: "y", // var x
- want: map[string]string{
- "/go/src/main/0.go": `package main
-
-func main() {
- var unused, y int
- print(y)
-}
-`,
- },
- },
- } {
- if test.ctxt != nil {
- ctxt = test.ctxt
- }
-
- got := make(map[string]string)
- writeFile = func(filename string, content []byte) error {
- got[filepath.ToSlash(filename)] = string(content)
- return nil
- }
-
- err := Main(ctxt, test.offset, test.from, test.to)
- var prefix string
- if test.offset == "" {
- prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to)
- } else {
- prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to)
- }
- if err != nil {
- t.Errorf("%s: unexpected error: %s", prefix, err)
- continue
- }
-
- for file, wantContent := range test.want {
- gotContent, ok := got[file]
- delete(got, file)
- if !ok {
- t.Errorf("%s: file %s not rewritten", prefix, file)
- continue
- }
- if gotContent != wantContent {
- t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+
- "want <<<%s>>>", prefix, file, gotContent, wantContent)
- }
- }
- // got should now be empty
- for file := range got {
- t.Errorf("%s: unexpected rewrite of file %s", prefix, file)
- }
- }
-}
-
-func TestDiff(t *testing.T) {
- switch runtime.GOOS {
- case "windows":
- if os.Getenv("GO_BUILDER_NAME") != "" {
- if _, err := exec.LookPath(DiffCmd); err != nil {
- t.Skipf("diff tool non-existent for %s on builders", runtime.GOOS)
- }
- }
- case "plan9":
- t.Skipf("plan9 diff tool doesn't support -u flag")
- }
- testenv.NeedsTool(t, DiffCmd)
- testenv.NeedsTool(t, "go") // to locate the package to be renamed
-
- defer func() {
- Diff = false
- stdout = os.Stdout
- }()
- Diff = true
- stdout = new(bytes.Buffer)
-
- // Set up a fake GOPATH in a temporary directory,
- // and ensure we're in GOPATH mode.
- tmpdir, err := ioutil.TempDir("", "TestDiff")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(tmpdir)
- buildCtx := build.Default
- buildCtx.GOPATH = tmpdir
-
- pkgDir := filepath.Join(tmpdir, "src", "example.com", "rename")
- if err := os.MkdirAll(pkgDir, 0777); err != nil {
- t.Fatal(err)
- }
-
- prevWD, err := os.Getwd()
- if err != nil {
- t.Fatal(err)
- }
- defer os.Chdir(prevWD)
-
- if err := os.Chdir(pkgDir); err != nil {
- t.Fatal(err)
- }
-
- const modFile = `module example.com/rename
-
-go 1.15
-`
- if err := ioutil.WriteFile(filepath.Join(pkgDir, "go.mod"), []byte(modFile), 0644); err != nil {
- t.Fatal(err)
- }
-
- const goFile = `package rename
-
-func justHereForTestingDiff() {
- justHereForTestingDiff()
-}
-`
- if err := ioutil.WriteFile(filepath.Join(pkgDir, "rename_test.go"), []byte(goFile), 0644); err != nil {
- t.Fatal(err)
- }
-
- if err := Main(&buildCtx, "", `"example.com/rename".justHereForTestingDiff`, "Foo"); err != nil {
- t.Fatal(err)
- }
-
- // NB: there are tabs in the string literal!
- if !strings.Contains(stdout.(fmt.Stringer).String(), `
--func justHereForTestingDiff() {
-- justHereForTestingDiff()
-+func Foo() {
-+ Foo()
- }
-`) {
- t.Errorf("unexpected diff:\n<<%s>>", stdout)
- }
-}
-
-// ---------------------------------------------------------------------
-
-// Simplifying wrapper around buildutil.FakeContext for packages whose
-// filenames are sequentially numbered (%d.go). pkgs maps a package
-// import path to its list of file contents.
-func fakeContext(pkgs map[string][]string) *build.Context {
- pkgs2 := make(map[string]map[string]string)
- for path, files := range pkgs {
- filemap := make(map[string]string)
- for i, contents := range files {
- filemap[fmt.Sprintf("%d.go", i)] = contents
- }
- pkgs2[path] = filemap
- }
- return buildutil.FakeContext(pkgs2)
-}
-
-// helper for single-file main packages with no imports.
-func main(content string) *build.Context {
- return fakeContext(map[string][]string{"main": {content}})
-}