1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
21 "golang.org/x/tools/go/buildutil"
22 "golang.org/x/tools/internal/testenv"
25 // TODO(adonovan): test reported source positions, somehow.
27 func TestConflicts(t *testing.T) {
28 defer func(savedWriteFile func(string, []byte) error, savedReportError func(token.Position, string)) {
29 writeFile = savedWriteFile
30 reportError = savedReportError
31 }(writeFile, reportError)
32 writeFile = func(string, []byte) error { return nil }
34 var ctxt *build.Context
35 for _, test := range []struct {
36 ctxt *build.Context // nil => use previous
37 offset, from, to string // values of the -offset/-from and -to flags
38 want string // regexp to match conflict errors, or "OK"
42 ctxt: fakeContext(map[string][]string{
43 "fmt": {`package fmt; type Stringer interface { String() }`},
51 func f() { v.String(); f() }
53 `package main; var w int`},
55 from: "main.v", to: "init",
56 want: `you cannot have a var at package level named "init"`,
59 from: "main.f", to: "init",
60 want: `renaming this func "f" to "init" would make it a package initializer.*` +
61 `but references to it exist`,
64 from: "/go/src/main/0.go::foo", to: "init",
65 want: `"init" is not a valid imported package name`,
70 from: "fmt.Stringer", to: "stringer",
71 want: `renaming this type "Stringer" to "stringer" would make it unexported.*` +
72 `breaking references from packages such as "main"`,
75 from: "(fmt.Stringer).String", to: "string",
76 want: `renaming this method "String" to "string" would make it unexported.*` +
77 `breaking references from packages such as "main"`,
80 // Lexical scope checks
82 // file/package conflict, same file
83 from: "main.v", to: "foo",
84 want: `renaming this var "v" to "foo" would conflict.*` +
85 `with this imported package name`,
88 // file/package conflict, same file
89 from: "main::foo", to: "v",
90 want: `renaming this imported package name "foo" to "v" would conflict.*` +
91 `with this package member var`,
94 // file/package conflict, different files
95 from: "main.w", to: "foo",
96 want: `renaming this var "w" to "foo" would conflict.*` +
97 `with this imported package name`,
100 // file/package conflict, different files
101 from: "main::foo", to: "w",
102 want: `renaming this imported package name "foo" to "w" would conflict.*` +
103 `with this package member var`,
121 from: "main.x", to: "y",
122 want: `renaming this var "x" to "y".*` +
123 `would cause this reference to become shadowed.*` +
124 `by this intervening var definition`,
127 from: "main.g::x", to: "w",
128 want: `renaming this var "x" to "w".*` +
129 `conflicts with var in same block`,
132 from: "main.f::y", to: "x",
133 want: `renaming this var "y" to "x".*` +
134 `would shadow this reference.*` +
135 `to the var declared here`,
138 from: "main.g::w", to: "x",
139 want: `renaming this var "w" to "x".*` +
140 `conflicts with var in same block`,
143 from: "main.z", to: "y", want: "OK",
162 from: "main.f::foo", to: "bar",
163 want: `renaming this label "foo" to "bar".*` +
164 `would conflict with this one`,
167 from: "main.f::foo", to: "wiz", want: "OK",
170 from: "main.f::wiz", to: "x", want: "OK",
173 from: "main.f::x", to: "wiz", want: "OK",
176 from: "main.f::wiz", to: "foo", want: "OK",
184 type U struct { u int }
185 type V struct { v int }
197 print(w.u) // NB: there is no selection of w.v
198 var _ struct { yy, zz int }
201 // field/field conflict in named struct declaration
202 from: "(main.W).U", to: "w",
203 want: `renaming this field "U" to "w".*` +
204 `would conflict with this field`,
207 // rename type used as embedded field
209 // => field/field conflict
210 // This is an entailed renaming;
211 // it would be nice if we checked source positions.
212 from: "main.U", to: "w",
213 want: `renaming this field "U" to "w".*` +
214 `would conflict with this field`,
217 // field/field conflict in unnamed struct declaration
218 from: "main.f::zz", to: "yy",
219 want: `renaming this field "zz" to "yy".*` +
220 `would conflict with this field`,
223 // Now we test both directions of (u,v) (u,w) (v,w) (u,x) (v,x).
224 // Too bad we don't test position info...
226 // field/field ambiguity at same promotion level ('from' selection)
227 from: "(main.U).u", to: "v",
228 want: `renaming this field "u" to "v".*` +
229 `would make this reference ambiguous.*` +
233 // field/field ambiguity at same promotion level ('to' selection)
234 from: "(main.V).v", to: "u",
235 want: `renaming this field "v" to "u".*` +
236 `would make this reference ambiguous.*` +
240 // field/method conflict at different promotion level ('from' selection)
241 from: "(main.U).u", to: "w",
242 want: `renaming this field "u" to "w".*` +
243 `would change the referent of this selection.*` +
247 // field/field shadowing at different promotion levels ('to' selection)
248 from: "(main.W).w", to: "u",
249 want: `renaming this field "w" to "u".*` +
250 `would shadow this selection.*` +
251 `of the field declared here`,
254 from: "(main.V).v", to: "w",
255 want: "OK", // since no selections are made ambiguous
258 from: "(main.W).w", to: "v",
259 want: "OK", // since no selections are made ambiguous
262 // field/method ambiguity at same promotion level ('from' selection)
263 from: "(main.U).u", to: "x",
264 want: `renaming this field "u" to "x".*` +
265 `would make this reference ambiguous.*` +
269 // field/field ambiguity at same promotion level ('to' selection)
270 from: "(main.V).x", to: "u",
271 want: `renaming this method "x" to "u".*` +
272 `would make this reference ambiguous.*` +
276 // field/method conflict at named struct declaration
277 from: "(main.V).v", to: "x",
278 want: `renaming this field "v" to "x".*` +
279 `would conflict with this method`,
282 // field/method conflict at named struct declaration
283 from: "(main.V).x", to: "v",
284 want: `renaming this method "x" to "v".*` +
285 `would conflict with this field`,
298 type I interface { f(); g() }
299 type J interface { I; h() }
301 var _ interface {f()} = C(0)
303 from: "(main.I).f", to: "g",
304 want: `renaming this interface method "f" to "g".*` +
305 `would conflict with this method`,
308 from: `("main".I).f`, to: "h", // NB: exercises quoted import paths too
309 want: `renaming this interface method "f" to "h".*` +
310 `would conflict with this method.*` +
311 `in named interface type "J"`,
314 // type J interface { h; h() } is not a conflict, amusingly.
315 from: "main.I", to: "h",
319 from: "(main.J).h", to: "f",
320 want: `renaming this interface method "h" to "f".*` +
321 `would conflict with this method`,
324 from: "(main.C).f", to: "e",
325 want: `renaming this method "f" to "e".*` +
326 `would make main.C no longer assignable to interface{f..}.*` +
327 `(rename interface{f..}.f if you intend to change both types)`,
330 from: "(main.D).g", to: "e",
331 want: `renaming this method "g" to "e".*` +
332 `would make \*main.D no longer assignable to interface I.*` +
333 `(rename main.I.g if you intend to change both types)`,
336 from: "(main.I).f", to: "e",
339 // Indirect C/I method coupling via another concrete type D.
343 type I interface { f() }
349 from: "(main.C).f", to: "F",
350 want: `renaming this method "f" to "F".*` +
351 `would make main.D no longer assignable to interface I.*` +
352 `(rename main.I.f if you intend to change both types)`,
354 // Renaming causes promoted method to become shadowed; C no longer satisfies I.
358 type I interface { f() }
363 from: "main.I.f", to: "g",
364 want: `renaming this method "f" to "g".*` +
365 `would change the g method of main.C invoked via interface main.I.*` +
366 `from \(main.I\).g.*` +
369 // Renaming causes promoted method to become ambiguous; C no longer satisfies I.
373 type I interface{f()}
381 from: "main.I.f", to: "g",
382 want: `renaming this method "f" to "g".*` +
383 `would make the g method of main.E invoked via interface main.I ambiguous.*` +
387 var conflicts []string
388 reportError = func(posn token.Position, message string) {
389 conflicts = append(conflicts, message)
391 if test.ctxt != nil {
394 err := Main(ctxt, test.offset, test.from, test.to)
396 if test.offset == "" {
397 prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to)
399 prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to)
401 if err == ConflictError {
402 got := strings.Join(conflicts, "\n")
404 t.Logf("%s: %s", prefix, got)
406 pattern := "(?s:" + test.want + ")" // enable multi-line matching
407 if !regexp.MustCompile(pattern).MatchString(got) {
408 t.Errorf("%s: conflict does not match pattern:\n"+
411 prefix, got, test.want)
413 } else if err != nil {
414 t.Errorf("%s: unexpected error: %s", prefix, err)
415 } else if test.want != "OK" {
416 t.Errorf("%s: unexpected success, want conflicts matching:\n%s",
422 func TestInvalidIdentifiers(t *testing.T) {
423 ctxt := fakeContext(map[string][]string{
430 for _, test := range []struct {
431 from, to string // values of the -offset/-from and -to flags
432 want string // expected error message
435 from: "main.f", to: "_",
436 want: `-to "_": not a valid identifier`,
439 from: "main.f", to: "123",
440 want: `-to "123": not a valid identifier`,
443 from: "main.f", to: "for",
444 want: `-to "for": not a valid identifier`,
447 from: "switch", to: "v",
448 want: `-from "switch": invalid expression`,
451 err := Main(ctxt, "", test.from, test.to)
452 prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to)
454 t.Errorf("%s: expected error %q", prefix, test.want)
455 } else if err.Error() != test.want {
456 t.Errorf("%s: unexpected error\nwant: %s\n got: %s", prefix, test.want, err.Error())
461 func TestRewrites(t *testing.T) {
462 defer func(savedWriteFile func(string, []byte) error) {
463 writeFile = savedWriteFile
466 var ctxt *build.Context
467 for _, test := range []struct {
468 ctxt *build.Context // nil => use previous
469 offset, from, to string // values of the -from/-offset and -to flags
470 want map[string]string // contents of updated files
472 // Elimination of renaming import.
474 ctxt: fakeContext(map[string][]string{
475 "foo": {`package foo; type T int`},
476 "main": {`package main
483 from: "main::foo2", to: "foo",
484 want: map[string]string{
485 "/go/src/main/0.go": `package main
493 // Introduction of renaming import.
495 ctxt: fakeContext(map[string][]string{
496 "foo": {`package foo; type T int`},
497 "main": {`package main
504 offset: "/go/src/main/0.go:#36", to: "foo2", // the "foo" in foo.T
505 want: map[string]string{
506 "/go/src/main/0.go": `package main
514 // Renaming of package-level member.
516 from: "foo.T", to: "U",
517 want: map[string]string{
518 "/go/src/main/0.go": `package main
524 "/go/src/foo/0.go": `package foo
530 // Rename package-level func plus doc
532 ctxt: main(`package main
535 // Calling Foo does nothing.
539 from: "main.Foo", to: "FooBar",
540 want: map[string]string{
541 "/go/src/main/0.go": `package main
543 // FooBar is a no-op.
544 // Calling FooBar does nothing.
550 // Rename method plus doc
552 ctxt: main(`package main
560 from: "main.Foo.Bar", to: "Baz",
561 want: map[string]string{
562 "/go/src/main/0.go": `package main
572 // Rename type spec plus doc
574 ctxt: main(`package main
577 // Test but not Testing.
581 from: "main.Test", to: "Type",
582 want: map[string]string{
583 "/go/src/main/0.go": `package main
586 // Type but not Testing.
592 // Rename type in gen decl plus doc
594 ctxt: main(`package main
599 from: "main.T", to: "Type",
600 want: map[string]string{
601 "/go/src/main/0.go": `package main
603 // Type is a test type.
608 // Rename value spec with doc
610 ctxt: main(`package main
613 // C is the speed of light.
617 from: "main.C", to: "Lightspeed",
618 want: map[string]string{
619 "/go/src/main/0.go": `package main
622 // Lightspeed is the speed of light.
628 // Rename value inside gen decl with doc
630 ctxt: main(`package main
634 from: "main.out", to: "discard",
635 want: map[string]string{
636 "/go/src/main/0.go": `package main
642 // Rename field plus doc
644 ctxt: main(`package main
647 // Field is a struct field.
651 from: "main.Struct.Field", to: "Foo",
652 want: map[string]string{
653 "/go/src/main/0.go": `package main
656 // Foo is a struct field.
664 ctxt: main(`package main
676 offset: "/go/src/main/0.go:#25", to: "loop2", // def of outer label "loop"
677 want: map[string]string{
678 "/go/src/main/0.go": `package main
694 offset: "/go/src/main/0.go:#70", to: "loop2", // ref to inner label "loop"
695 want: map[string]string{
696 "/go/src/main/0.go": `package main
711 // Renaming of type used as embedded field.
713 ctxt: main(`package main
720 from: "main.T", to: "T2",
721 want: map[string]string{
722 "/go/src/main/0.go": `package main
731 // Renaming of embedded field.
733 ctxt: main(`package main
740 offset: "/go/src/main/0.go:#58", to: "T2", // T in "U{}.T"
741 want: map[string]string{
742 "/go/src/main/0.go": `package main
751 // Renaming of pointer embedded field.
753 ctxt: main(`package main
760 offset: "/go/src/main/0.go:#59", to: "T2", // T in "U{}.T"
761 want: map[string]string{
762 "/go/src/main/0.go": `package main
772 // Lexical scope tests.
774 ctxt: main(`package main
784 from: "main.y", to: "x",
785 want: map[string]string{
786 "/go/src/main/0.go": `package main
799 from: "main.f::y", to: "x",
800 want: map[string]string{
801 "/go/src/main/0.go": `package main
813 // Renaming of typeswitch vars (a corner case).
815 ctxt: main(`package main
817 func f(z interface{}) {
818 switch y := z.(type) {
826 offset: "/go/src/main/0.go:#46", to: "x", // def of y
827 want: map[string]string{
828 "/go/src/main/0.go": `package main
830 func f(z interface{}) {
831 switch x := z.(type) {
841 offset: "/go/src/main/0.go:#81", to: "x", // ref of y in case int
842 want: map[string]string{
843 "/go/src/main/0.go": `package main
845 func f(z interface{}) {
846 switch x := z.(type) {
856 offset: "/go/src/main/0.go:#102", to: "x", // ref of y in default case
857 want: map[string]string{
858 "/go/src/main/0.go": `package main
860 func f(z interface{}) {
861 switch x := z.(type) {
871 // Renaming of embedded field that is a qualified reference.
872 // (Regression test for bug 8924.)
874 ctxt: fakeContext(map[string][]string{
875 "foo": {`package foo; type T int`},
876 "main": {`package main
880 type _ struct{ *foo.T }
883 offset: "/go/src/main/0.go:#48", to: "U", // the "T" in *foo.T
884 want: map[string]string{
885 "/go/src/foo/0.go": `package foo
889 "/go/src/main/0.go": `package main
893 type _ struct{ *foo.U }
898 // Renaming of embedded field that is a qualified reference with the '-from' flag.
899 // (Regression test for bug 12038.)
901 ctxt: fakeContext(map[string][]string{
902 "foo": {`package foo; type T int`},
903 "main": {`package main
907 type V struct{ *foo.T }
910 from: "(main.V).T", to: "U", // the "T" in *foo.T
911 want: map[string]string{
912 "/go/src/foo/0.go": `package foo
916 "/go/src/main/0.go": `package main
920 type V struct{ *foo.U }
925 ctxt: fakeContext(map[string][]string{
926 "foo": {`package foo; type T int`},
927 "main": {`package main
931 type V struct{ foo.T }
934 from: "(main.V).T", to: "U", // the "T" in *foo.T
935 want: map[string]string{
936 "/go/src/foo/0.go": `package foo
940 "/go/src/main/0.go": `package main
944 type V struct{ foo.U }
949 // Interface method renaming.
951 ctxt: fakeContext(map[string][]string{
957 type J interface { f(); g() }
966 var _, _ I = A(0), B(0)
967 var _, _ J = B(0), C(0)
971 offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f
972 want: map[string]string{
973 "/go/src/main/0.go": `package main
996 var _, _ I = A(0), B(0)
997 var _, _ J = B(0), C(0)
1002 offset: "/go/src/main/0.go:#59", to: "F", // abstract method J.f
1003 want: map[string]string{
1004 "/go/src/main/0.go": `package main
1027 var _, _ I = A(0), B(0)
1028 var _, _ J = B(0), C(0)
1033 offset: "/go/src/main/0.go:#64", to: "G", // abstract method J.g
1034 want: map[string]string{
1035 "/go/src/main/0.go": `package main
1058 var _, _ I = A(0), B(0)
1059 var _, _ J = B(0), C(0)
1063 // Indirect coupling of I.f to C.f from D->I assignment and anonymous field of D.
1065 ctxt: fakeContext(map[string][]string{
1078 offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f
1079 want: map[string]string{
1080 "/go/src/main/0.go": `package main
1095 // Interface embedded in struct. No conflict if C need not satisfy I.
1097 ctxt: fakeContext(map[string][]string{
1109 offset: "/go/src/main/0.go:#34", to: "g", // abstract method I.f
1110 want: map[string]string{
1111 "/go/src/main/0.go": `package main
1124 // A type assertion causes method coupling iff signatures match.
1126 ctxt: fakeContext(map[string][]string{
1127 "main": {`package main
1138 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
1139 want: map[string]string{
1140 "/go/src/main/0.go": `package main
1153 // Impossible type assertion: no method coupling.
1155 ctxt: fakeContext(map[string][]string{
1156 "main": {`package main
1167 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
1168 want: map[string]string{
1169 "/go/src/main/0.go": `package main
1182 // Impossible type assertion: no method coupling C.f<->J.f.
1184 ctxt: fakeContext(map[string][]string{
1185 "main": {`package main
1198 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
1199 want: map[string]string{
1200 "/go/src/main/0.go": `package main
1217 // Progress after "soft" type errors (Go issue 14596).
1219 ctxt: fakeContext(map[string][]string{
1220 "main": {`package main
1229 offset: "/go/src/main/0.go:#54", to: "y", // var x
1230 want: map[string]string{
1231 "/go/src/main/0.go": `package main
1241 if test.ctxt != nil {
1245 got := make(map[string]string)
1246 writeFile = func(filename string, content []byte) error {
1247 got[filepath.ToSlash(filename)] = string(content)
1251 err := Main(ctxt, test.offset, test.from, test.to)
1253 if test.offset == "" {
1254 prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to)
1256 prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to)
1259 t.Errorf("%s: unexpected error: %s", prefix, err)
1263 for file, wantContent := range test.want {
1264 gotContent, ok := got[file]
1267 t.Errorf("%s: file %s not rewritten", prefix, file)
1270 if gotContent != wantContent {
1271 t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+
1272 "want <<<%s>>>", prefix, file, gotContent, wantContent)
1275 // got should now be empty
1276 for file := range got {
1277 t.Errorf("%s: unexpected rewrite of file %s", prefix, file)
1282 func TestDiff(t *testing.T) {
1283 switch runtime.GOOS {
1285 if os.Getenv("GO_BUILDER_NAME") != "" {
1286 if _, err := exec.LookPath(DiffCmd); err != nil {
1287 t.Skipf("diff tool non-existent for %s on builders", runtime.GOOS)
1291 t.Skipf("plan9 diff tool doesn't support -u flag")
1293 testenv.NeedsTool(t, DiffCmd)
1294 testenv.NeedsTool(t, "go") // to locate the package to be renamed
1301 stdout = new(bytes.Buffer)
1303 // Set up a fake GOPATH in a temporary directory,
1304 // and ensure we're in GOPATH mode.
1305 tmpdir, err := ioutil.TempDir("", "TestDiff")
1309 defer os.RemoveAll(tmpdir)
1310 buildCtx := build.Default
1311 buildCtx.GOPATH = tmpdir
1313 pkgDir := filepath.Join(tmpdir, "src", "example.com", "rename")
1314 if err := os.MkdirAll(pkgDir, 0777); err != nil {
1318 prevWD, err := os.Getwd()
1322 defer os.Chdir(prevWD)
1324 if err := os.Chdir(pkgDir); err != nil {
1328 const modFile = `module example.com/rename
1332 if err := ioutil.WriteFile(filepath.Join(pkgDir, "go.mod"), []byte(modFile), 0644); err != nil {
1336 const goFile = `package rename
1338 func justHereForTestingDiff() {
1339 justHereForTestingDiff()
1342 if err := ioutil.WriteFile(filepath.Join(pkgDir, "rename_test.go"), []byte(goFile), 0644); err != nil {
1346 if err := Main(&buildCtx, "", `"example.com/rename".justHereForTestingDiff`, "Foo"); err != nil {
1350 // NB: there are tabs in the string literal!
1351 if !strings.Contains(stdout.(fmt.Stringer).String(), `
1352 -func justHereForTestingDiff() {
1353 - justHereForTestingDiff()
1358 t.Errorf("unexpected diff:\n<<%s>>", stdout)
1362 // ---------------------------------------------------------------------
1364 // Simplifying wrapper around buildutil.FakeContext for packages whose
1365 // filenames are sequentially numbered (%d.go). pkgs maps a package
1366 // import path to its list of file contents.
1367 func fakeContext(pkgs map[string][]string) *build.Context {
1368 pkgs2 := make(map[string]map[string]string)
1369 for path, files := range pkgs {
1370 filemap := make(map[string]string)
1371 for i, contents := range files {
1372 filemap[fmt.Sprintf("%d.go", i)] = contents
1374 pkgs2[path] = filemap
1376 return buildutil.FakeContext(pkgs2)
1379 // helper for single-file main packages with no imports.
1380 func main(content string) *build.Context {
1381 return fakeContext(map[string][]string{"main": {content}})