2014-09-23 08:23:04 -06:00
|
|
|
// Copyright 2014 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package rename
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"go/build"
|
|
|
|
"go/token"
|
2019-08-30 09:02:08 -06:00
|
|
|
"io/ioutil"
|
2015-12-29 14:26:28 -07:00
|
|
|
"os"
|
2019-08-29 14:22:26 -06:00
|
|
|
"os/exec"
|
2014-09-23 08:23:04 -06:00
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
2016-01-07 17:29:29 -07:00
|
|
|
"runtime"
|
2014-09-23 08:23:04 -06:00
|
|
|
"strings"
|
|
|
|
"testing"
|
2015-01-14 14:53:34 -07:00
|
|
|
|
|
|
|
"golang.org/x/tools/go/buildutil"
|
2019-08-29 14:22:26 -06:00
|
|
|
"golang.org/x/tools/internal/testenv"
|
2014-09-23 08:23:04 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// TODO(adonovan): test reported source positions, somehow.
|
|
|
|
|
|
|
|
func TestConflicts(t *testing.T) {
|
2015-12-29 14:26:28 -07:00
|
|
|
defer func(savedWriteFile func(string, []byte) error, savedReportError func(token.Position, string)) {
|
|
|
|
writeFile = savedWriteFile
|
2014-09-23 08:23:04 -06:00
|
|
|
reportError = savedReportError
|
2015-12-29 14:26:28 -07:00
|
|
|
}(writeFile, reportError)
|
|
|
|
writeFile = func(string, []byte) error { return nil }
|
2014-09-23 08:23:04 -06:00
|
|
|
|
|
|
|
var ctxt *build.Context
|
|
|
|
for _, test := range []struct {
|
|
|
|
ctxt *build.Context // nil => use previous
|
|
|
|
offset, from, to string // values of the -offset/-from and -to flags
|
|
|
|
want string // regexp to match conflict errors, or "OK"
|
|
|
|
}{
|
|
|
|
// init() checks
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"fmt": {`package fmt; type Stringer interface { String() }`},
|
|
|
|
"main": {`
|
|
|
|
package main
|
|
|
|
|
|
|
|
import foo "fmt"
|
|
|
|
|
|
|
|
var v foo.Stringer
|
|
|
|
|
|
|
|
func f() { v.String(); f() }
|
|
|
|
`,
|
|
|
|
`package main; var w int`},
|
|
|
|
}),
|
|
|
|
from: "main.v", to: "init",
|
|
|
|
want: `you cannot have a var at package level named "init"`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f", to: "init",
|
|
|
|
want: `renaming this func "f" to "init" would make it a package initializer.*` +
|
|
|
|
`but references to it exist`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "/go/src/main/0.go::foo", to: "init",
|
|
|
|
want: `"init" is not a valid imported package name`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Export checks
|
|
|
|
{
|
|
|
|
from: "fmt.Stringer", to: "stringer",
|
|
|
|
want: `renaming this type "Stringer" to "stringer" would make it unexported.*` +
|
|
|
|
`breaking references from packages such as "main"`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "(fmt.Stringer).String", to: "string",
|
|
|
|
want: `renaming this method "String" to "string" would make it unexported.*` +
|
|
|
|
`breaking references from packages such as "main"`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Lexical scope checks
|
|
|
|
{
|
|
|
|
// file/package conflict, same file
|
|
|
|
from: "main.v", to: "foo",
|
|
|
|
want: `renaming this var "v" to "foo" would conflict.*` +
|
|
|
|
`with this imported package name`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// file/package conflict, same file
|
|
|
|
from: "main::foo", to: "v",
|
|
|
|
want: `renaming this imported package name "foo" to "v" would conflict.*` +
|
|
|
|
`with this package member var`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// file/package conflict, different files
|
|
|
|
from: "main.w", to: "foo",
|
|
|
|
want: `renaming this var "w" to "foo" would conflict.*` +
|
|
|
|
`with this imported package name`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// file/package conflict, different files
|
|
|
|
from: "main::foo", to: "w",
|
|
|
|
want: `renaming this imported package name "foo" to "w" would conflict.*` +
|
|
|
|
`with this package member var`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ctxt: main(`
|
|
|
|
package main
|
|
|
|
|
|
|
|
var x, z int
|
|
|
|
|
|
|
|
func f(y int) {
|
|
|
|
print(x)
|
|
|
|
print(y)
|
|
|
|
}
|
|
|
|
|
|
|
|
func g(w int) {
|
|
|
|
print(x)
|
|
|
|
x := 1
|
|
|
|
print(x)
|
|
|
|
}`),
|
|
|
|
from: "main.x", to: "y",
|
|
|
|
want: `renaming this var "x" to "y".*` +
|
|
|
|
`would cause this reference to become shadowed.*` +
|
|
|
|
`by this intervening var definition`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.g::x", to: "w",
|
|
|
|
want: `renaming this var "x" to "w".*` +
|
|
|
|
`conflicts with var in same block`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f::y", to: "x",
|
|
|
|
want: `renaming this var "y" to "x".*` +
|
|
|
|
`would shadow this reference.*` +
|
|
|
|
`to the var declared here`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.g::w", to: "x",
|
|
|
|
want: `renaming this var "w" to "x".*` +
|
|
|
|
`conflicts with var in same block`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.z", to: "y", want: "OK",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Label checks
|
|
|
|
{
|
|
|
|
ctxt: main(`
|
|
|
|
package main
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
foo:
|
|
|
|
goto foo
|
|
|
|
bar:
|
|
|
|
goto bar
|
|
|
|
func(x int) {
|
|
|
|
wiz:
|
|
|
|
goto wiz
|
|
|
|
}(0)
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
from: "main.f::foo", to: "bar",
|
|
|
|
want: `renaming this label "foo" to "bar".*` +
|
|
|
|
`would conflict with this one`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f::foo", to: "wiz", want: "OK",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f::wiz", to: "x", want: "OK",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f::x", to: "wiz", want: "OK",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f::wiz", to: "foo", want: "OK",
|
|
|
|
},
|
|
|
|
|
|
|
|
// Struct fields
|
|
|
|
{
|
|
|
|
ctxt: main(`
|
|
|
|
package main
|
|
|
|
|
|
|
|
type U struct { u int }
|
|
|
|
type V struct { v int }
|
|
|
|
|
|
|
|
func (V) x() {}
|
|
|
|
|
|
|
|
type W (struct {
|
|
|
|
U
|
|
|
|
V
|
|
|
|
w int
|
|
|
|
})
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
var w W
|
|
|
|
print(w.u) // NB: there is no selection of w.v
|
|
|
|
var _ struct { yy, zz int }
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
// field/field conflict in named struct declaration
|
|
|
|
from: "(main.W).U", to: "w",
|
|
|
|
want: `renaming this field "U" to "w".*` +
|
|
|
|
`would conflict with this field`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// rename type used as embedded field
|
|
|
|
// => rename field
|
|
|
|
// => field/field conflict
|
|
|
|
// This is an entailed renaming;
|
|
|
|
// it would be nice if we checked source positions.
|
|
|
|
from: "main.U", to: "w",
|
|
|
|
want: `renaming this field "U" to "w".*` +
|
|
|
|
`would conflict with this field`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/field conflict in unnamed struct declaration
|
|
|
|
from: "main.f::zz", to: "yy",
|
|
|
|
want: `renaming this field "zz" to "yy".*` +
|
|
|
|
`would conflict with this field`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Now we test both directions of (u,v) (u,w) (v,w) (u,x) (v,x).
|
|
|
|
// Too bad we don't test position info...
|
|
|
|
{
|
|
|
|
// field/field ambiguity at same promotion level ('from' selection)
|
|
|
|
from: "(main.U).u", to: "v",
|
|
|
|
want: `renaming this field "u" to "v".*` +
|
|
|
|
`would make this reference ambiguous.*` +
|
|
|
|
`with this field`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/field ambiguity at same promotion level ('to' selection)
|
|
|
|
from: "(main.V).v", to: "u",
|
|
|
|
want: `renaming this field "v" to "u".*` +
|
|
|
|
`would make this reference ambiguous.*` +
|
|
|
|
`with this field`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/method conflict at different promotion level ('from' selection)
|
|
|
|
from: "(main.U).u", to: "w",
|
|
|
|
want: `renaming this field "u" to "w".*` +
|
|
|
|
`would change the referent of this selection.*` +
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
`of this field`,
|
2014-09-23 08:23:04 -06:00
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/field shadowing at different promotion levels ('to' selection)
|
|
|
|
from: "(main.W).w", to: "u",
|
|
|
|
want: `renaming this field "w" to "u".*` +
|
|
|
|
`would shadow this selection.*` +
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
`of the field declared here`,
|
2014-09-23 08:23:04 -06:00
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "(main.V).v", to: "w",
|
|
|
|
want: "OK", // since no selections are made ambiguous
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "(main.W).w", to: "v",
|
|
|
|
want: "OK", // since no selections are made ambiguous
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/method ambiguity at same promotion level ('from' selection)
|
|
|
|
from: "(main.U).u", to: "x",
|
|
|
|
want: `renaming this field "u" to "x".*` +
|
|
|
|
`would make this reference ambiguous.*` +
|
|
|
|
`with this method`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/field ambiguity at same promotion level ('to' selection)
|
|
|
|
from: "(main.V).x", to: "u",
|
|
|
|
want: `renaming this method "x" to "u".*` +
|
|
|
|
`would make this reference ambiguous.*` +
|
|
|
|
`with this field`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/method conflict at named struct declaration
|
|
|
|
from: "(main.V).v", to: "x",
|
|
|
|
want: `renaming this field "v" to "x".*` +
|
|
|
|
`would conflict with this method`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// field/method conflict at named struct declaration
|
|
|
|
from: "(main.V).x", to: "v",
|
|
|
|
want: `renaming this method "x" to "v".*` +
|
|
|
|
`would conflict with this field`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Methods
|
|
|
|
{
|
|
|
|
ctxt: main(`
|
|
|
|
package main
|
|
|
|
type C int
|
|
|
|
func (C) f()
|
|
|
|
func (C) g()
|
|
|
|
type D int
|
|
|
|
func (*D) f()
|
|
|
|
func (*D) g()
|
|
|
|
type I interface { f(); g() }
|
|
|
|
type J interface { I; h() }
|
|
|
|
var _ I = new(D)
|
|
|
|
var _ interface {f()} = C(0)
|
|
|
|
`),
|
|
|
|
from: "(main.I).f", to: "g",
|
|
|
|
want: `renaming this interface method "f" to "g".*` +
|
|
|
|
`would conflict with this method`,
|
|
|
|
},
|
|
|
|
{
|
2014-10-31 13:39:22 -06:00
|
|
|
from: `("main".I).f`, to: "h", // NB: exercises quoted import paths too
|
2014-09-23 08:23:04 -06:00
|
|
|
want: `renaming this interface method "f" to "h".*` +
|
|
|
|
`would conflict with this method.*` +
|
|
|
|
`in named interface type "J"`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// type J interface { h; h() } is not a conflict, amusingly.
|
|
|
|
from: "main.I", to: "h",
|
|
|
|
want: `OK`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "(main.J).h", to: "f",
|
|
|
|
want: `renaming this interface method "h" to "f".*` +
|
|
|
|
`would conflict with this method`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "(main.C).f", to: "e",
|
|
|
|
want: `renaming this method "f" to "e".*` +
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
`would make main.C no longer assignable to interface{f..}.*` +
|
|
|
|
`(rename interface{f..}.f if you intend to change both types)`,
|
2014-09-23 08:23:04 -06:00
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "(main.D).g", to: "e",
|
|
|
|
want: `renaming this method "g" to "e".*` +
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
`would make \*main.D no longer assignable to interface I.*` +
|
|
|
|
`(rename main.I.g if you intend to change both types)`,
|
2014-09-23 08:23:04 -06:00
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "(main.I).f", to: "e",
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: `OK`,
|
|
|
|
},
|
|
|
|
// Indirect C/I method coupling via another concrete type D.
|
|
|
|
{
|
|
|
|
ctxt: main(`
|
|
|
|
package main
|
|
|
|
type I interface { f() }
|
|
|
|
type C int
|
|
|
|
func (C) f()
|
|
|
|
type D struct{C}
|
|
|
|
var _ I = D{}
|
|
|
|
`),
|
|
|
|
from: "(main.C).f", to: "F",
|
|
|
|
want: `renaming this method "f" to "F".*` +
|
|
|
|
`would make main.D no longer assignable to interface I.*` +
|
|
|
|
`(rename main.I.f if you intend to change both types)`,
|
|
|
|
},
|
|
|
|
// Renaming causes promoted method to become shadowed; C no longer satisfies I.
|
|
|
|
{
|
|
|
|
ctxt: main(`
|
|
|
|
package main
|
|
|
|
type I interface { f() }
|
|
|
|
type C struct { I }
|
|
|
|
func (C) g() int
|
|
|
|
var _ I = C{}
|
|
|
|
`),
|
|
|
|
from: "main.I.f", to: "g",
|
|
|
|
want: `renaming this method "f" to "g".*` +
|
|
|
|
`would change the g method of main.C invoked via interface main.I.*` +
|
|
|
|
`from \(main.I\).g.*` +
|
|
|
|
`to \(main.C\).g`,
|
|
|
|
},
|
|
|
|
// Renaming causes promoted method to become ambiguous; C no longer satisfies I.
|
|
|
|
{
|
|
|
|
ctxt: main(`
|
|
|
|
package main
|
|
|
|
type I interface{f()}
|
|
|
|
type C int
|
|
|
|
func (C) f()
|
|
|
|
type D int
|
|
|
|
func (D) g()
|
|
|
|
type E struct{C;D}
|
|
|
|
var _ I = E{}
|
|
|
|
`),
|
|
|
|
from: "main.I.f", to: "g",
|
|
|
|
want: `renaming this method "f" to "g".*` +
|
|
|
|
`would make the g method of main.E invoked via interface main.I ambiguous.*` +
|
|
|
|
`with \(main.D\).g`,
|
2014-09-23 08:23:04 -06:00
|
|
|
},
|
|
|
|
} {
|
|
|
|
var conflicts []string
|
|
|
|
reportError = func(posn token.Position, message string) {
|
|
|
|
conflicts = append(conflicts, message)
|
|
|
|
}
|
|
|
|
if test.ctxt != nil {
|
|
|
|
ctxt = test.ctxt
|
|
|
|
}
|
|
|
|
err := Main(ctxt, test.offset, test.from, test.to)
|
|
|
|
var prefix string
|
|
|
|
if test.offset == "" {
|
|
|
|
prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to)
|
|
|
|
} else {
|
|
|
|
prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to)
|
|
|
|
}
|
|
|
|
if err == ConflictError {
|
|
|
|
got := strings.Join(conflicts, "\n")
|
|
|
|
if false {
|
|
|
|
t.Logf("%s: %s", prefix, got)
|
|
|
|
}
|
|
|
|
pattern := "(?s:" + test.want + ")" // enable multi-line matching
|
|
|
|
if !regexp.MustCompile(pattern).MatchString(got) {
|
|
|
|
t.Errorf("%s: conflict does not match pattern:\n"+
|
|
|
|
"Conflict:\t%s\n"+
|
|
|
|
"Pattern: %s",
|
|
|
|
prefix, got, test.want)
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
t.Errorf("%s: unexpected error: %s", prefix, err)
|
|
|
|
} else if test.want != "OK" {
|
|
|
|
t.Errorf("%s: unexpected success, want conflicts matching:\n%s",
|
|
|
|
prefix, test.want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-01 09:18:57 -06:00
|
|
|
func TestInvalidIdentifiers(t *testing.T) {
|
|
|
|
ctxt := fakeContext(map[string][]string{
|
|
|
|
"main": {`
|
|
|
|
package main
|
|
|
|
|
|
|
|
func f() { }
|
|
|
|
`}})
|
|
|
|
|
|
|
|
for _, test := range []struct {
|
|
|
|
from, to string // values of the -offset/-from and -to flags
|
|
|
|
want string // expected error message
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
from: "main.f", to: "_",
|
|
|
|
want: `-to "_": not a valid identifier`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f", to: "123",
|
|
|
|
want: `-to "123": not a valid identifier`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f", to: "for",
|
|
|
|
want: `-to "for": not a valid identifier`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "switch", to: "v",
|
|
|
|
want: `-from "switch": invalid expression`,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
err := Main(ctxt, "", test.from, test.to)
|
|
|
|
prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to)
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("%s: expected error %q", prefix, test.want)
|
|
|
|
} else if err.Error() != test.want {
|
|
|
|
t.Errorf("%s: unexpected error\nwant: %s\n got: %s", prefix, test.want, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-23 08:23:04 -06:00
|
|
|
func TestRewrites(t *testing.T) {
|
2015-12-29 14:26:28 -07:00
|
|
|
defer func(savedWriteFile func(string, []byte) error) {
|
|
|
|
writeFile = savedWriteFile
|
|
|
|
}(writeFile)
|
2014-09-23 08:23:04 -06:00
|
|
|
|
|
|
|
var ctxt *build.Context
|
|
|
|
for _, test := range []struct {
|
|
|
|
ctxt *build.Context // nil => use previous
|
|
|
|
offset, from, to string // values of the -from/-offset and -to flags
|
|
|
|
want map[string]string // contents of updated files
|
|
|
|
}{
|
|
|
|
// Elimination of renaming import.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"foo": {`package foo; type T int`},
|
|
|
|
"main": {`package main
|
|
|
|
|
|
|
|
import foo2 "foo"
|
|
|
|
|
|
|
|
var _ foo2.T
|
|
|
|
`},
|
|
|
|
}),
|
|
|
|
from: "main::foo2", to: "foo",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
var _ foo.T
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Introduction of renaming import.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"foo": {`package foo; type T int`},
|
|
|
|
"main": {`package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
var _ foo.T
|
|
|
|
`},
|
|
|
|
}),
|
|
|
|
offset: "/go/src/main/0.go:#36", to: "foo2", // the "foo" in foo.T
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
import foo2 "foo"
|
|
|
|
|
|
|
|
var _ foo2.T
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Renaming of package-level member.
|
|
|
|
{
|
|
|
|
from: "foo.T", to: "U",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
var _ foo.U
|
|
|
|
`,
|
|
|
|
"/go/src/foo/0.go": `package foo
|
|
|
|
|
|
|
|
type U int
|
2016-11-22 12:19:45 -07:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Rename package-level func plus doc
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
// Foo is a no-op.
|
|
|
|
// Calling Foo does nothing.
|
|
|
|
func Foo() {
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
from: "main.Foo", to: "FooBar",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
// FooBar is a no-op.
|
|
|
|
// Calling FooBar does nothing.
|
|
|
|
func FooBar() {
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Rename method plus doc
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
type Foo struct{}
|
|
|
|
|
|
|
|
// Bar does nothing.
|
|
|
|
func (Foo) Bar() {
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
from: "main.Foo.Bar", to: "Baz",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type Foo struct{}
|
|
|
|
|
|
|
|
// Baz does nothing.
|
|
|
|
func (Foo) Baz() {
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Rename type spec plus doc
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
type (
|
|
|
|
// Test but not Testing.
|
|
|
|
Test struct{}
|
|
|
|
)
|
|
|
|
`),
|
|
|
|
from: "main.Test", to: "Type",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type (
|
|
|
|
// Type but not Testing.
|
|
|
|
Type struct{}
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Rename type in gen decl plus doc
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
// T is a test type.
|
|
|
|
type T struct{}
|
|
|
|
`),
|
|
|
|
from: "main.T", to: "Type",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
// Type is a test type.
|
|
|
|
type Type struct{}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Rename value spec with doc
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
const (
|
|
|
|
// C is the speed of light.
|
|
|
|
C = 2.998e8
|
|
|
|
)
|
|
|
|
`),
|
|
|
|
from: "main.C", to: "Lightspeed",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Lightspeed is the speed of light.
|
|
|
|
Lightspeed = 2.998e8
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Rename value inside gen decl with doc
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
var out *string
|
|
|
|
`),
|
|
|
|
from: "main.out", to: "discard",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
var discard *string
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Rename field plus doc
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
type Struct struct {
|
|
|
|
// Field is a struct field.
|
|
|
|
Field string
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
from: "main.Struct.Field", to: "Foo",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type Struct struct {
|
|
|
|
// Foo is a struct field.
|
|
|
|
Foo string
|
|
|
|
}
|
2014-09-23 08:23:04 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Label renamings.
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
func f() {
|
|
|
|
loop:
|
|
|
|
loop := 0
|
|
|
|
go func() {
|
|
|
|
loop:
|
|
|
|
goto loop
|
|
|
|
}()
|
|
|
|
loop++
|
|
|
|
goto loop
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
offset: "/go/src/main/0.go:#25", to: "loop2", // def of outer label "loop"
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
loop2:
|
|
|
|
loop := 0
|
|
|
|
go func() {
|
|
|
|
loop:
|
|
|
|
goto loop
|
|
|
|
}()
|
|
|
|
loop++
|
|
|
|
goto loop2
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
offset: "/go/src/main/0.go:#70", to: "loop2", // ref to inner label "loop"
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
loop:
|
|
|
|
loop := 0
|
|
|
|
go func() {
|
|
|
|
loop2:
|
|
|
|
goto loop2
|
|
|
|
}()
|
|
|
|
loop++
|
|
|
|
goto loop
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Renaming of type used as embedded field.
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
type T int
|
|
|
|
type U struct { T }
|
|
|
|
|
|
|
|
var _ = U{}.T
|
|
|
|
`),
|
|
|
|
from: "main.T", to: "T2",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type T2 int
|
|
|
|
type U struct{ T2 }
|
|
|
|
|
|
|
|
var _ = U{}.T2
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Renaming of embedded field.
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
type T int
|
|
|
|
type U struct { T }
|
|
|
|
|
|
|
|
var _ = U{}.T
|
|
|
|
`),
|
|
|
|
offset: "/go/src/main/0.go:#58", to: "T2", // T in "U{}.T"
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type T2 int
|
|
|
|
type U struct{ T2 }
|
|
|
|
|
|
|
|
var _ = U{}.T2
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Renaming of pointer embedded field.
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
type T int
|
|
|
|
type U struct { *T }
|
|
|
|
|
|
|
|
var _ = U{}.T
|
|
|
|
`),
|
|
|
|
offset: "/go/src/main/0.go:#59", to: "T2", // T in "U{}.T"
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type T2 int
|
|
|
|
type U struct{ *T2 }
|
|
|
|
|
|
|
|
var _ = U{}.T2
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// Lexical scope tests.
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
var y int
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
print(y)
|
|
|
|
y := ""
|
|
|
|
print(y)
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
from: "main.y", to: "x",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
var x int
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
print(x)
|
|
|
|
y := ""
|
|
|
|
print(y)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
from: "main.f::y", to: "x",
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
var y int
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
print(y)
|
|
|
|
x := ""
|
|
|
|
print(x)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Renaming of typeswitch vars (a corner case).
|
|
|
|
{
|
|
|
|
ctxt: main(`package main
|
|
|
|
|
|
|
|
func f(z interface{}) {
|
|
|
|
switch y := z.(type) {
|
|
|
|
case int:
|
|
|
|
print(y)
|
|
|
|
default:
|
|
|
|
print(y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`),
|
|
|
|
offset: "/go/src/main/0.go:#46", to: "x", // def of y
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
func f(z interface{}) {
|
|
|
|
switch x := z.(type) {
|
|
|
|
case int:
|
|
|
|
print(x)
|
|
|
|
default:
|
|
|
|
print(x)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
offset: "/go/src/main/0.go:#81", to: "x", // ref of y in case int
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
func f(z interface{}) {
|
|
|
|
switch x := z.(type) {
|
|
|
|
case int:
|
|
|
|
print(x)
|
|
|
|
default:
|
|
|
|
print(x)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
offset: "/go/src/main/0.go:#102", to: "x", // ref of y in default case
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
func f(z interface{}) {
|
|
|
|
switch x := z.(type) {
|
|
|
|
case int:
|
|
|
|
print(x)
|
|
|
|
default:
|
|
|
|
print(x)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`},
|
|
|
|
},
|
2014-11-10 14:03:40 -07:00
|
|
|
|
|
|
|
// Renaming of embedded field that is a qualified reference.
|
|
|
|
// (Regression test for bug 8924.)
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"foo": {`package foo; type T int`},
|
|
|
|
"main": {`package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
type _ struct{ *foo.T }
|
|
|
|
`},
|
|
|
|
}),
|
|
|
|
offset: "/go/src/main/0.go:#48", to: "U", // the "T" in *foo.T
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/foo/0.go": `package foo
|
|
|
|
|
|
|
|
type U int
|
|
|
|
`,
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
type _ struct{ *foo.U }
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2015-08-09 11:07:02 -06:00
|
|
|
// Renaming of embedded field that is a qualified reference with the '-from' flag.
|
|
|
|
// (Regression test for bug 12038.)
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"foo": {`package foo; type T int`},
|
|
|
|
"main": {`package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
type V struct{ *foo.T }
|
|
|
|
`},
|
|
|
|
}),
|
|
|
|
from: "(main.V).T", to: "U", // the "T" in *foo.T
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/foo/0.go": `package foo
|
|
|
|
|
|
|
|
type U int
|
|
|
|
`,
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
type V struct{ *foo.U }
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"foo": {`package foo; type T int`},
|
|
|
|
"main": {`package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
type V struct{ foo.T }
|
|
|
|
`},
|
|
|
|
}),
|
|
|
|
from: "(main.V).T", to: "U", // the "T" in *foo.T
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/foo/0.go": `package foo
|
|
|
|
|
|
|
|
type U int
|
|
|
|
`,
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
import "foo"
|
|
|
|
|
|
|
|
type V struct{ foo.U }
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
// Interface method renaming.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"main": {`
|
|
|
|
package main
|
2017-11-06 12:55:38 -07:00
|
|
|
type I interface {
|
|
|
|
f()
|
|
|
|
}
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
type J interface { f(); g() }
|
|
|
|
type A int
|
|
|
|
func (A) f()
|
|
|
|
type B int
|
|
|
|
func (B) f()
|
|
|
|
func (B) g()
|
|
|
|
type C int
|
|
|
|
func (C) f()
|
|
|
|
func (C) g()
|
|
|
|
var _, _ I = A(0), B(0)
|
|
|
|
var _, _ J = B(0), C(0)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}),
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
F()
|
|
|
|
}
|
|
|
|
type J interface {
|
|
|
|
F()
|
|
|
|
g()
|
|
|
|
}
|
|
|
|
type A int
|
|
|
|
|
|
|
|
func (A) F()
|
|
|
|
|
|
|
|
type B int
|
|
|
|
|
|
|
|
func (B) F()
|
|
|
|
func (B) g()
|
|
|
|
|
|
|
|
type C int
|
|
|
|
|
|
|
|
func (C) F()
|
|
|
|
func (C) g()
|
|
|
|
|
|
|
|
var _, _ I = A(0), B(0)
|
|
|
|
var _, _ J = B(0), C(0)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#59", to: "F", // abstract method J.f
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
F()
|
|
|
|
}
|
|
|
|
type J interface {
|
|
|
|
F()
|
|
|
|
g()
|
|
|
|
}
|
|
|
|
type A int
|
|
|
|
|
|
|
|
func (A) F()
|
|
|
|
|
|
|
|
type B int
|
|
|
|
|
|
|
|
func (B) F()
|
|
|
|
func (B) g()
|
|
|
|
|
|
|
|
type C int
|
|
|
|
|
|
|
|
func (C) F()
|
|
|
|
func (C) g()
|
|
|
|
|
|
|
|
var _, _ I = A(0), B(0)
|
|
|
|
var _, _ J = B(0), C(0)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#64", to: "G", // abstract method J.g
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
type J interface {
|
|
|
|
f()
|
|
|
|
G()
|
|
|
|
}
|
|
|
|
type A int
|
|
|
|
|
|
|
|
func (A) f()
|
|
|
|
|
|
|
|
type B int
|
|
|
|
|
|
|
|
func (B) f()
|
|
|
|
func (B) G()
|
|
|
|
|
|
|
|
type C int
|
|
|
|
|
|
|
|
func (C) f()
|
|
|
|
func (C) G()
|
|
|
|
|
|
|
|
var _, _ I = A(0), B(0)
|
|
|
|
var _, _ J = B(0), C(0)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Indirect coupling of I.f to C.f from D->I assignment and anonymous field of D.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"main": {`
|
|
|
|
package main
|
2017-11-06 12:55:38 -07:00
|
|
|
type I interface {
|
|
|
|
f()
|
|
|
|
}
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
type C int
|
|
|
|
func (C) f()
|
|
|
|
type D struct{C}
|
|
|
|
var _ I = D{}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}),
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#34", to: "F", // abstract method I.f
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
F()
|
|
|
|
}
|
|
|
|
type C int
|
|
|
|
|
|
|
|
func (C) F()
|
|
|
|
|
|
|
|
type D struct{ C }
|
|
|
|
|
|
|
|
var _ I = D{}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Interface embedded in struct. No conflict if C need not satisfy I.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"main": {`
|
|
|
|
package main
|
2017-11-06 12:55:38 -07:00
|
|
|
type I interface {
|
|
|
|
f()
|
|
|
|
}
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
type C struct{I}
|
|
|
|
func (C) g() int
|
|
|
|
var _ int = C{}.g()
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}),
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#34", to: "g", // abstract method I.f
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
g()
|
|
|
|
}
|
|
|
|
type C struct{ I }
|
|
|
|
|
|
|
|
func (C) g() int
|
|
|
|
|
|
|
|
var _ int = C{}.g()
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// A type assertion causes method coupling iff signatures match.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"main": {`package main
|
2017-11-06 12:55:38 -07:00
|
|
|
type I interface{
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
type J interface{
|
|
|
|
f()
|
|
|
|
}
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
var _ = I(nil).(J)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}),
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
g()
|
|
|
|
}
|
|
|
|
type J interface {
|
|
|
|
g()
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ = I(nil).(J)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Impossible type assertion: no method coupling.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"main": {`package main
|
2017-11-06 12:55:38 -07:00
|
|
|
type I interface{
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
type J interface{
|
|
|
|
f()int
|
|
|
|
}
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
var _ = I(nil).(J)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}),
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
g()
|
|
|
|
}
|
|
|
|
type J interface {
|
|
|
|
f() int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ = I(nil).(J)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Impossible type assertion: no method coupling C.f<->J.f.
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"main": {`package main
|
2017-11-06 12:55:38 -07:00
|
|
|
type I interface{
|
|
|
|
f()
|
|
|
|
}
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
type C int
|
|
|
|
func (C) f()
|
2017-11-06 12:55:38 -07:00
|
|
|
type J interface{
|
|
|
|
f()int
|
|
|
|
}
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
var _ = I(C(0)).(J)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}),
|
2017-11-06 12:55:38 -07:00
|
|
|
offset: "/go/src/main/0.go:#32", to: "g", // abstract method I.f
|
cmd/gorename: support renaming of methods with consequences for other types, iff initiated at an abstract method.
Previously, gorename rejected all method renamings if it would
change the assignability relation.
Now, so long as the renaming was initiated at an abstract
method, the renaming proceeds, changing concrete methods (and
possibly other abstract methods) as needed. The user
intention is clear.
The intention of a renaming initiated at a concrete method is
less clear, so we still reject it if it would change the
assignability relation. The diagnostic advises the user to
rename the abstract method if that was the intention.
Additional safety checks are required: for each
satisfy.Constraint that couples a concrete type C and an
interface type I, we must treat it just like a set of implicit
selections C.f, one per abstract method f of I, and ensure the
selections' meanings are unchanged.
The satisfy package no longer canonicalizes types, since this
substitutes one interface for another (equivalent) one, which
is sound, but makes the type names random and the error
messages confusing.
Also, fixed a bug in 'satisfy' relating to map keys.
+ Lots more tests.
LGTM=sameer
R=sameer
CC=golang-codereviews
https://golang.org/cl/173430043
2014-12-04 07:37:50 -07:00
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
type I interface {
|
|
|
|
g()
|
|
|
|
}
|
|
|
|
type C int
|
|
|
|
|
|
|
|
func (C) g()
|
|
|
|
|
|
|
|
type J interface {
|
|
|
|
f() int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ = I(C(0)).(J)
|
2016-09-27 07:58:56 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Progress after "soft" type errors (Go issue 14596).
|
|
|
|
{
|
|
|
|
ctxt: fakeContext(map[string][]string{
|
|
|
|
"main": {`package main
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var unused, x int
|
|
|
|
print(x)
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
offset: "/go/src/main/0.go:#54", to: "y", // var x
|
|
|
|
want: map[string]string{
|
|
|
|
"/go/src/main/0.go": `package main
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var unused, y int
|
|
|
|
print(y)
|
|
|
|
}
|
2014-11-10 14:03:40 -07:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
},
|
2014-09-23 08:23:04 -06:00
|
|
|
} {
|
|
|
|
if test.ctxt != nil {
|
|
|
|
ctxt = test.ctxt
|
|
|
|
}
|
|
|
|
|
|
|
|
got := make(map[string]string)
|
2015-12-29 14:26:28 -07:00
|
|
|
writeFile = func(filename string, content []byte) error {
|
|
|
|
got[filepath.ToSlash(filename)] = string(content)
|
2014-09-23 08:23:04 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := Main(ctxt, test.offset, test.from, test.to)
|
|
|
|
var prefix string
|
|
|
|
if test.offset == "" {
|
|
|
|
prefix = fmt.Sprintf("-from %q -to %q", test.from, test.to)
|
|
|
|
} else {
|
|
|
|
prefix = fmt.Sprintf("-offset %q -to %q", test.offset, test.to)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("%s: unexpected error: %s", prefix, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for file, wantContent := range test.want {
|
|
|
|
gotContent, ok := got[file]
|
|
|
|
delete(got, file)
|
|
|
|
if !ok {
|
|
|
|
t.Errorf("%s: file %s not rewritten", prefix, file)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if gotContent != wantContent {
|
|
|
|
t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+
|
|
|
|
"want <<<%s>>>", prefix, file, gotContent, wantContent)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// got should now be empty
|
|
|
|
for file := range got {
|
|
|
|
t.Errorf("%s: unexpected rewrite of file %s", prefix, file)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-29 14:26:28 -07:00
|
|
|
func TestDiff(t *testing.T) {
|
2019-03-06 02:28:10 -07:00
|
|
|
switch runtime.GOOS {
|
2019-08-29 14:22:26 -06:00
|
|
|
case "windows":
|
|
|
|
if os.Getenv("GO_BUILDER_NAME") != "" {
|
|
|
|
if _, err := exec.LookPath(DiffCmd); err != nil {
|
|
|
|
t.Skipf("diff tool non-existent for %s on builders", runtime.GOOS)
|
|
|
|
}
|
|
|
|
}
|
2019-03-06 02:28:10 -07:00
|
|
|
case "plan9":
|
2016-11-28 11:15:10 -07:00
|
|
|
t.Skipf("plan9 diff tool doesn't support -u flag")
|
|
|
|
}
|
2019-08-29 14:22:26 -06:00
|
|
|
testenv.NeedsTool(t, DiffCmd)
|
2019-08-30 09:02:08 -06:00
|
|
|
testenv.NeedsTool(t, "go") // to locate the package to be renamed
|
2016-01-07 17:29:29 -07:00
|
|
|
|
2015-12-29 14:26:28 -07:00
|
|
|
defer func() {
|
|
|
|
Diff = false
|
|
|
|
stdout = os.Stdout
|
|
|
|
}()
|
|
|
|
Diff = true
|
|
|
|
stdout = new(bytes.Buffer)
|
|
|
|
|
2019-08-30 09:02:08 -06:00
|
|
|
// Set up a fake GOPATH in a temporary directory,
|
|
|
|
// and ensure we're in GOPATH mode.
|
|
|
|
tmpdir, err := ioutil.TempDir("", "TestDiff")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
buildCtx := build.Default
|
|
|
|
buildCtx.GOPATH = tmpdir
|
|
|
|
|
|
|
|
pkgDir := filepath.Join(tmpdir, "src", "example.com", "rename")
|
|
|
|
if err := os.MkdirAll(pkgDir, 0777); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
prevWD, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.Chdir(prevWD)
|
|
|
|
|
|
|
|
if err := os.Chdir(pkgDir); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
const goFile = `package rename
|
|
|
|
|
|
|
|
func justHereForTestingDiff() {
|
|
|
|
justHereForTestingDiff()
|
|
|
|
}
|
|
|
|
`
|
|
|
|
if err := ioutil.WriteFile(filepath.Join(pkgDir, "rename_test.go"), []byte(goFile), 0644); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := Main(&buildCtx, "", `"example.com/rename".justHereForTestingDiff`, "Foo"); err != nil {
|
2015-12-29 14:26:28 -07:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NB: there are tabs in the string literal!
|
|
|
|
if !strings.Contains(stdout.(fmt.Stringer).String(), `
|
|
|
|
-func justHereForTestingDiff() {
|
|
|
|
- justHereForTestingDiff()
|
|
|
|
+func Foo() {
|
|
|
|
+ Foo()
|
|
|
|
}
|
|
|
|
`) {
|
|
|
|
t.Errorf("unexpected diff:\n<<%s>>", stdout)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-23 08:23:04 -06:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2015-01-14 14:53:34 -07:00
|
|
|
// Simplifying wrapper around buildutil.FakeContext for packages whose
|
|
|
|
// filenames are sequentially numbered (%d.go). pkgs maps a package
|
|
|
|
// import path to its list of file contents.
|
2014-09-23 08:23:04 -06:00
|
|
|
func fakeContext(pkgs map[string][]string) *build.Context {
|
2015-01-14 14:53:34 -07:00
|
|
|
pkgs2 := make(map[string]map[string]string)
|
|
|
|
for path, files := range pkgs {
|
|
|
|
filemap := make(map[string]string)
|
|
|
|
for i, contents := range files {
|
|
|
|
filemap[fmt.Sprintf("%d.go", i)] = contents
|
2014-09-23 08:23:04 -06:00
|
|
|
}
|
2015-01-14 14:53:34 -07:00
|
|
|
pkgs2[path] = filemap
|
2014-09-23 08:23:04 -06:00
|
|
|
}
|
2015-01-14 14:53:34 -07:00
|
|
|
return buildutil.FakeContext(pkgs2)
|
2014-09-23 08:23:04 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// helper for single-file main packages with no imports.
|
|
|
|
func main(content string) *build.Context {
|
|
|
|
return fakeContext(map[string][]string{"main": {content}})
|
|
|
|
}
|