|  | // 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}}) | 
|  | } |