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