golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/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 "os" 13 "os/exec" 14 "path/filepath" 15 "regexp" 16 "runtime" 17 "strings" 18 "testing" 19 20 "golang.org/x/tools/go/buildutil" 21 "golang.org/x/tools/internal/aliases" 22 "golang.org/x/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 alias bool // requires materialized aliases 472 }{ 473 // Elimination of renaming import. 474 { 475 ctxt: fakeContext(map[string][]string{ 476 "foo": {`package foo; type T int`}, 477 "main": {`package main 478 479 import foo2 "foo" 480 481 var _ foo2.T 482 `}, 483 }), 484 from: "main::foo2", to: "foo", 485 want: map[string]string{ 486 "/go/src/main/0.go": `package main 487 488 import "foo" 489 490 var _ foo.T 491 `, 492 }, 493 }, 494 // Introduction of renaming import. 495 { 496 ctxt: fakeContext(map[string][]string{ 497 "foo": {`package foo; type T int`}, 498 "main": {`package main 499 500 import "foo" 501 502 var _ foo.T 503 `}, 504 }), 505 offset: "/go/src/main/0.go:#36", to: "foo2", // the "foo" in foo.T 506 want: map[string]string{ 507 "/go/src/main/0.go": `package main 508 509 import foo2 "foo" 510 511 var _ foo2.T 512 `, 513 }, 514 }, 515 // Renaming of package-level member. 516 { 517 from: "foo.T", to: "U", 518 want: map[string]string{ 519 "/go/src/main/0.go": `package main 520 521 import "foo" 522 523 var _ foo.U 524 `, 525 "/go/src/foo/0.go": `package foo 526 527 type U int 528 `, 529 }, 530 }, 531 // Rename package-level func plus doc 532 { 533 ctxt: main(`package main 534 535 // Foo is a no-op. 536 // Calling Foo does nothing. 537 func Foo() { 538 } 539 `), 540 from: "main.Foo", to: "FooBar", 541 want: map[string]string{ 542 "/go/src/main/0.go": `package main 543 544 // FooBar is a no-op. 545 // Calling FooBar does nothing. 546 func FooBar() { 547 } 548 `, 549 }, 550 }, 551 // Rename method plus doc 552 { 553 ctxt: main(`package main 554 555 type Foo struct{} 556 557 // Bar does nothing. 558 func (Foo) Bar() { 559 } 560 `), 561 from: "main.Foo.Bar", to: "Baz", 562 want: map[string]string{ 563 "/go/src/main/0.go": `package main 564 565 type Foo struct{} 566 567 // Baz does nothing. 568 func (Foo) Baz() { 569 } 570 `, 571 }, 572 }, 573 // Rename type spec plus doc 574 { 575 ctxt: main(`package main 576 577 type ( 578 // Test but not Testing. 579 Test struct{} 580 ) 581 `), 582 from: "main.Test", to: "Type", 583 want: map[string]string{ 584 "/go/src/main/0.go": `package main 585 586 type ( 587 // Type but not Testing. 588 Type struct{} 589 ) 590 `, 591 }, 592 }, 593 // Rename type in gen decl plus doc 594 { 595 ctxt: main(`package main 596 597 // T is a test type. 598 type T struct{} 599 `), 600 from: "main.T", to: "Type", 601 want: map[string]string{ 602 "/go/src/main/0.go": `package main 603 604 // Type is a test type. 605 type Type struct{} 606 `, 607 }, 608 }, 609 // Rename value spec with doc 610 { 611 ctxt: main(`package main 612 613 const ( 614 // C is the speed of light. 615 C = 2.998e8 616 ) 617 `), 618 from: "main.C", to: "Lightspeed", 619 want: map[string]string{ 620 "/go/src/main/0.go": `package main 621 622 const ( 623 // Lightspeed is the speed of light. 624 Lightspeed = 2.998e8 625 ) 626 `, 627 }, 628 }, 629 // Rename value inside gen decl with doc 630 { 631 ctxt: main(`package main 632 633 var out *string 634 `), 635 from: "main.out", to: "discard", 636 want: map[string]string{ 637 "/go/src/main/0.go": `package main 638 639 var discard *string 640 `, 641 }, 642 }, 643 // Rename field plus doc 644 { 645 ctxt: main(`package main 646 647 type Struct struct { 648 // Field is a struct field. 649 Field string 650 } 651 `), 652 from: "main.Struct.Field", to: "Foo", 653 want: map[string]string{ 654 "/go/src/main/0.go": `package main 655 656 type Struct struct { 657 // Foo is a struct field. 658 Foo string 659 } 660 `, 661 }, 662 }, 663 // Label renamings. 664 { 665 ctxt: main(`package main 666 func f() { 667 loop: 668 loop := 0 669 go func() { 670 loop: 671 goto loop 672 }() 673 loop++ 674 goto loop 675 } 676 `), 677 offset: "/go/src/main/0.go:#25", to: "loop2", // def of outer label "loop" 678 want: map[string]string{ 679 "/go/src/main/0.go": `package main 680 681 func f() { 682 loop2: 683 loop := 0 684 go func() { 685 loop: 686 goto loop 687 }() 688 loop++ 689 goto loop2 690 } 691 `, 692 }, 693 }, 694 { 695 offset: "/go/src/main/0.go:#70", to: "loop2", // ref to inner label "loop" 696 want: map[string]string{ 697 "/go/src/main/0.go": `package main 698 699 func f() { 700 loop: 701 loop := 0 702 go func() { 703 loop2: 704 goto loop2 705 }() 706 loop++ 707 goto loop 708 } 709 `, 710 }, 711 }, 712 // Renaming of type used as embedded field. 713 { 714 ctxt: main(`package main 715 716 type T int 717 type U struct { T } 718 719 var _ = U{}.T 720 `), 721 from: "main.T", to: "T2", 722 want: map[string]string{ 723 "/go/src/main/0.go": `package main 724 725 type T2 int 726 type U struct{ T2 } 727 728 var _ = U{}.T2 729 `, 730 }, 731 }, 732 // Renaming of embedded field. 733 { 734 ctxt: main(`package main 735 736 type T int 737 type U struct { T } 738 739 var _ = U{}.T 740 `), 741 offset: "/go/src/main/0.go:#58", to: "T2", // T in "U{}.T" 742 want: map[string]string{ 743 "/go/src/main/0.go": `package main 744 745 type T2 int 746 type U struct{ T2 } 747 748 var _ = U{}.T2 749 `, 750 }, 751 }, 752 // Renaming of pointer embedded field. 753 { 754 ctxt: main(`package main 755 756 type T int 757 type U struct { *T } 758 759 var _ = U{}.T 760 `), 761 offset: "/go/src/main/0.go:#59", to: "T2", // T in "U{}.T" 762 want: map[string]string{ 763 "/go/src/main/0.go": `package main 764 765 type T2 int 766 type U struct{ *T2 } 767 768 var _ = U{}.T2 769 `, 770 }, 771 }, 772 // Renaming of embedded field alias. 773 { 774 alias: true, 775 ctxt: main(`package main 776 777 type T int 778 type A = T 779 type U struct{ A } 780 781 var _ = U{}.A 782 var a A 783 `), 784 offset: "/go/src/main/0.go:#68", to: "A2", // A in "U{}.A" 785 want: map[string]string{ 786 "/go/src/main/0.go": `package main 787 788 type T int 789 type A2 = T 790 type U struct{ A2 } 791 792 var _ = U{}.A2 793 var a A2 794 `, 795 }, 796 }, 797 // Renaming of embedded field pointer to alias. 798 { 799 alias: true, 800 ctxt: main(`package main 801 802 type T int 803 type A = T 804 type U struct{ *A } 805 806 var _ = U{}.A 807 var a A 808 `), 809 offset: "/go/src/main/0.go:#69", to: "A2", // A in "U{}.A" 810 want: map[string]string{ 811 "/go/src/main/0.go": `package main 812 813 type T int 814 type A2 = T 815 type U struct{ *A2 } 816 817 var _ = U{}.A2 818 var a A2 819 `, 820 }, 821 }, 822 // Renaming of alias 823 { 824 ctxt: main(`package main 825 826 type A = int 827 828 func _() A { 829 return A(0) 830 } 831 `), 832 offset: "/go/src/main/0.go:#49", to: "A2", // A in "A(0)" 833 want: map[string]string{ 834 "/go/src/main/0.go": `package main 835 836 type A2 = int 837 838 func _() A2 { 839 return A2(0) 840 } 841 `, 842 }, 843 }, 844 845 // Lexical scope tests. 846 { 847 ctxt: main(`package main 848 849 var y int 850 851 func f() { 852 print(y) 853 y := "" 854 print(y) 855 } 856 `), 857 from: "main.y", to: "x", 858 want: map[string]string{ 859 "/go/src/main/0.go": `package main 860 861 var x int 862 863 func f() { 864 print(x) 865 y := "" 866 print(y) 867 } 868 `, 869 }, 870 }, 871 { 872 from: "main.f::y", to: "x", 873 want: map[string]string{ 874 "/go/src/main/0.go": `package main 875 876 var y int 877 878 func f() { 879 print(y) 880 x := "" 881 print(x) 882 } 883 `, 884 }, 885 }, 886 // Renaming of typeswitch vars (a corner case). 887 { 888 ctxt: main(`package main 889 890 func f(z interface{}) { 891 switch y := z.(type) { 892 case int: 893 print(y) 894 default: 895 print(y) 896 } 897 } 898 `), 899 offset: "/go/src/main/0.go:#46", to: "x", // def of y 900 want: map[string]string{ 901 "/go/src/main/0.go": `package main 902 903 func f(z interface{}) { 904 switch x := z.(type) { 905 case int: 906 print(x) 907 default: 908 print(x) 909 } 910 } 911 `}, 912 }, 913 { 914 offset: "/go/src/main/0.go:#81", to: "x", // ref of y in case int 915 want: map[string]string{ 916 "/go/src/main/0.go": `package main 917 918 func f(z interface{}) { 919 switch x := z.(type) { 920 case int: 921 print(x) 922 default: 923 print(x) 924 } 925 } 926 `}, 927 }, 928 { 929 offset: "/go/src/main/0.go:#102", to: "x", // ref of y in default case 930 want: map[string]string{ 931 "/go/src/main/0.go": `package main 932 933 func f(z interface{}) { 934 switch x := z.(type) { 935 case int: 936 print(x) 937 default: 938 print(x) 939 } 940 } 941 `}, 942 }, 943 944 // Renaming of embedded field that is a qualified reference. 945 // (Regression test for bug 8924.) 946 { 947 ctxt: fakeContext(map[string][]string{ 948 "foo": {`package foo; type T int`}, 949 "main": {`package main 950 951 import "foo" 952 953 type _ struct{ *foo.T } 954 `}, 955 }), 956 offset: "/go/src/main/0.go:#48", to: "U", // the "T" in *foo.T 957 want: map[string]string{ 958 "/go/src/foo/0.go": `package foo 959 960 type U int 961 `, 962 "/go/src/main/0.go": `package main 963 964 import "foo" 965 966 type _ struct{ *foo.U } 967 `, 968 }, 969 }, 970 971 // Renaming of embedded field that is a qualified reference with the '-from' flag. 972 // (Regression test for bug 12038.) 973 { 974 ctxt: fakeContext(map[string][]string{ 975 "foo": {`package foo; type T int`}, 976 "main": {`package main 977 978 import "foo" 979 980 type V struct{ *foo.T } 981 `}, 982 }), 983 from: "(main.V).T", to: "U", // the "T" in *foo.T 984 want: map[string]string{ 985 "/go/src/foo/0.go": `package foo 986 987 type U int 988 `, 989 "/go/src/main/0.go": `package main 990 991 import "foo" 992 993 type V struct{ *foo.U } 994 `, 995 }, 996 }, 997 { 998 ctxt: fakeContext(map[string][]string{ 999 "foo": {`package foo; type T int`}, 1000 "main": {`package main 1001 1002 import "foo" 1003 1004 type V struct{ foo.T } 1005 `}, 1006 }), 1007 from: "(main.V).T", to: "U", // the "T" in *foo.T 1008 want: map[string]string{ 1009 "/go/src/foo/0.go": `package foo 1010 1011 type U int 1012 `, 1013 "/go/src/main/0.go": `package main 1014 1015 import "foo" 1016 1017 type V struct{ foo.U } 1018 `, 1019 }, 1020 }, 1021 1022 // Interface method renaming. 1023 { 1024 ctxt: fakeContext(map[string][]string{ 1025 "main": {` 1026 package main 1027 type I interface { 1028 f() 1029 } 1030 type J interface { f(); g() } 1031 type A int 1032 func (A) f() 1033 type B int 1034 func (B) f() 1035 func (B) g() 1036 type C int 1037 func (C) f() 1038 func (C) g() 1039 var _, _ I = A(0), B(0) 1040 var _, _ J = B(0), C(0) 1041 `, 1042 }, 1043 }), 1044 offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f 1045 want: map[string]string{ 1046 "/go/src/main/0.go": `package main 1047 1048 type I interface { 1049 F() 1050 } 1051 type J interface { 1052 F() 1053 g() 1054 } 1055 type A int 1056 1057 func (A) F() 1058 1059 type B int 1060 1061 func (B) F() 1062 func (B) g() 1063 1064 type C int 1065 1066 func (C) F() 1067 func (C) g() 1068 1069 var _, _ I = A(0), B(0) 1070 var _, _ J = B(0), C(0) 1071 `, 1072 }, 1073 }, 1074 { 1075 offset: "/go/src/main/0.go:#59", to: "F", // abstract method J.f 1076 want: map[string]string{ 1077 "/go/src/main/0.go": `package main 1078 1079 type I interface { 1080 F() 1081 } 1082 type J interface { 1083 F() 1084 g() 1085 } 1086 type A int 1087 1088 func (A) F() 1089 1090 type B int 1091 1092 func (B) F() 1093 func (B) g() 1094 1095 type C int 1096 1097 func (C) F() 1098 func (C) g() 1099 1100 var _, _ I = A(0), B(0) 1101 var _, _ J = B(0), C(0) 1102 `, 1103 }, 1104 }, 1105 { 1106 offset: "/go/src/main/0.go:#64", to: "G", // abstract method J.g 1107 want: map[string]string{ 1108 "/go/src/main/0.go": `package main 1109 1110 type I interface { 1111 f() 1112 } 1113 type J interface { 1114 f() 1115 G() 1116 } 1117 type A int 1118 1119 func (A) f() 1120 1121 type B int 1122 1123 func (B) f() 1124 func (B) G() 1125 1126 type C int 1127 1128 func (C) f() 1129 func (C) G() 1130 1131 var _, _ I = A(0), B(0) 1132 var _, _ J = B(0), C(0) 1133 `, 1134 }, 1135 }, 1136 // Indirect coupling of I.f to C.f from D->I assignment and anonymous field of D. 1137 { 1138 ctxt: fakeContext(map[string][]string{ 1139 "main": {` 1140 package main 1141 type I interface { 1142 f() 1143 } 1144 type C int 1145 func (C) f() 1146 type D struct{C} 1147 var _ I = D{} 1148 `, 1149 }, 1150 }), 1151 offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f 1152 want: map[string]string{ 1153 "/go/src/main/0.go": `package main 1154 1155 type I interface { 1156 F() 1157 } 1158 type C int 1159 1160 func (C) F() 1161 1162 type D struct{ C } 1163 1164 var _ I = D{} 1165 `, 1166 }, 1167 }, 1168 // Interface embedded in struct. No conflict if C need not satisfy I. 1169 { 1170 ctxt: fakeContext(map[string][]string{ 1171 "main": {` 1172 package main 1173 type I interface { 1174 f() 1175 } 1176 type C struct{I} 1177 func (C) g() int 1178 var _ int = C{}.g() 1179 `, 1180 }, 1181 }), 1182 offset: "/go/src/main/0.go:#34", to: "g", // abstract method I.f 1183 want: map[string]string{ 1184 "/go/src/main/0.go": `package main 1185 1186 type I interface { 1187 g() 1188 } 1189 type C struct{ I } 1190 1191 func (C) g() int 1192 1193 var _ int = C{}.g() 1194 `, 1195 }, 1196 }, 1197 // A type assertion causes method coupling iff signatures match. 1198 { 1199 ctxt: fakeContext(map[string][]string{ 1200 "main": {`package main 1201 type I interface{ 1202 f() 1203 } 1204 type J interface{ 1205 f() 1206 } 1207 var _ = I(nil).(J) 1208 `, 1209 }, 1210 }), 1211 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f 1212 want: map[string]string{ 1213 "/go/src/main/0.go": `package main 1214 1215 type I interface { 1216 g() 1217 } 1218 type J interface { 1219 g() 1220 } 1221 1222 var _ = I(nil).(J) 1223 `, 1224 }, 1225 }, 1226 // Impossible type assertion: no method coupling. 1227 { 1228 ctxt: fakeContext(map[string][]string{ 1229 "main": {`package main 1230 type I interface{ 1231 f() 1232 } 1233 type J interface{ 1234 f()int 1235 } 1236 var _ = I(nil).(J) 1237 `, 1238 }, 1239 }), 1240 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f 1241 want: map[string]string{ 1242 "/go/src/main/0.go": `package main 1243 1244 type I interface { 1245 g() 1246 } 1247 type J interface { 1248 f() int 1249 } 1250 1251 var _ = I(nil).(J) 1252 `, 1253 }, 1254 }, 1255 // Impossible type assertion: no method coupling C.f<->J.f. 1256 { 1257 ctxt: fakeContext(map[string][]string{ 1258 "main": {`package main 1259 type I interface{ 1260 f() 1261 } 1262 type C int 1263 func (C) f() 1264 type J interface{ 1265 f()int 1266 } 1267 var _ = I(C(0)).(J) 1268 `, 1269 }, 1270 }), 1271 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f 1272 want: map[string]string{ 1273 "/go/src/main/0.go": `package main 1274 1275 type I interface { 1276 g() 1277 } 1278 type C int 1279 1280 func (C) g() 1281 1282 type J interface { 1283 f() int 1284 } 1285 1286 var _ = I(C(0)).(J) 1287 `, 1288 }, 1289 }, 1290 // Progress after "soft" type errors (Go issue 14596). 1291 { 1292 ctxt: fakeContext(map[string][]string{ 1293 "main": {`package main 1294 1295 func main() { 1296 var unused, x int 1297 print(x) 1298 } 1299 `, 1300 }, 1301 }), 1302 offset: "/go/src/main/0.go:#54", to: "y", // var x 1303 want: map[string]string{ 1304 "/go/src/main/0.go": `package main 1305 1306 func main() { 1307 var unused, y int 1308 print(y) 1309 } 1310 `, 1311 }, 1312 }, 1313 } { 1314 if test.ctxt != nil { 1315 ctxt = test.ctxt 1316 } 1317 1318 got := make(map[string]string) 1319 writeFile = func(filename string, content []byte) error { 1320 got[filepath.ToSlash(filename)] = string(content) 1321 return nil 1322 } 1323 1324 // Skip tests that require aliases when not enables. 1325 // (No test requires _no_ aliases, 1326 // so there is no contrapositive case.) 1327 if test.alias && !aliases.Enabled() { 1328 t.Log("test requires aliases") 1329 continue 1330 } 1331 1332 err := Main(ctxt, test.offset, test.from, test.to) 1333 var prefix string 1334 if test.offset == "" { 1335 prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to) 1336 } else { 1337 prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to) 1338 } 1339 if err != nil { 1340 t.Errorf("%s: unexpected error: %s", prefix, err) 1341 continue 1342 } 1343 1344 for file, wantContent := range test.want { 1345 gotContent, ok := got[file] 1346 delete(got, file) 1347 if !ok { 1348 t.Errorf("%s: file %s not rewritten", prefix, file) 1349 continue 1350 } 1351 if gotContent != wantContent { 1352 t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+ 1353 "want <<<%s>>>", prefix, file, gotContent, wantContent) 1354 } 1355 } 1356 // got should now be empty 1357 for file := range got { 1358 t.Errorf("%s: unexpected rewrite of file %s", prefix, file) 1359 } 1360 } 1361 } 1362 1363 func TestDiff(t *testing.T) { 1364 switch runtime.GOOS { 1365 case "windows": 1366 if os.Getenv("GO_BUILDER_NAME") != "" { 1367 if _, err := exec.LookPath(DiffCmd); err != nil { 1368 t.Skipf("diff tool non-existent for %s on builders", runtime.GOOS) 1369 } 1370 } 1371 case "plan9": 1372 t.Skipf("plan9 diff tool doesn't support -u flag") 1373 } 1374 testenv.NeedsTool(t, DiffCmd) 1375 testenv.NeedsTool(t, "go") // to locate the package to be renamed 1376 1377 defer func() { 1378 Diff = false 1379 stdout = os.Stdout 1380 }() 1381 Diff = true 1382 stdout = new(bytes.Buffer) 1383 1384 // Set up a fake GOPATH in a temporary directory, 1385 // and ensure we're in GOPATH mode. 1386 tmpdir, err := os.MkdirTemp("", "TestDiff") 1387 if err != nil { 1388 t.Fatal(err) 1389 } 1390 defer os.RemoveAll(tmpdir) 1391 buildCtx := build.Default 1392 buildCtx.GOPATH = tmpdir 1393 1394 pkgDir := filepath.Join(tmpdir, "src", "example.com", "rename") 1395 if err := os.MkdirAll(pkgDir, 0777); err != nil { 1396 t.Fatal(err) 1397 } 1398 1399 prevWD, err := os.Getwd() 1400 if err != nil { 1401 t.Fatal(err) 1402 } 1403 defer os.Chdir(prevWD) 1404 1405 if err := os.Chdir(pkgDir); err != nil { 1406 t.Fatal(err) 1407 } 1408 1409 const modFile = `module example.com/rename 1410 1411 go 1.15 1412 ` 1413 if err := os.WriteFile(filepath.Join(pkgDir, "go.mod"), []byte(modFile), 0644); err != nil { 1414 t.Fatal(err) 1415 } 1416 1417 const goFile = `package rename 1418 1419 func justHereForTestingDiff() { 1420 justHereForTestingDiff() 1421 } 1422 ` 1423 if err := os.WriteFile(filepath.Join(pkgDir, "rename_test.go"), []byte(goFile), 0644); err != nil { 1424 t.Fatal(err) 1425 } 1426 1427 if err := Main(&buildCtx, "", `"example.com/rename".justHereForTestingDiff`, "Foo"); err != nil { 1428 t.Fatal(err) 1429 } 1430 1431 // NB: there are tabs in the string literal! 1432 if !strings.Contains(stdout.(fmt.Stringer).String(), ` 1433 -func justHereForTestingDiff() { 1434 - justHereForTestingDiff() 1435 +func Foo() { 1436 + Foo() 1437 } 1438 `) { 1439 t.Errorf("unexpected diff:\n<<%s>>", stdout) 1440 } 1441 } 1442 1443 // --------------------------------------------------------------------- 1444 1445 // Simplifying wrapper around buildutil.FakeContext for packages whose 1446 // filenames are sequentially numbered (%d.go). pkgs maps a package 1447 // import path to its list of file contents. 1448 func fakeContext(pkgs map[string][]string) *build.Context { 1449 pkgs2 := make(map[string]map[string]string) 1450 for path, files := range pkgs { 1451 filemap := make(map[string]string) 1452 for i, contents := range files { 1453 filemap[fmt.Sprintf("%d.go", i)] = contents 1454 } 1455 pkgs2[path] = filemap 1456 } 1457 return buildutil.FakeContext(pkgs2) 1458 } 1459 1460 // helper for single-file main packages with no imports. 1461 func main(content string) *build.Context { 1462 return fakeContext(map[string][]string{"main": {content}}) 1463 }