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 5package rename 6 7import ( 8 "bytes" 9 "fmt" 10 "go/build" 11 "go/token" 12 "os" 13 "path/filepath" 14 "regexp" 15 "runtime" 16 "strings" 17 "testing" 18 19 "golang.org/x/tools/go/buildutil" 20) 21 22// TODO(adonovan): test reported source positions, somehow. 23 24func TestConflicts(t *testing.T) { 25 defer func(savedWriteFile func(string, []byte) error, savedReportError func(token.Position, string)) { 26 writeFile = savedWriteFile 27 reportError = savedReportError 28 }(writeFile, reportError) 29 writeFile = func(string, []byte) error { return nil } 30 31 var ctxt *build.Context 32 for _, test := range []struct { 33 ctxt *build.Context // nil => use previous 34 offset, from, to string // values of the -offset/-from and -to flags 35 want string // regexp to match conflict errors, or "OK" 36 }{ 37 // init() checks 38 { 39 ctxt: fakeContext(map[string][]string{ 40 "fmt": {`package fmt; type Stringer interface { String() }`}, 41 "main": {` 42package main 43 44import foo "fmt" 45 46var v foo.Stringer 47 48func f() { v.String(); f() } 49`, 50 `package main; var w int`}, 51 }), 52 from: "main.v", to: "init", 53 want: `you cannot have a var at package level named "init"`, 54 }, 55 { 56 from: "main.f", to: "init", 57 want: `renaming this func "f" to "init" would make it a package initializer.*` + 58 `but references to it exist`, 59 }, 60 { 61 from: "/go/src/main/0.go::foo", to: "init", 62 want: `"init" is not a valid imported package name`, 63 }, 64 65 // Export checks 66 { 67 from: "fmt.Stringer", to: "stringer", 68 want: `renaming this type "Stringer" to "stringer" would make it unexported.*` + 69 `breaking references from packages such as "main"`, 70 }, 71 { 72 from: "(fmt.Stringer).String", to: "string", 73 want: `renaming this method "String" to "string" would make it unexported.*` + 74 `breaking references from packages such as "main"`, 75 }, 76 77 // Lexical scope checks 78 { 79 // file/package conflict, same file 80 from: "main.v", to: "foo", 81 want: `renaming this var "v" to "foo" would conflict.*` + 82 `with this imported package name`, 83 }, 84 { 85 // file/package conflict, same file 86 from: "main::foo", to: "v", 87 want: `renaming this imported package name "foo" to "v" would conflict.*` + 88 `with this package member var`, 89 }, 90 { 91 // file/package conflict, different files 92 from: "main.w", to: "foo", 93 want: `renaming this var "w" to "foo" would conflict.*` + 94 `with this imported package name`, 95 }, 96 { 97 // file/package conflict, different files 98 from: "main::foo", to: "w", 99 want: `renaming this imported package name "foo" to "w" would conflict.*` + 100 `with this package member var`, 101 }, 102 { 103 ctxt: main(` 104package main 105 106var x, z int 107 108func f(y int) { 109 print(x) 110 print(y) 111} 112 113func g(w int) { 114 print(x) 115 x := 1 116 print(x) 117}`), 118 from: "main.x", to: "y", 119 want: `renaming this var "x" to "y".*` + 120 `would cause this reference to become shadowed.*` + 121 `by this intervening var definition`, 122 }, 123 { 124 from: "main.g::x", to: "w", 125 want: `renaming this var "x" to "w".*` + 126 `conflicts with var in same block`, 127 }, 128 { 129 from: "main.f::y", to: "x", 130 want: `renaming this var "y" to "x".*` + 131 `would shadow this reference.*` + 132 `to the var declared here`, 133 }, 134 { 135 from: "main.g::w", to: "x", 136 want: `renaming this var "w" to "x".*` + 137 `conflicts with var in same block`, 138 }, 139 { 140 from: "main.z", to: "y", want: "OK", 141 }, 142 143 // Label checks 144 { 145 ctxt: main(` 146package main 147 148func f() { 149foo: 150 goto foo 151bar: 152 goto bar 153 func(x int) { 154 wiz: 155 goto wiz 156 }(0) 157} 158`), 159 from: "main.f::foo", to: "bar", 160 want: `renaming this label "foo" to "bar".*` + 161 `would conflict with this one`, 162 }, 163 { 164 from: "main.f::foo", to: "wiz", want: "OK", 165 }, 166 { 167 from: "main.f::wiz", to: "x", want: "OK", 168 }, 169 { 170 from: "main.f::x", to: "wiz", want: "OK", 171 }, 172 { 173 from: "main.f::wiz", to: "foo", want: "OK", 174 }, 175 176 // Struct fields 177 { 178 ctxt: main(` 179package main 180 181type U struct { u int } 182type V struct { v int } 183 184func (V) x() {} 185 186type W (struct { 187 U 188 V 189 w int 190}) 191 192func f() { 193 var w W 194 print(w.u) // NB: there is no selection of w.v 195 var _ struct { yy, zz int } 196} 197`), 198 // field/field conflict in named struct declaration 199 from: "(main.W).U", to: "w", 200 want: `renaming this field "U" to "w".*` + 201 `would conflict with this field`, 202 }, 203 { 204 // rename type used as embedded field 205 // => rename field 206 // => field/field conflict 207 // This is an entailed renaming; 208 // it would be nice if we checked source positions. 209 from: "main.U", to: "w", 210 want: `renaming this field "U" to "w".*` + 211 `would conflict with this field`, 212 }, 213 { 214 // field/field conflict in unnamed struct declaration 215 from: "main.f::zz", to: "yy", 216 want: `renaming this field "zz" to "yy".*` + 217 `would conflict with this field`, 218 }, 219 220 // Now we test both directions of (u,v) (u,w) (v,w) (u,x) (v,x). 221 // Too bad we don't test position info... 222 { 223 // field/field ambiguity at same promotion level ('from' selection) 224 from: "(main.U).u", to: "v", 225 want: `renaming this field "u" to "v".*` + 226 `would make this reference ambiguous.*` + 227 `with this field`, 228 }, 229 { 230 // field/field ambiguity at same promotion level ('to' selection) 231 from: "(main.V).v", to: "u", 232 want: `renaming this field "v" to "u".*` + 233 `would make this reference ambiguous.*` + 234 `with this field`, 235 }, 236 { 237 // field/method conflict at different promotion level ('from' selection) 238 from: "(main.U).u", to: "w", 239 want: `renaming this field "u" to "w".*` + 240 `would change the referent of this selection.*` + 241 `of this field`, 242 }, 243 { 244 // field/field shadowing at different promotion levels ('to' selection) 245 from: "(main.W).w", to: "u", 246 want: `renaming this field "w" to "u".*` + 247 `would shadow this selection.*` + 248 `of the field declared here`, 249 }, 250 { 251 from: "(main.V).v", to: "w", 252 want: "OK", // since no selections are made ambiguous 253 }, 254 { 255 from: "(main.W).w", to: "v", 256 want: "OK", // since no selections are made ambiguous 257 }, 258 { 259 // field/method ambiguity at same promotion level ('from' selection) 260 from: "(main.U).u", to: "x", 261 want: `renaming this field "u" to "x".*` + 262 `would make this reference ambiguous.*` + 263 `with this method`, 264 }, 265 { 266 // field/field ambiguity at same promotion level ('to' selection) 267 from: "(main.V).x", to: "u", 268 want: `renaming this method "x" to "u".*` + 269 `would make this reference ambiguous.*` + 270 `with this field`, 271 }, 272 { 273 // field/method conflict at named struct declaration 274 from: "(main.V).v", to: "x", 275 want: `renaming this field "v" to "x".*` + 276 `would conflict with this method`, 277 }, 278 { 279 // field/method conflict at named struct declaration 280 from: "(main.V).x", to: "v", 281 want: `renaming this method "x" to "v".*` + 282 `would conflict with this field`, 283 }, 284 285 // Methods 286 { 287 ctxt: main(` 288package main 289type C int 290func (C) f() 291func (C) g() 292type D int 293func (*D) f() 294func (*D) g() 295type I interface { f(); g() } 296type J interface { I; h() } 297var _ I = new(D) 298var _ interface {f()} = C(0) 299`), 300 from: "(main.I).f", to: "g", 301 want: `renaming this interface method "f" to "g".*` + 302 `would conflict with this method`, 303 }, 304 { 305 from: `("main".I).f`, to: "h", // NB: exercises quoted import paths too 306 want: `renaming this interface method "f" to "h".*` + 307 `would conflict with this method.*` + 308 `in named interface type "J"`, 309 }, 310 { 311 // type J interface { h; h() } is not a conflict, amusingly. 312 from: "main.I", to: "h", 313 want: `OK`, 314 }, 315 { 316 from: "(main.J).h", to: "f", 317 want: `renaming this interface method "h" to "f".*` + 318 `would conflict with this method`, 319 }, 320 { 321 from: "(main.C).f", to: "e", 322 want: `renaming this method "f" to "e".*` + 323 `would make main.C no longer assignable to interface{f..}.*` + 324 `(rename interface{f..}.f if you intend to change both types)`, 325 }, 326 { 327 from: "(main.D).g", to: "e", 328 want: `renaming this method "g" to "e".*` + 329 `would make \*main.D no longer assignable to interface I.*` + 330 `(rename main.I.g if you intend to change both types)`, 331 }, 332 { 333 from: "(main.I).f", to: "e", 334 want: `OK`, 335 }, 336 // Indirect C/I method coupling via another concrete type D. 337 { 338 ctxt: main(` 339package main 340type I interface { f() } 341type C int 342func (C) f() 343type D struct{C} 344var _ I = D{} 345`), 346 from: "(main.C).f", to: "F", 347 want: `renaming this method "f" to "F".*` + 348 `would make main.D no longer assignable to interface I.*` + 349 `(rename main.I.f if you intend to change both types)`, 350 }, 351 // Renaming causes promoted method to become shadowed; C no longer satisfies I. 352 { 353 ctxt: main(` 354package main 355type I interface { f() } 356type C struct { I } 357func (C) g() int 358var _ I = C{} 359`), 360 from: "main.I.f", to: "g", 361 want: `renaming this method "f" to "g".*` + 362 `would change the g method of main.C invoked via interface main.I.*` + 363 `from \(main.I\).g.*` + 364 `to \(main.C\).g`, 365 }, 366 // Renaming causes promoted method to become ambiguous; C no longer satisfies I. 367 { 368 ctxt: main(` 369package main 370type I interface{f()} 371type C int 372func (C) f() 373type D int 374func (D) g() 375type E struct{C;D} 376var _ I = E{} 377`), 378 from: "main.I.f", to: "g", 379 want: `renaming this method "f" to "g".*` + 380 `would make the g method of main.E invoked via interface main.I ambiguous.*` + 381 `with \(main.D\).g`, 382 }, 383 } { 384 var conflicts []string 385 reportError = func(posn token.Position, message string) { 386 conflicts = append(conflicts, message) 387 } 388 if test.ctxt != nil { 389 ctxt = test.ctxt 390 } 391 err := Main(ctxt, test.offset, test.from, test.to) 392 var prefix string 393 if test.offset == "" { 394 prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to) 395 } else { 396 prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to) 397 } 398 if err == ConflictError { 399 got := strings.Join(conflicts, "\n") 400 if false { 401 t.Logf("%s: %s", prefix, got) 402 } 403 pattern := "(?s:" + test.want + ")" // enable multi-line matching 404 if !regexp.MustCompile(pattern).MatchString(got) { 405 t.Errorf("%s: conflict does not match pattern:\n"+ 406 "Conflict:\t%s\n"+ 407 "Pattern: %s", 408 prefix, got, test.want) 409 } 410 } else if err != nil { 411 t.Errorf("%s: unexpected error: %s", prefix, err) 412 } else if test.want != "OK" { 413 t.Errorf("%s: unexpected success, want conflicts matching:\n%s", 414 prefix, test.want) 415 } 416 } 417} 418 419func TestInvalidIdentifiers(t *testing.T) { 420 ctxt := fakeContext(map[string][]string{ 421 "main": {` 422package main 423 424func f() { } 425`}}) 426 427 for _, test := range []struct { 428 from, to string // values of the -offset/-from and -to flags 429 want string // expected error message 430 }{ 431 { 432 from: "main.f", to: "_", 433 want: `-to "_": not a valid identifier`, 434 }, 435 { 436 from: "main.f", to: "123", 437 want: `-to "123": not a valid identifier`, 438 }, 439 { 440 from: "main.f", to: "for", 441 want: `-to "for": not a valid identifier`, 442 }, 443 { 444 from: "switch", to: "v", 445 want: `-from "switch": invalid expression`, 446 }, 447 } { 448 err := Main(ctxt, "", test.from, test.to) 449 prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to) 450 if err == nil { 451 t.Errorf("%s: expected error %q", prefix, test.want) 452 } else if err.Error() != test.want { 453 t.Errorf("%s: unexpected error\nwant: %s\n got: %s", prefix, test.want, err.Error()) 454 } 455 } 456} 457 458func TestRewrites(t *testing.T) { 459 defer func(savedWriteFile func(string, []byte) error) { 460 writeFile = savedWriteFile 461 }(writeFile) 462 463 var ctxt *build.Context 464 for _, test := range []struct { 465 ctxt *build.Context // nil => use previous 466 offset, from, to string // values of the -from/-offset and -to flags 467 want map[string]string // contents of updated files 468 }{ 469 // Elimination of renaming import. 470 { 471 ctxt: fakeContext(map[string][]string{ 472 "foo": {`package foo; type T int`}, 473 "main": {`package main 474 475import foo2 "foo" 476 477var _ foo2.T 478`}, 479 }), 480 from: "main::foo2", to: "foo", 481 want: map[string]string{ 482 "/go/src/main/0.go": `package main 483 484import "foo" 485 486var _ foo.T 487`, 488 }, 489 }, 490 // Introduction of renaming import. 491 { 492 ctxt: fakeContext(map[string][]string{ 493 "foo": {`package foo; type T int`}, 494 "main": {`package main 495 496import "foo" 497 498var _ foo.T 499`}, 500 }), 501 offset: "/go/src/main/0.go:#36", to: "foo2", // the "foo" in foo.T 502 want: map[string]string{ 503 "/go/src/main/0.go": `package main 504 505import foo2 "foo" 506 507var _ foo2.T 508`, 509 }, 510 }, 511 // Renaming of package-level member. 512 { 513 from: "foo.T", to: "U", 514 want: map[string]string{ 515 "/go/src/main/0.go": `package main 516 517import "foo" 518 519var _ foo.U 520`, 521 "/go/src/foo/0.go": `package foo 522 523type U int 524`, 525 }, 526 }, 527 // Rename package-level func plus doc 528 { 529 ctxt: main(`package main 530 531// Foo is a no-op. 532// Calling Foo does nothing. 533func Foo() { 534} 535`), 536 from: "main.Foo", to: "FooBar", 537 want: map[string]string{ 538 "/go/src/main/0.go": `package main 539 540// FooBar is a no-op. 541// Calling FooBar does nothing. 542func FooBar() { 543} 544`, 545 }, 546 }, 547 // Rename method plus doc 548 { 549 ctxt: main(`package main 550 551type Foo struct{} 552 553// Bar does nothing. 554func (Foo) Bar() { 555} 556`), 557 from: "main.Foo.Bar", to: "Baz", 558 want: map[string]string{ 559 "/go/src/main/0.go": `package main 560 561type Foo struct{} 562 563// Baz does nothing. 564func (Foo) Baz() { 565} 566`, 567 }, 568 }, 569 // Rename type spec plus doc 570 { 571 ctxt: main(`package main 572 573type ( 574 // Test but not Testing. 575 Test struct{} 576) 577`), 578 from: "main.Test", to: "Type", 579 want: map[string]string{ 580 "/go/src/main/0.go": `package main 581 582type ( 583 // Type but not Testing. 584 Type struct{} 585) 586`, 587 }, 588 }, 589 // Rename type in gen decl plus doc 590 { 591 ctxt: main(`package main 592 593// T is a test type. 594type T struct{} 595`), 596 from: "main.T", to: "Type", 597 want: map[string]string{ 598 "/go/src/main/0.go": `package main 599 600// Type is a test type. 601type Type struct{} 602`, 603 }, 604 }, 605 // Rename value spec with doc 606 { 607 ctxt: main(`package main 608 609const ( 610 // C is the speed of light. 611 C = 2.998e8 612) 613`), 614 from: "main.C", to: "Lightspeed", 615 want: map[string]string{ 616 "/go/src/main/0.go": `package main 617 618const ( 619 // Lightspeed is the speed of light. 620 Lightspeed = 2.998e8 621) 622`, 623 }, 624 }, 625 // Rename value inside gen decl with doc 626 { 627 ctxt: main(`package main 628 629var out *string 630`), 631 from: "main.out", to: "discard", 632 want: map[string]string{ 633 "/go/src/main/0.go": `package main 634 635var discard *string 636`, 637 }, 638 }, 639 // Rename field plus doc 640 { 641 ctxt: main(`package main 642 643type Struct struct { 644 // Field is a struct field. 645 Field string 646} 647`), 648 from: "main.Struct.Field", to: "Foo", 649 want: map[string]string{ 650 "/go/src/main/0.go": `package main 651 652type Struct struct { 653 // Foo is a struct field. 654 Foo string 655} 656`, 657 }, 658 }, 659 // Label renamings. 660 { 661 ctxt: main(`package main 662func f() { 663loop: 664 loop := 0 665 go func() { 666 loop: 667 goto loop 668 }() 669 loop++ 670 goto loop 671} 672`), 673 offset: "/go/src/main/0.go:#25", to: "loop2", // def of outer label "loop" 674 want: map[string]string{ 675 "/go/src/main/0.go": `package main 676 677func f() { 678loop2: 679 loop := 0 680 go func() { 681 loop: 682 goto loop 683 }() 684 loop++ 685 goto loop2 686} 687`, 688 }, 689 }, 690 { 691 offset: "/go/src/main/0.go:#70", to: "loop2", // ref to inner label "loop" 692 want: map[string]string{ 693 "/go/src/main/0.go": `package main 694 695func f() { 696loop: 697 loop := 0 698 go func() { 699 loop2: 700 goto loop2 701 }() 702 loop++ 703 goto loop 704} 705`, 706 }, 707 }, 708 // Renaming of type used as embedded field. 709 { 710 ctxt: main(`package main 711 712type T int 713type U struct { T } 714 715var _ = U{}.T 716`), 717 from: "main.T", to: "T2", 718 want: map[string]string{ 719 "/go/src/main/0.go": `package main 720 721type T2 int 722type U struct{ T2 } 723 724var _ = U{}.T2 725`, 726 }, 727 }, 728 // Renaming of embedded field. 729 { 730 ctxt: main(`package main 731 732type T int 733type U struct { T } 734 735var _ = U{}.T 736`), 737 offset: "/go/src/main/0.go:#58", to: "T2", // T in "U{}.T" 738 want: map[string]string{ 739 "/go/src/main/0.go": `package main 740 741type T2 int 742type U struct{ T2 } 743 744var _ = U{}.T2 745`, 746 }, 747 }, 748 // Renaming of pointer embedded field. 749 { 750 ctxt: main(`package main 751 752type T int 753type U struct { *T } 754 755var _ = U{}.T 756`), 757 offset: "/go/src/main/0.go:#59", to: "T2", // T in "U{}.T" 758 want: map[string]string{ 759 "/go/src/main/0.go": `package main 760 761type T2 int 762type U struct{ *T2 } 763 764var _ = U{}.T2 765`, 766 }, 767 }, 768 769 // Lexical scope tests. 770 { 771 ctxt: main(`package main 772 773var y int 774 775func f() { 776 print(y) 777 y := "" 778 print(y) 779} 780`), 781 from: "main.y", to: "x", 782 want: map[string]string{ 783 "/go/src/main/0.go": `package main 784 785var x int 786 787func f() { 788 print(x) 789 y := "" 790 print(y) 791} 792`, 793 }, 794 }, 795 { 796 from: "main.f::y", to: "x", 797 want: map[string]string{ 798 "/go/src/main/0.go": `package main 799 800var y int 801 802func f() { 803 print(y) 804 x := "" 805 print(x) 806} 807`, 808 }, 809 }, 810 // Renaming of typeswitch vars (a corner case). 811 { 812 ctxt: main(`package main 813 814func f(z interface{}) { 815 switch y := z.(type) { 816 case int: 817 print(y) 818 default: 819 print(y) 820 } 821} 822`), 823 offset: "/go/src/main/0.go:#46", to: "x", // def of y 824 want: map[string]string{ 825 "/go/src/main/0.go": `package main 826 827func f(z interface{}) { 828 switch x := z.(type) { 829 case int: 830 print(x) 831 default: 832 print(x) 833 } 834} 835`}, 836 }, 837 { 838 offset: "/go/src/main/0.go:#81", to: "x", // ref of y in case int 839 want: map[string]string{ 840 "/go/src/main/0.go": `package main 841 842func f(z interface{}) { 843 switch x := z.(type) { 844 case int: 845 print(x) 846 default: 847 print(x) 848 } 849} 850`}, 851 }, 852 { 853 offset: "/go/src/main/0.go:#102", to: "x", // ref of y in default case 854 want: map[string]string{ 855 "/go/src/main/0.go": `package main 856 857func f(z interface{}) { 858 switch x := z.(type) { 859 case int: 860 print(x) 861 default: 862 print(x) 863 } 864} 865`}, 866 }, 867 868 // Renaming of embedded field that is a qualified reference. 869 // (Regression test for bug 8924.) 870 { 871 ctxt: fakeContext(map[string][]string{ 872 "foo": {`package foo; type T int`}, 873 "main": {`package main 874 875import "foo" 876 877type _ struct{ *foo.T } 878`}, 879 }), 880 offset: "/go/src/main/0.go:#48", to: "U", // the "T" in *foo.T 881 want: map[string]string{ 882 "/go/src/foo/0.go": `package foo 883 884type U int 885`, 886 "/go/src/main/0.go": `package main 887 888import "foo" 889 890type _ struct{ *foo.U } 891`, 892 }, 893 }, 894 895 // Renaming of embedded field that is a qualified reference with the '-from' flag. 896 // (Regression test for bug 12038.) 897 { 898 ctxt: fakeContext(map[string][]string{ 899 "foo": {`package foo; type T int`}, 900 "main": {`package main 901 902import "foo" 903 904type V struct{ *foo.T } 905`}, 906 }), 907 from: "(main.V).T", to: "U", // the "T" in *foo.T 908 want: map[string]string{ 909 "/go/src/foo/0.go": `package foo 910 911type U int 912`, 913 "/go/src/main/0.go": `package main 914 915import "foo" 916 917type V struct{ *foo.U } 918`, 919 }, 920 }, 921 { 922 ctxt: fakeContext(map[string][]string{ 923 "foo": {`package foo; type T int`}, 924 "main": {`package main 925 926import "foo" 927 928type V struct{ foo.T } 929`}, 930 }), 931 from: "(main.V).T", to: "U", // the "T" in *foo.T 932 want: map[string]string{ 933 "/go/src/foo/0.go": `package foo 934 935type U int 936`, 937 "/go/src/main/0.go": `package main 938 939import "foo" 940 941type V struct{ foo.U } 942`, 943 }, 944 }, 945 946 // Interface method renaming. 947 { 948 ctxt: fakeContext(map[string][]string{ 949 "main": {` 950package main 951type I interface { 952 f() 953} 954type J interface { f(); g() } 955type A int 956func (A) f() 957type B int 958func (B) f() 959func (B) g() 960type C int 961func (C) f() 962func (C) g() 963var _, _ I = A(0), B(0) 964var _, _ J = B(0), C(0) 965`, 966 }, 967 }), 968 offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f 969 want: map[string]string{ 970 "/go/src/main/0.go": `package main 971 972type I interface { 973 F() 974} 975type J interface { 976 F() 977 g() 978} 979type A int 980 981func (A) F() 982 983type B int 984 985func (B) F() 986func (B) g() 987 988type C int 989 990func (C) F() 991func (C) g() 992 993var _, _ I = A(0), B(0) 994var _, _ J = B(0), C(0) 995`, 996 }, 997 }, 998 { 999 offset: "/go/src/main/0.go:#59", to: "F", // abstract method J.f 1000 want: map[string]string{ 1001 "/go/src/main/0.go": `package main 1002 1003type I interface { 1004 F() 1005} 1006type J interface { 1007 F() 1008 g() 1009} 1010type A int 1011 1012func (A) F() 1013 1014type B int 1015 1016func (B) F() 1017func (B) g() 1018 1019type C int 1020 1021func (C) F() 1022func (C) g() 1023 1024var _, _ I = A(0), B(0) 1025var _, _ J = B(0), C(0) 1026`, 1027 }, 1028 }, 1029 { 1030 offset: "/go/src/main/0.go:#64", to: "G", // abstract method J.g 1031 want: map[string]string{ 1032 "/go/src/main/0.go": `package main 1033 1034type I interface { 1035 f() 1036} 1037type J interface { 1038 f() 1039 G() 1040} 1041type A int 1042 1043func (A) f() 1044 1045type B int 1046 1047func (B) f() 1048func (B) G() 1049 1050type C int 1051 1052func (C) f() 1053func (C) G() 1054 1055var _, _ I = A(0), B(0) 1056var _, _ J = B(0), C(0) 1057`, 1058 }, 1059 }, 1060 // Indirect coupling of I.f to C.f from D->I assignment and anonymous field of D. 1061 { 1062 ctxt: fakeContext(map[string][]string{ 1063 "main": {` 1064package main 1065type I interface { 1066 f() 1067} 1068type C int 1069func (C) f() 1070type D struct{C} 1071var _ I = D{} 1072`, 1073 }, 1074 }), 1075 offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f 1076 want: map[string]string{ 1077 "/go/src/main/0.go": `package main 1078 1079type I interface { 1080 F() 1081} 1082type C int 1083 1084func (C) F() 1085 1086type D struct{ C } 1087 1088var _ I = D{} 1089`, 1090 }, 1091 }, 1092 // Interface embedded in struct. No conflict if C need not satisfy I. 1093 { 1094 ctxt: fakeContext(map[string][]string{ 1095 "main": {` 1096package main 1097type I interface { 1098 f() 1099} 1100type C struct{I} 1101func (C) g() int 1102var _ int = C{}.g() 1103`, 1104 }, 1105 }), 1106 offset: "/go/src/main/0.go:#34", to: "g", // abstract method I.f 1107 want: map[string]string{ 1108 "/go/src/main/0.go": `package main 1109 1110type I interface { 1111 g() 1112} 1113type C struct{ I } 1114 1115func (C) g() int 1116 1117var _ int = C{}.g() 1118`, 1119 }, 1120 }, 1121 // A type assertion causes method coupling iff signatures match. 1122 { 1123 ctxt: fakeContext(map[string][]string{ 1124 "main": {`package main 1125type I interface{ 1126 f() 1127} 1128type J interface{ 1129 f() 1130} 1131var _ = I(nil).(J) 1132`, 1133 }, 1134 }), 1135 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f 1136 want: map[string]string{ 1137 "/go/src/main/0.go": `package main 1138 1139type I interface { 1140 g() 1141} 1142type J interface { 1143 g() 1144} 1145 1146var _ = I(nil).(J) 1147`, 1148 }, 1149 }, 1150 // Impossible type assertion: no method coupling. 1151 { 1152 ctxt: fakeContext(map[string][]string{ 1153 "main": {`package main 1154type I interface{ 1155 f() 1156} 1157type J interface{ 1158 f()int 1159} 1160var _ = I(nil).(J) 1161`, 1162 }, 1163 }), 1164 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f 1165 want: map[string]string{ 1166 "/go/src/main/0.go": `package main 1167 1168type I interface { 1169 g() 1170} 1171type J interface { 1172 f() int 1173} 1174 1175var _ = I(nil).(J) 1176`, 1177 }, 1178 }, 1179 // Impossible type assertion: no method coupling C.f<->J.f. 1180 { 1181 ctxt: fakeContext(map[string][]string{ 1182 "main": {`package main 1183type I interface{ 1184 f() 1185} 1186type C int 1187func (C) f() 1188type J interface{ 1189 f()int 1190} 1191var _ = I(C(0)).(J) 1192`, 1193 }, 1194 }), 1195 offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f 1196 want: map[string]string{ 1197 "/go/src/main/0.go": `package main 1198 1199type I interface { 1200 g() 1201} 1202type C int 1203 1204func (C) g() 1205 1206type J interface { 1207 f() int 1208} 1209 1210var _ = I(C(0)).(J) 1211`, 1212 }, 1213 }, 1214 // Progress after "soft" type errors (Go issue 14596). 1215 { 1216 ctxt: fakeContext(map[string][]string{ 1217 "main": {`package main 1218 1219func main() { 1220 var unused, x int 1221 print(x) 1222} 1223`, 1224 }, 1225 }), 1226 offset: "/go/src/main/0.go:#54", to: "y", // var x 1227 want: map[string]string{ 1228 "/go/src/main/0.go": `package main 1229 1230func main() { 1231 var unused, y int 1232 print(y) 1233} 1234`, 1235 }, 1236 }, 1237 } { 1238 if test.ctxt != nil { 1239 ctxt = test.ctxt 1240 } 1241 1242 got := make(map[string]string) 1243 writeFile = func(filename string, content []byte) error { 1244 got[filepath.ToSlash(filename)] = string(content) 1245 return nil 1246 } 1247 1248 err := Main(ctxt, test.offset, test.from, test.to) 1249 var prefix string 1250 if test.offset == "" { 1251 prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to) 1252 } else { 1253 prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to) 1254 } 1255 if err != nil { 1256 t.Errorf("%s: unexpected error: %s", prefix, err) 1257 continue 1258 } 1259 1260 for file, wantContent := range test.want { 1261 gotContent, ok := got[file] 1262 delete(got, file) 1263 if !ok { 1264 t.Errorf("%s: file %s not rewritten", prefix, file) 1265 continue 1266 } 1267 if gotContent != wantContent { 1268 t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+ 1269 "want <<<%s>>>", prefix, file, gotContent, wantContent) 1270 } 1271 } 1272 // got should now be empty 1273 for file := range got { 1274 t.Errorf("%s: unexpected rewrite of file %s", prefix, file) 1275 } 1276 } 1277} 1278 1279func TestDiff(t *testing.T) { 1280 switch runtime.GOOS { 1281 case "windows", "android": 1282 t.Skipf("diff tool non-existent for %s on builders", runtime.GOOS) 1283 case "plan9": 1284 t.Skipf("plan9 diff tool doesn't support -u flag") 1285 } 1286 1287 defer func() { 1288 Diff = false 1289 stdout = os.Stdout 1290 }() 1291 Diff = true 1292 stdout = new(bytes.Buffer) 1293 1294 if err := Main(&build.Default, "", `"golang.org/x/tools/refactor/rename".justHereForTestingDiff`, "Foo"); err != nil { 1295 t.Fatal(err) 1296 } 1297 1298 // NB: there are tabs in the string literal! 1299 if !strings.Contains(stdout.(fmt.Stringer).String(), ` 1300-func justHereForTestingDiff() { 1301- justHereForTestingDiff() 1302+func Foo() { 1303+ Foo() 1304 } 1305`) { 1306 t.Errorf("unexpected diff:\n<<%s>>", stdout) 1307 } 1308} 1309 1310func justHereForTestingDiff() { 1311 justHereForTestingDiff() 1312} 1313 1314// --------------------------------------------------------------------- 1315 1316// Simplifying wrapper around buildutil.FakeContext for packages whose 1317// filenames are sequentially numbered (%d.go). pkgs maps a package 1318// import path to its list of file contents. 1319func fakeContext(pkgs map[string][]string) *build.Context { 1320 pkgs2 := make(map[string]map[string]string) 1321 for path, files := range pkgs { 1322 filemap := make(map[string]string) 1323 for i, contents := range files { 1324 filemap[fmt.Sprintf("%d.go", i)] = contents 1325 } 1326 pkgs2[path] = filemap 1327 } 1328 return buildutil.FakeContext(pkgs2) 1329} 1330 1331// helper for single-file main packages with no imports. 1332func main(content string) *build.Context { 1333 return fakeContext(map[string][]string{"main": {content}}) 1334} 1335