GoPLS Viewer

Home|gopls/refactor/rename/rename_test.go
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    "io/ioutil"
13    "os"
14    "os/exec"
15    "path/filepath"
16    "regexp"
17    "runtime"
18    "strings"
19    "testing"
20
21    "golang.org/x/tools/go/buildutil"
22    "golang.org/x/tools/internal/testenv"
23)
24
25// TODO(adonovan): test reported source positions, somehow.
26
27func TestConflicts(t *testing.T) {
28    defer func(savedWriteFile func(string, []byteerrorsavedReportError func(token.Positionstring)) {
29        writeFile = savedWriteFile
30        reportError = savedReportError
31    }(writeFilereportError)
32    writeFile = func(string, []byteerror { return nil }
33
34    var ctxt *build.Context
35    for _test := range []struct {
36        ctxt             *build.Context // nil => use previous
37        offsetfromto 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            ctxtfakeContext(map[string][]string{
43                "fmt": {`package fmt; type Stringer interface { String() }`},
44                "main": {`
45package main
46
47import foo "fmt"
48
49var v foo.Stringer
50
51func 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            ctxtmain(`
107package main
108
109var x, z int
110
111func f(y int) {
112    print(x)
113    print(y)
114}
115
116func 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            ctxtmain(`
149package main
150
151func f() {
152foo:
153    goto foo
154bar:
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            ctxtmain(`
182package main
183
184type U struct { u int }
185type V struct { v int }
186
187func (V) x() {}
188
189type W (struct {
190    U
191    V
192    w int
193})
194
195func 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            ctxtmain(`
291package main
292type C int
293func (C) f()
294func (C) g()
295type D int
296func (*D) f()
297func (*D) g()
298type I interface { f(); g() }
299type J interface { I; h() }
300var _ I = new(D)
301var _ 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            ctxtmain(`
342package main
343type I interface { f() }
344type C int
345func (C) f()
346type D struct{C}
347var _ 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            ctxtmain(`
357package main
358type I interface { f() }
359type C struct { I }
360func (C) g() int
361var _ 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            ctxtmain(`
372package main
373type I interface{f()}
374type C int
375func (C) f()
376type D int
377func (D) g()
378type E struct{C;D}
379var _ 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.Positionmessage string) {
389            conflicts = append(conflictsmessage)
390        }
391        if test.ctxt != nil {
392            ctxt = test.ctxt
393        }
394        err := Main(ctxttest.offsettest.fromtest.to)
395        var prefix string
396        if test.offset == "" {
397            prefix = fmt.Sprintf("-from %q -to %q"test.fromtest.to)
398        } else {
399            prefix = fmt.Sprintf("-offset %q -to %q"test.offsettest.to)
400        }
401        if err == ConflictError {
402            got := strings.Join(conflicts"\n")
403            if false {
404                t.Logf("%s: %s"prefixgot)
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                    prefixgottest.want)
412            }
413        } else if err != nil {
414            t.Errorf("%s: unexpected error: %s"prefixerr)
415        } else if test.want != "OK" {
416            t.Errorf("%s: unexpected success, want conflicts matching:\n%s",
417                prefixtest.want)
418        }
419    }
420}
421
422func TestInvalidIdentifiers(t *testing.T) {
423    ctxt := fakeContext(map[string][]string{
424        "main": {`
425package main
426
427func f() { }
428`}})
429
430    for _test := range []struct {
431        fromto 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.fromtest.to)
452        prefix := fmt.Sprintf("-from %q -to %q"test.fromtest.to)
453        if err == nil {
454            t.Errorf("%s: expected error %q"prefixtest.want)
455        } else if err.Error() != test.want {
456            t.Errorf("%s: unexpected error\nwant: %s\n got: %s"prefixtest.wanterr.Error())
457        }
458    }
459}
460
461func TestRewrites(t *testing.T) {
462    defer func(savedWriteFile func(string, []byteerror) {
463        writeFile = savedWriteFile
464    }(writeFile)
465
466    var ctxt *build.Context
467    for _test := range []struct {
468        ctxt             *build.Context    // nil => use previous
469        offsetfromto 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            ctxtfakeContext(map[string][]string{
475                "foo": {`package foo; type T int`},
476                "main": {`package main
477
478import foo2 "foo"
479
480var _ foo2.T
481`},
482            }),
483            from"main::foo2"to"foo",
484            want: map[string]string{
485                "/go/src/main/0.go"`package main
486
487import "foo"
488
489var _ foo.T
490`,
491            },
492        },
493        // Introduction of renaming import.
494        {
495            ctxtfakeContext(map[string][]string{
496                "foo": {`package foo; type T int`},
497                "main": {`package main
498
499import "foo"
500
501var _ 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
508import foo2 "foo"
509
510var _ 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
520import "foo"
521
522var _ foo.U
523`,
524                "/go/src/foo/0.go"`package foo
525
526type U int
527`,
528            },
529        },
530        // Rename package-level func plus doc
531        {
532            ctxtmain(`package main
533
534// Foo is a no-op.
535// Calling Foo does nothing.
536func 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.
545func FooBar() {
546}
547`,
548            },
549        },
550        // Rename method plus doc
551        {
552            ctxtmain(`package main
553
554type Foo struct{}
555
556// Bar does nothing.
557func (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
564type Foo struct{}
565
566// Baz does nothing.
567func (Foo) Baz() {
568}
569`,
570            },
571        },
572        // Rename type spec plus doc
573        {
574            ctxtmain(`package main
575
576type (
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
585type (
586    // Type but not Testing.
587    Type struct{}
588)
589`,
590            },
591        },
592        // Rename type in gen decl plus doc
593        {
594            ctxtmain(`package main
595
596// T is a test type.
597type 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.
604type Type struct{}
605`,
606            },
607        },
608        // Rename value spec with doc
609        {
610            ctxtmain(`package main
611
612const (
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
621const (
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            ctxtmain(`package main
631
632var out *string
633`),
634            from"main.out"to"discard",
635            want: map[string]string{
636                "/go/src/main/0.go"`package main
637
638var discard *string
639`,
640            },
641        },
642        // Rename field plus doc
643        {
644            ctxtmain(`package main
645
646type 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
655type Struct struct {
656    // Foo is a struct field.
657    Foo string
658}
659`,
660            },
661        },
662        // Label renamings.
663        {
664            ctxtmain(`package main
665func f() {
666loop:
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
680func f() {
681loop2:
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
698func f() {
699loop:
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            ctxtmain(`package main
714
715type T int
716type U struct { T }
717
718var _ = U{}.T
719`),
720            from"main.T"to"T2",
721            want: map[string]string{
722                "/go/src/main/0.go"`package main
723
724type T2 int
725type U struct{ T2 }
726
727var _ = U{}.T2
728`,
729            },
730        },
731        // Renaming of embedded field.
732        {
733            ctxtmain(`package main
734
735type T int
736type U struct { T }
737
738var _ = 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
744type T2 int
745type U struct{ T2 }
746
747var _ = U{}.T2
748`,
749            },
750        },
751        // Renaming of pointer embedded field.
752        {
753            ctxtmain(`package main
754
755type T int
756type U struct { *T }
757
758var _ = 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
764type T2 int
765type U struct{ *T2 }
766
767var _ = U{}.T2
768`,
769            },
770        },
771
772        // Lexical scope tests.
773        {
774            ctxtmain(`package main
775
776var y int
777
778func 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
788var x int
789
790func 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
803var y int
804
805func f() {
806    print(y)
807    x := ""
808    print(x)
809}
810`,
811            },
812        },
813        // Renaming of typeswitch vars (a corner case).
814        {
815            ctxtmain(`package main
816
817func 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
830func 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
845func 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
860func 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            ctxtfakeContext(map[string][]string{
875                "foo": {`package foo; type T int`},
876                "main": {`package main
877
878import "foo"
879
880type _ 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
887type U int
888`,
889                "/go/src/main/0.go"`package main
890
891import "foo"
892
893type _ 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            ctxtfakeContext(map[string][]string{
902                "foo": {`package foo; type T int`},
903                "main": {`package main
904
905import "foo"
906
907type 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
914type U int
915`,
916                "/go/src/main/0.go"`package main
917
918import "foo"
919
920type V struct{ *foo.U }
921`,
922            },
923        },
924        {
925            ctxtfakeContext(map[string][]string{
926                "foo": {`package foo; type T int`},
927                "main": {`package main
928
929import "foo"
930
931type 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
938type U int
939`,
940                "/go/src/main/0.go"`package main
941
942import "foo"
943
944type V struct{ foo.U }
945`,
946            },
947        },
948
949        // Interface method renaming.
950        {
951            ctxtfakeContext(map[string][]string{
952                "main": {`
953package main
954type I interface {
955    f()
956}
957type J interface { f(); g() }
958type A int
959func (A) f()
960type B int
961func (B) f()
962func (B) g()
963type C int
964func (C) f()
965func (C) g()
966var _, _ I = A(0), B(0)
967var _, _ 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
975type I interface {
976    F()
977}
978type J interface {
979    F()
980    g()
981}
982type A int
983
984func (A) F()
985
986type B int
987
988func (B) F()
989func (B) g()
990
991type C int
992
993func (C) F()
994func (C) g()
995
996var _, _ I = A(0), B(0)
997var _, _ 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
1006type I interface {
1007    F()
1008}
1009type J interface {
1010    F()
1011    g()
1012}
1013type A int
1014
1015func (A) F()
1016
1017type B int
1018
1019func (B) F()
1020func (B) g()
1021
1022type C int
1023
1024func (C) F()
1025func (C) g()
1026
1027var _, _ I = A(0), B(0)
1028var _, _ 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
1037type I interface {
1038    f()
1039}
1040type J interface {
1041    f()
1042    G()
1043}
1044type A int
1045
1046func (A) f()
1047
1048type B int
1049
1050func (B) f()
1051func (B) G()
1052
1053type C int
1054
1055func (C) f()
1056func (C) G()
1057
1058var _, _ I = A(0), B(0)
1059var _, _ 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            ctxtfakeContext(map[string][]string{
1066                "main": {`
1067package main
1068type I interface {
1069    f()
1070}
1071type C int
1072func (C) f()
1073type D struct{C}
1074var _ 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
1082type I interface {
1083    F()
1084}
1085type C int
1086
1087func (C) F()
1088
1089type D struct{ C }
1090
1091var _ I = D{}
1092`,
1093            },
1094        },
1095        // Interface embedded in struct.  No conflict if C need not satisfy I.
1096        {
1097            ctxtfakeContext(map[string][]string{
1098                "main": {`
1099package main
1100type I interface {
1101    f()
1102}
1103type C struct{I}
1104func (C) g() int
1105var _ 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
1113type I interface {
1114    g()
1115}
1116type C struct{ I }
1117
1118func (C) g() int
1119
1120var _ int = C{}.g()
1121`,
1122            },
1123        },
1124        // A type assertion causes method coupling iff signatures match.
1125        {
1126            ctxtfakeContext(map[string][]string{
1127                "main": {`package main
1128type I interface{
1129    f()
1130}
1131type J interface{
1132    f()
1133}
1134var _ = 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
1142type I interface {
1143    g()
1144}
1145type J interface {
1146    g()
1147}
1148
1149var _ = I(nil).(J)
1150`,
1151            },
1152        },
1153        // Impossible type assertion: no method coupling.
1154        {
1155            ctxtfakeContext(map[string][]string{
1156                "main": {`package main
1157type I interface{
1158    f()
1159}
1160type J interface{
1161    f()int
1162}
1163var _ = 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
1171type I interface {
1172    g()
1173}
1174type J interface {
1175    f() int
1176}
1177
1178var _ = I(nil).(J)
1179`,
1180            },
1181        },
1182        // Impossible type assertion: no method coupling C.f<->J.f.
1183        {
1184            ctxtfakeContext(map[string][]string{
1185                "main": {`package main
1186type I interface{
1187    f()
1188}
1189type C int
1190func (C) f()
1191type J interface{
1192    f()int
1193}
1194var _ = 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
1202type I interface {
1203    g()
1204}
1205type C int
1206
1207func (C) g()
1208
1209type J interface {
1210    f() int
1211}
1212
1213var _ = I(C(0)).(J)
1214`,
1215            },
1216        },
1217        // Progress after "soft" type errors (Go issue 14596).
1218        {
1219            ctxtfakeContext(map[string][]string{
1220                "main": {`package main
1221
1222func 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
1233func 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 stringcontent []byteerror {
1247            got[filepath.ToSlash(filename)] = string(content)
1248            return nil
1249        }
1250
1251        err := Main(ctxttest.offsettest.fromtest.to)
1252        var prefix string
1253        if test.offset == "" {
1254            prefix = fmt.Sprintf("-from %q -to %q"test.fromtest.to)
1255        } else {
1256            prefix = fmt.Sprintf("-offset %q -to %q"test.offsettest.to)
1257        }
1258        if err != nil {
1259            t.Errorf("%s: unexpected error: %s"prefixerr)
1260            continue
1261        }
1262
1263        for filewantContent := range test.want {
1264            gotContentok := got[file]
1265            delete(gotfile)
1266            if !ok {
1267                t.Errorf("%s: file %s not rewritten"prefixfile)
1268                continue
1269            }
1270            if gotContent != wantContent {
1271                t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+
1272                    "want <<<%s>>>"prefixfilegotContentwantContent)
1273            }
1274        }
1275        // got should now be empty
1276        for file := range got {
1277            t.Errorf("%s: unexpected rewrite of file %s"prefixfile)
1278        }
1279    }
1280}
1281
1282func 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(tDiffCmd)
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    tmpdirerr := 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(pkgDir0777); err != nil {
1315        t.Fatal(err)
1316    }
1317
1318    prevWDerr := 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
1330go 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
1338func 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.
1367func fakeContext(pkgs map[string][]string) *build.Context {
1368    pkgs2 := make(map[string]map[string]string)
1369    for pathfiles := range pkgs {
1370        filemap := make(map[string]string)
1371        for icontents := 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.
1380func main(content string) *build.Context {
1381    return fakeContext(map[string][]string{"main": {content}})
1382}
1383
MembersX
TestRewrites
TestRewrites.ctxt
TestRewrites.RangeStmt_11705.test
TestDiff.buildCtx
TestDiff.goFile
TestConflicts.RangeStmt_765.BlockStmt.BlockStmt.got
TestRewrites.RangeStmt_11705.BlockStmt.got
TestRewrites.RangeStmt_11705.BlockStmt.prefix
TestDiff.BlockStmt.BlockStmt.err
TestConflicts.t
TestRewrites.RangeStmt_11705.BlockStmt.RangeStmt_23948.wantContent
TestDiff.pkgDir
fakeContext.pkgs2
fakeContext.RangeStmt_26610.BlockStmt.filemap
TestRewrites.t
TestDiff
fakeContext.RangeStmt_26610.BlockStmt.RangeStmt_26681.contents
main
TestConflicts.RangeStmt_765.BlockStmt.prefix
TestConflicts.ctxt
TestConflicts.RangeStmt_765.BlockStmt.conflicts
TestInvalidIdentifiers
TestInvalidIdentifiers.RangeStmt_10743.BlockStmt.prefix
TestDiff.t
TestDiff.modFile
fakeContext.pkgs
testenv
fakeContext.RangeStmt_26610.files
TestConflicts.RangeStmt_765.BlockStmt.err
TestRewrites.RangeStmt_11705.BlockStmt.err
TestRewrites.RangeStmt_11705.BlockStmt.RangeStmt_23948.file
TestDiff.BlockStmt.BlockStmt._
TestDiff.prevWD
fakeContext.RangeStmt_26610.BlockStmt.RangeStmt_26681.i
TestConflicts
TestDiff.tmpdir
fakeContext
fakeContext.RangeStmt_26610.path
main.content
TestInvalidIdentifiers.ctxt
TestInvalidIdentifiers.t
TestInvalidIdentifiers.RangeStmt_10743.test
TestInvalidIdentifiers.RangeStmt_10743.BlockStmt.err
TestRewrites.RangeStmt_11705.BlockStmt.RangeStmt_24345.file
TestDiff.err
TestConflicts.RangeStmt_765.test
Members
X