github.com/jackie-feng/tools@v0.0.0-20191231093943-4ebd680984ae/refactor/rename/rename_test.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package rename
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/build"
    11  	"go/token"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"regexp"
    17  	"runtime"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/jackie-feng/tools/go/buildutil"
    22  	"github.com/jackie-feng/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 goFile = `package rename
  1329  
  1330  func justHereForTestingDiff() {
  1331  	justHereForTestingDiff()
  1332  }
  1333  `
  1334  	if err := ioutil.WriteFile(filepath.Join(pkgDir, "rename_test.go"), []byte(goFile), 0644); err != nil {
  1335  		t.Fatal(err)
  1336  	}
  1337  
  1338  	if err := Main(&buildCtx, "", `"example.com/rename".justHereForTestingDiff`, "Foo"); err != nil {
  1339  		t.Fatal(err)
  1340  	}
  1341  
  1342  	// NB: there are tabs in the string literal!
  1343  	if !strings.Contains(stdout.(fmt.Stringer).String(), `
  1344  -func justHereForTestingDiff() {
  1345  -	justHereForTestingDiff()
  1346  +func Foo() {
  1347  +	Foo()
  1348   }
  1349  `) {
  1350  		t.Errorf("unexpected diff:\n<<%s>>", stdout)
  1351  	}
  1352  }
  1353  
  1354  // ---------------------------------------------------------------------
  1355  
  1356  // Simplifying wrapper around buildutil.FakeContext for packages whose
  1357  // filenames are sequentially numbered (%d.go).  pkgs maps a package
  1358  // import path to its list of file contents.
  1359  func fakeContext(pkgs map[string][]string) *build.Context {
  1360  	pkgs2 := make(map[string]map[string]string)
  1361  	for path, files := range pkgs {
  1362  		filemap := make(map[string]string)
  1363  		for i, contents := range files {
  1364  			filemap[fmt.Sprintf("%d.go", i)] = contents
  1365  		}
  1366  		pkgs2[path] = filemap
  1367  	}
  1368  	return buildutil.FakeContext(pkgs2)
  1369  }
  1370  
  1371  // helper for single-file main packages with no imports.
  1372  func main(content string) *build.Context {
  1373  	return fakeContext(map[string][]string{"main": {content}})
  1374  }