github.com/jd-ly/tools@v0.5.7/refactor/rename/rename_test.go (about) 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. 4 5 package rename 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/build" 11 "go/token" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path/filepath" 16 "regexp" 17 "runtime" 18 "strings" 19 "testing" 20 21 "github.com/jd-ly/tools/go/buildutil" 22 "github.com/jd-ly/tools/internal/testenv" 23 ) 24 25 // TODO(adonovan): test reported source positions, somehow. 26 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 } 33 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" 39 }{ 40 // init() checks 41 { 42 ctxt: fakeContext(map[string][]string{ 43 "fmt": {`package fmt; type Stringer interface { String() }`}, 44 "main": {` 45 package main 46 47 import foo "fmt" 48 49 var v foo.Stringer 50 51 func f() { v.String(); f() } 52 `, 53 `package main; var w int`}, 54 }), 55 from: "main.v", to: "init", 56 want: `you cannot have a var at package level named "init"`, 57 }, 58 { 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`, 62 }, 63 { 64 from: "/go/src/main/0.go::foo", to: "init", 65 want: `"init" is not a valid imported package name`, 66 }, 67 68 // Export checks 69 { 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"`, 73 }, 74 { 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"`, 78 }, 79 80 // Lexical scope checks 81 { 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`, 86 }, 87 { 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`, 92 }, 93 { 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`, 98 }, 99 { 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`, 104 }, 105 { 106 ctxt: main(` 107 package main 108 109 var x, z int 110 111 func f(y int) { 112 print(x) 113 print(y) 114 } 115 116 func g(w int) { 117 print(x) 118 x := 1 119 print(x) 120 }`), 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`, 125 }, 126 { 127 from: "main.g::x", to: "w", 128 want: `renaming this var "x" to "w".*` + 129 `conflicts with var in same block`, 130 }, 131 { 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`, 136 }, 137 { 138 from: "main.g::w", to: "x", 139 want: `renaming this var "w" to "x".*` + 140 `conflicts with var in same block`, 141 }, 142 { 143 from: "main.z", to: "y", want: "OK", 144 }, 145 146 // Label checks 147 { 148 ctxt: main(` 149 package main 150 151 func f() { 152 foo: 153 goto foo 154 bar: 155 goto bar 156 func(x int) { 157 wiz: 158 goto wiz 159 }(0) 160 } 161 `), 162 from: "main.f::foo", to: "bar", 163 want: `renaming this label "foo" to "bar".*` + 164 `would conflict with this one`, 165 }, 166 { 167 from: "main.f::foo", to: "wiz", want: "OK", 168 }, 169 { 170 from: "main.f::wiz", to: "x", want: "OK", 171 }, 172 { 173 from: "main.f::x", to: "wiz", want: "OK", 174 }, 175 { 176 from: "main.f::wiz", to: "foo", want: "OK", 177 }, 178 179 // Struct fields 180 { 181 ctxt: main(` 182 package main 183 184 type U struct { u int } 185 type V struct { v int } 186 187 func (V) x() {} 188 189 type W (struct { 190 U 191 V 192 w int 193 }) 194 195 func f() { 196 var w W 197 print(w.u) // NB: there is no selection of w.v 198 var _ struct { yy, zz int } 199 } 200 `), 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`, 205 }, 206 { 207 // rename type used as embedded field 208 // => rename 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`, 215 }, 216 { 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`, 221 }, 222 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... 225 { 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.*` + 230 `with this field`, 231 }, 232 { 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.*` + 237 `with this field`, 238 }, 239 { 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.*` + 244 `of this field`, 245 }, 246 { 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`, 252 }, 253 { 254 from: "(main.V).v", to: "w", 255 want: "OK", // since no selections are made ambiguous 256 }, 257 { 258 from: "(main.W).w", to: "v", 259 want: "OK", // since no selections are made ambiguous 260 }, 261 { 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.*` + 266 `with this method`, 267 }, 268 { 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.*` + 273 `with this field`, 274 }, 275 { 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`, 280 }, 281 { 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`, 286 }, 287 288 // Methods 289 { 290 ctxt: main(` 291 package main 292 type C int 293 func (C) f() 294 func (C) g() 295 type D int 296 func (*D) f() 297 func (*D) g() 298 type I interface { f(); g() } 299 type J interface { I; h() } 300 var _ I = new(D) 301 var _ interface {f()} = C(0) 302 `), 303 from: "(main.I).f", to: "g", 304 want: `renaming this interface method "f" to "g".*` + 305 `would conflict with this method`, 306 }, 307 { 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"`, 312 }, 313 { 314 // type J interface { h; h() } is not a conflict, amusingly. 315 from: "main.I", to: "h", 316 want: `OK`, 317 }, 318 { 319 from: "(main.J).h", to: "f", 320 want: `renaming this interface method "h" to "f".*` + 321 `would conflict with this method`, 322 }, 323 { 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)`, 328 }, 329 { 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)`, 334 }, 335 { 336 from: "(main.I).f", to: "e", 337 want: `OK`, 338 }, 339 // Indirect C/I method coupling via another concrete type D. 340 { 341 ctxt: main(` 342 package main 343 type I interface { f() } 344 type C int 345 func (C) f() 346 type D struct{C} 347 var _ I = D{} 348 `), 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)`, 353 }, 354 // Renaming causes promoted method to become shadowed; C no longer satisfies I. 355 { 356 ctxt: main(` 357 package main 358 type I interface { f() } 359 type C struct { I } 360 func (C) g() int 361 var _ I = C{} 362 `), 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.*` + 367 `to \(main.C\).g`, 368 }, 369 // Renaming causes promoted method to become ambiguous; C no longer satisfies I. 370 { 371 ctxt: main(` 372 package main 373 type I interface{f()} 374 type C int 375 func (C) f() 376 type D int 377 func (D) g() 378 type E struct{C;D} 379 var _ I = E{} 380 `), 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.*` + 384 `with \(main.D\).g`, 385 }, 386 } { 387 var conflicts []string 388 reportError = func(posn token.Position, message string) { 389 conflicts = append(conflicts, message) 390 } 391 if test.ctxt != nil { 392 ctxt = test.ctxt 393 } 394 err := Main(ctxt, test.offset, test.from, test.to) 395 var prefix string 396 if test.offset == "" { 397 prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to) 398 } else { 399 prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to) 400 } 401 if err == ConflictError { 402 got := strings.Join(conflicts, "\n") 403 if false { 404 t.Logf("%s: %s", prefix, got) 405 } 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"+ 409 "Conflict:\t%s\n"+ 410 "Pattern: %s", 411 prefix, got, test.want) 412 } 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", 417 prefix, test.want) 418 } 419 } 420 } 421 422 func TestInvalidIdentifiers(t *testing.T) { 423 ctxt := fakeContext(map[string][]string{ 424 "main": {` 425 package main 426 427 func f() { } 428 `}}) 429 430 for _, test := range []struct { 431 from, to string // values of the -offset/-from and -to flags 432 want string // expected error message 433 }{ 434 { 435 from: "main.f", to: "_", 436 want: `-to "_": not a valid identifier`, 437 }, 438 { 439 from: "main.f", to: "123", 440 want: `-to "123": not a valid identifier`, 441 }, 442 { 443 from: "main.f", to: "for", 444 want: `-to "for": not a valid identifier`, 445 }, 446 { 447 from: "switch", to: "v", 448 want: `-from "switch": invalid expression`, 449 }, 450 } { 451 err := Main(ctxt, "", test.from, test.to) 452 prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to) 453 if err == nil { 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()) 457 } 458 } 459 } 460 461 func TestRewrites(t *testing.T) { 462 defer func(savedWriteFile func(string, []byte) error) { 463 writeFile = savedWriteFile 464 }(writeFile) 465 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 471 }{ 472 // Elimination of renaming import. 473 { 474 ctxt: fakeContext(map[string][]string{ 475 "foo": {`package foo; type T int`}, 476 "main": {`package main 477 478 import foo2 "foo" 479 480 var _ foo2.T 481 `}, 482 }), 483 from: "main::foo2", to: "foo", 484 want: map[string]string{ 485 "/go/src/main/0.go": `package main 486 487 import "foo" 488 489 var _ foo.T 490 `, 491 }, 492 }, 493 // Introduction of renaming import. 494 { 495 ctxt: fakeContext(map[string][]string{ 496 "foo": {`package foo; type T int`}, 497 "main": {`package main 498 499 import "foo" 500 501 var _ foo.T 502 `}, 503 }), 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 507 508 import foo2 "foo" 509 510 var _ foo2.T 511 `, 512 }, 513 }, 514 // Renaming of package-level member. 515 { 516 from: "foo.T", to: "U", 517 want: map[string]string{ 518 "/go/src/main/0.go": `package main 519 520 import "foo" 521 522 var _ foo.U 523 `, 524 "/go/src/foo/0.go": `package foo 525 526 type U int 527 `, 528 }, 529 }, 530 // Rename package-level func plus doc 531 { 532 ctxt: main(`package main 533 534 // Foo is a no-op. 535 // Calling Foo does nothing. 536 func Foo() { 537 } 538 `), 539 from: "main.Foo", to: "FooBar", 540 want: map[string]string{ 541 "/go/src/main/0.go": `package main 542 543 // FooBar is a no-op. 544 // Calling FooBar does nothing. 545 func FooBar() { 546 } 547 `, 548 }, 549 }, 550 // Rename method plus doc 551 { 552 ctxt: main(`package main 553 554 type Foo struct{} 555 556 // Bar does nothing. 557 func (Foo) Bar() { 558 } 559 `), 560 from: "main.Foo.Bar", to: "Baz", 561 want: map[string]string{ 562 "/go/src/main/0.go": `package main 563 564 type Foo struct{} 565 566 // Baz does nothing. 567 func (Foo) Baz() { 568 } 569 `, 570 }, 571 }, 572 // Rename type spec plus doc 573 { 574 ctxt: main(`package main 575 576 type ( 577 // Test but not Testing. 578 Test struct{} 579 ) 580 `), 581 from: "main.Test", to: "Type", 582 want: map[string]string{ 583 "/go/src/main/0.go": `package main 584 585 type ( 586 // Type but not Testing. 587 Type struct{} 588 ) 589 `, 590 }, 591 }, 592 // Rename type in gen decl plus doc 593 { 594 ctxt: main(`package main 595 596 // T is a test type. 597 type T struct{} 598 `), 599 from: "main.T", to: "Type", 600 want: map[string]string{ 601 "/go/src/main/0.go": `package main 602 603 // Type is a test type. 604 type Type struct{} 605 `, 606 }, 607 }, 608 // Rename value spec with doc 609 { 610 ctxt: main(`package main 611 612 const ( 613 // C is the speed of light. 614 C = 2.998e8 615 ) 616 `), 617 from: "main.C", to: "Lightspeed", 618 want: map[string]string{ 619 "/go/src/main/0.go": `package main 620 621 const ( 622 // Lightspeed is the speed of light. 623 Lightspeed = 2.998e8 624 ) 625 `, 626 }, 627 }, 628 // Rename value inside gen decl with doc 629 { 630 ctxt: main(`package main 631 632 var out *string 633 `), 634 from: "main.out", to: "discard", 635 want: map[string]string{ 636 "/go/src/main/0.go": `package main 637 638 var discard *string 639 `, 640 }, 641 }, 642 // Rename field plus doc 643 { 644 ctxt: main(`package main 645 646 type Struct struct { 647 // Field is a struct field. 648 Field string 649 } 650 `), 651 from: "main.Struct.Field", to: "Foo", 652 want: map[string]string{ 653 "/go/src/main/0.go": `package main 654 655 type Struct struct { 656 // Foo is a struct field. 657 Foo string 658 } 659 `, 660 }, 661 }, 662 // Label renamings. 663 { 664 ctxt: main(`package main 665 func f() { 666 loop: 667 loop := 0 668 go func() { 669 loop: 670 goto loop 671 }() 672 loop++ 673 goto loop 674 } 675 `), 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 679 680 func f() { 681 loop2: 682 loop := 0 683 go func() { 684 loop: 685 goto loop 686 }() 687 loop++ 688 goto loop2 689 } 690 `, 691 }, 692 }, 693 { 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 697 698 func f() { 699 loop: 700 loop := 0 701 go func() { 702 loop2: 703 goto loop2 704 }() 705 loop++ 706 goto loop 707 } 708 `, 709 }, 710 }, 711 // Renaming of type used as embedded field. 712 { 713 ctxt: main(`package main 714 715 type T int 716 type U struct { T } 717 718 var _ = U{}.T 719 `), 720 from: "main.T", to: "T2", 721 want: map[string]string{ 722 "/go/src/main/0.go": `package main 723 724 type T2 int 725 type U struct{ T2 } 726 727 var _ = U{}.T2 728 `, 729 }, 730 }, 731 // Renaming of embedded field. 732 { 733 ctxt: main(`package main 734 735 type T int 736 type U struct { T } 737 738 var _ = U{}.T 739 `), 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 743 744 type T2 int 745 type U struct{ T2 } 746 747 var _ = U{}.T2 748 `, 749 }, 750 }, 751 // Renaming of pointer embedded field. 752 { 753 ctxt: main(`package main 754 755 type T int 756 type U struct { *T } 757 758 var _ = U{}.T 759 `), 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 763 764 type T2 int 765 type U struct{ *T2 } 766 767 var _ = U{}.T2 768 `, 769 }, 770 }, 771 772 // Lexical scope tests. 773 { 774 ctxt: main(`package main 775 776 var y int 777 778 func f() { 779 print(y) 780 y := "" 781 print(y) 782 } 783 `), 784 from: "main.y", to: "x", 785 want: map[string]string{ 786 "/go/src/main/0.go": `package main 787 788 var x int 789 790 func f() { 791 print(x) 792 y := "" 793 print(y) 794 } 795 `, 796 }, 797 }, 798 { 799 from: "main.f::y", to: "x", 800 want: map[string]string{ 801 "/go/src/main/0.go": `package main 802 803 var y int 804 805 func f() { 806 print(y) 807 x := "" 808 print(x) 809 } 810 `, 811 }, 812 }, 813 // Renaming of typeswitch vars (a corner case). 814 { 815 ctxt: main(`package main 816 817 func f(z interface{}) { 818 switch y := z.(type) { 819 case int: 820 print(y) 821 default: 822 print(y) 823 } 824 } 825 `), 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 829 830 func f(z interface{}) { 831 switch x := z.(type) { 832 case int: 833 print(x) 834 default: 835 print(x) 836 } 837 } 838 `}, 839 }, 840 { 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 844 845 func f(z interface{}) { 846 switch x := z.(type) { 847 case int: 848 print(x) 849 default: 850 print(x) 851 } 852 } 853 `}, 854 }, 855 { 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 859 860 func f(z interface{}) { 861 switch x := z.(type) { 862 case int: 863 print(x) 864 default: 865 print(x) 866 } 867 } 868 `}, 869 }, 870 871 // Renaming of embedded field that is a qualified reference. 872 // (Regression test for bug 8924.) 873 { 874 ctxt: fakeContext(map[string][]string{ 875 "foo": {`package foo; type T int`}, 876 "main": {`package main 877 878 import "foo" 879 880 type _ struct{ *foo.T } 881 `}, 882 }), 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 886 887 type U int 888 `, 889 "/go/src/main/0.go": `package main 890 891 import "foo" 892 893 type _ struct{ *foo.U } 894 `, 895 }, 896 }, 897 898 // Renaming of embedded field that is a qualified reference with the '-from' flag. 899 // (Regression test for bug 12038.) 900 { 901 ctxt: fakeContext(map[string][]string{ 902 "foo": {`package foo; type T int`}, 903 "main": {`package main 904 905 import "foo" 906 907 type V struct{ *foo.T } 908 `}, 909 }), 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 913 914 type U int 915 `, 916 "/go/src/main/0.go": `package main 917 918 import "foo" 919 920 type V struct{ *foo.U } 921 `, 922 }, 923 }, 924 { 925 ctxt: fakeContext(map[string][]string{ 926 "foo": {`package foo; type T int`}, 927 "main": {`package main 928 929 import "foo" 930 931 type V struct{ foo.T } 932 `}, 933 }), 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 937 938 type U int 939 `, 940 "/go/src/main/0.go": `package main 941 942 import "foo" 943 944 type V struct{ foo.U } 945 `, 946 }, 947 }, 948 949 // Interface method renaming. 950 { 951 ctxt: fakeContext(map[string][]string{ 952 "main": {` 953 package main 954 type I interface { 955 f() 956 } 957 type J interface { f(); g() } 958 type A int 959 func (A) f() 960 type B int 961 func (B) f() 962 func (B) g() 963 type C int 964 func (C) f() 965 func (C) g() 966 var _, _ I = A(0), B(0) 967 var _, _ J = B(0), C(0) 968 `, 969 }, 970 }), 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 974 975 type I interface { 976 F() 977 } 978 type J interface { 979 F() 980 g() 981 } 982 type A int 983 984 func (A) F() 985 986 type B int 987 988 func (B) F() 989 func (B) g() 990 991 type C int 992 993 func (C) F() 994 func (C) g() 995 996 var _, _ I = A(0), B(0) 997 var _, _ J = B(0), C(0) 998 `, 999 }, 1000 }, 1001 { 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 1005 1006 type I interface { 1007 F() 1008 } 1009 type J interface { 1010 F() 1011 g() 1012 } 1013 type A int 1014 1015 func (A) F() 1016 1017 type B int 1018 1019 func (B) F() 1020 func (B) g() 1021 1022 type C int 1023 1024 func (C) F() 1025 func (C) g() 1026 1027 var _, _ I = A(0), B(0) 1028 var _, _ J = B(0), C(0) 1029 `, 1030 }, 1031 }, 1032 { 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 1036 1037 type I interface { 1038 f() 1039 } 1040 type J interface { 1041 f() 1042 G() 1043 } 1044 type A int 1045 1046 func (A) f() 1047 1048 type B int 1049 1050 func (B) f() 1051 func (B) G() 1052 1053 type C int 1054 1055 func (C) f() 1056 func (C) G() 1057 1058 var _, _ I = A(0), B(0) 1059 var _, _ J = B(0), C(0) 1060 `, 1061 }, 1062 }, 1063 // Indirect coupling of I.f to C.f from D->I assignment and anonymous field of D. 1064 { 1065 ctxt: fakeContext(map[string][]string{ 1066 "main": {` 1067 package main 1068 type I interface { 1069 f() 1070 } 1071 type C int 1072 func (C) f() 1073 type D struct{C} 1074 var _ I = D{} 1075 `, 1076 }, 1077 }), 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 1081 1082 type I interface { 1083 F() 1084 } 1085 type C int 1086 1087 func (C) F() 1088 1089 type D struct{ C } 1090 1091 var _ I = D{} 1092 `, 1093 }, 1094 }, 1095 // Interface embedded in struct. No conflict if C need not satisfy I. 1096 { 1097 ctxt: fakeContext(map[string][]string{ 1098 "main": {` 1099 package main 1100 type I interface { 1101 f() 1102 } 1103 type C struct{I} 1104 func (C) g() int 1105 var _ int = C{}.g() 1106 `, 1107 }, 1108 }), 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 1112 1113 type I interface { 1114 g() 1115 } 1116 type C struct{ I } 1117 1118 func (C) g() int 1119 1120 var _ int = C{}.g() 1121 `, 1122 }, 1123 }, 1124 // A type assertion causes method coupling iff signatures match. 1125 { 1126 ctxt: fakeContext(map[string][]string{ 1127 "main": {`package main 1128 type I interface{ 1129 f() 1130 } 1131 type J interface{ 1132 f() 1133 } 1134 var _ = I(nil).(J) 1135 `, 1136 }, 1137 }), 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 1141 1142 type I interface { 1143 g() 1144 } 1145 type J interface { 1146 g() 1147 } 1148 1149 var _ = I(nil).(J) 1150 `, 1151 }, 1152 }, 1153 // Impossible type assertion: no method coupling. 1154 { 1155 ctxt: fakeContext(map[string][]string{ 1156 "main": {`package main 1157 type I interface{ 1158 f() 1159 } 1160 type J interface{ 1161 f()int 1162 } 1163 var _ = I(nil).(J) 1164 `, 1165 }, 1166 }), 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 1170 1171 type I interface { 1172 g() 1173 } 1174 type J interface { 1175 f() int 1176 } 1177 1178 var _ = I(nil).(J) 1179 `, 1180 }, 1181 }, 1182 // Impossible type assertion: no method coupling C.f<->J.f. 1183 { 1184 ctxt: fakeContext(map[string][]string{ 1185 "main": {`package main 1186 type I interface{ 1187 f() 1188 } 1189 type C int 1190 func (C) f() 1191 type J interface{ 1192 f()int 1193 } 1194 var _ = I(C(0)).(J) 1195 `, 1196 }, 1197 }), 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 1201 1202 type I interface { 1203 g() 1204 } 1205 type C int 1206 1207 func (C) g() 1208 1209 type J interface { 1210 f() int 1211 } 1212 1213 var _ = I(C(0)).(J) 1214 `, 1215 }, 1216 }, 1217 // Progress after "soft" type errors (Go issue 14596). 1218 { 1219 ctxt: fakeContext(map[string][]string{ 1220 "main": {`package main 1221 1222 func main() { 1223 var unused, x int 1224 print(x) 1225 } 1226 `, 1227 }, 1228 }), 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 1232 1233 func main() { 1234 var unused, y int 1235 print(y) 1236 } 1237 `, 1238 }, 1239 }, 1240 } { 1241 if test.ctxt != nil { 1242 ctxt = test.ctxt 1243 } 1244 1245 got := make(map[string]string) 1246 writeFile = func(filename string, content []byte) error { 1247 got[filepath.ToSlash(filename)] = string(content) 1248 return nil 1249 } 1250 1251 err := Main(ctxt, test.offset, test.from, test.to) 1252 var prefix string 1253 if test.offset == "" { 1254 prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to) 1255 } else { 1256 prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to) 1257 } 1258 if err != nil { 1259 t.Errorf("%s: unexpected error: %s", prefix, err) 1260 continue 1261 } 1262 1263 for file, wantContent := range test.want { 1264 gotContent, ok := got[file] 1265 delete(got, file) 1266 if !ok { 1267 t.Errorf("%s: file %s not rewritten", prefix, file) 1268 continue 1269 } 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) 1273 } 1274 } 1275 // got should now be empty 1276 for file := range got { 1277 t.Errorf("%s: unexpected rewrite of file %s", prefix, file) 1278 } 1279 } 1280 } 1281 1282 func TestDiff(t *testing.T) { 1283 switch runtime.GOOS { 1284 case "windows": 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) 1288 } 1289 } 1290 case "plan9": 1291 t.Skipf("plan9 diff tool doesn't support -u flag") 1292 } 1293 testenv.NeedsTool(t, DiffCmd) 1294 testenv.NeedsTool(t, "go") // to locate the package to be renamed 1295 1296 defer func() { 1297 Diff = false 1298 stdout = os.Stdout 1299 }() 1300 Diff = true 1301 stdout = new(bytes.Buffer) 1302 1303 // Set up a fake GOPATH in a temporary directory, 1304 // and ensure we're in GOPATH mode. 1305 tmpdir, err := ioutil.TempDir("", "TestDiff") 1306 if err != nil { 1307 t.Fatal(err) 1308 } 1309 defer os.RemoveAll(tmpdir) 1310 buildCtx := build.Default 1311 buildCtx.GOPATH = tmpdir 1312 1313 pkgDir := filepath.Join(tmpdir, "src", "example.com", "rename") 1314 if err := os.MkdirAll(pkgDir, 0777); err != nil { 1315 t.Fatal(err) 1316 } 1317 1318 prevWD, err := os.Getwd() 1319 if err != nil { 1320 t.Fatal(err) 1321 } 1322 defer os.Chdir(prevWD) 1323 1324 if err := os.Chdir(pkgDir); err != nil { 1325 t.Fatal(err) 1326 } 1327 1328 const modFile = `module example.com/rename 1329 1330 go 1.15 1331 ` 1332 if err := ioutil.WriteFile(filepath.Join(pkgDir, "go.mod"), []byte(modFile), 0644); err != nil { 1333 t.Fatal(err) 1334 } 1335 1336 const goFile = `package rename 1337 1338 func justHereForTestingDiff() { 1339 justHereForTestingDiff() 1340 } 1341 ` 1342 if err := ioutil.WriteFile(filepath.Join(pkgDir, "rename_test.go"), []byte(goFile), 0644); err != nil { 1343 t.Fatal(err) 1344 } 1345 1346 if err := Main(&buildCtx, "", `"example.com/rename".justHereForTestingDiff`, "Foo"); err != nil { 1347 t.Fatal(err) 1348 } 1349 1350 // NB: there are tabs in the string literal! 1351 if !strings.Contains(stdout.(fmt.Stringer).String(), ` 1352 -func justHereForTestingDiff() { 1353 - justHereForTestingDiff() 1354 +func Foo() { 1355 + Foo() 1356 } 1357 `) { 1358 t.Errorf("unexpected diff:\n<<%s>>", stdout) 1359 } 1360 } 1361 1362 // --------------------------------------------------------------------- 1363 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 1373 } 1374 pkgs2[path] = filemap 1375 } 1376 return buildutil.FakeContext(pkgs2) 1377 } 1378 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}}) 1382 }