Add generated file

This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
xing-yang
2018-07-12 10:55:15 -07:00
parent 36b1de0341
commit e213d1890d
17729 changed files with 5090889 additions and 0 deletions

858
vendor/golang.org/x/tools/refactor/rename/check.go generated vendored Normal file
View File

@@ -0,0 +1,858 @@
// 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
// This file defines the safety checks for each kind of renaming.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/refactor/satisfy"
)
// errorf reports an error (e.g. conflict) and prevents file modification.
func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) {
r.hadConflicts = true
reportError(r.iprog.Fset.Position(pos), fmt.Sprintf(format, args...))
}
// check performs safety checks of the renaming of the 'from' object to r.to.
func (r *renamer) check(from types.Object) {
if r.objsToUpdate[from] {
return
}
r.objsToUpdate[from] = true
// NB: order of conditions is important.
if from_, ok := from.(*types.PkgName); ok {
r.checkInFileBlock(from_)
} else if from_, ok := from.(*types.Label); ok {
r.checkLabel(from_)
} else if isPackageLevel(from) {
r.checkInPackageBlock(from)
} else if v, ok := from.(*types.Var); ok && v.IsField() {
r.checkStructField(v)
} else if f, ok := from.(*types.Func); ok && recv(f) != nil {
r.checkMethod(f)
} else if isLocal(from) {
r.checkInLocalScope(from)
} else {
r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
objectKind(from), from)
}
}
// checkInFileBlock performs safety checks for renames of objects in the file block,
// i.e. imported package names.
func (r *renamer) checkInFileBlock(from *types.PkgName) {
// Check import name is not "init".
if r.to == "init" {
r.errorf(from.Pos(), "%q is not a valid imported package name", r.to)
}
// Check for conflicts between file and package block.
if prev := from.Pkg().Scope().Lookup(r.to); prev != nil {
r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
objectKind(from), from.Name(), r.to)
r.errorf(prev.Pos(), "\twith this package member %s",
objectKind(prev))
return // since checkInPackageBlock would report redundant errors
}
// Check for conflicts in lexical scope.
r.checkInLexicalScope(from, r.packages[from.Pkg()])
// Finally, modify ImportSpec syntax to add or remove the Name as needed.
info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
if from.Imported().Name() == r.to {
// ImportSpec.Name not needed
path[1].(*ast.ImportSpec).Name = nil
} else {
// ImportSpec.Name needed
if spec := path[1].(*ast.ImportSpec); spec.Name == nil {
spec.Name = &ast.Ident{NamePos: spec.Path.Pos(), Name: r.to}
info.Defs[spec.Name] = from
}
}
}
// checkInPackageBlock performs safety checks for renames of
// func/var/const/type objects in the package block.
func (r *renamer) checkInPackageBlock(from types.Object) {
// Check that there are no references to the name from another
// package if the renaming would make it unexported.
if ast.IsExported(from.Name()) && !ast.IsExported(r.to) {
for pkg, info := range r.packages {
if pkg == from.Pkg() {
continue
}
if id := someUse(info, from); id != nil &&
!r.checkExport(id, pkg, from) {
break
}
}
}
info := r.packages[from.Pkg()]
// Check that in the package block, "init" is a function, and never referenced.
if r.to == "init" {
kind := objectKind(from)
if kind == "func" {
// Reject if intra-package references to it exist.
for id, obj := range info.Uses {
if obj == from {
r.errorf(from.Pos(),
"renaming this func %q to %q would make it a package initializer",
from.Name(), r.to)
r.errorf(id.Pos(), "\tbut references to it exist")
break
}
}
} else {
r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
kind, r.to)
}
}
// Check for conflicts between package block and all file blocks.
for _, f := range info.Files {
fileScope := info.Info.Scopes[f]
b, prev := fileScope.LookupParent(r.to, token.NoPos)
if b == fileScope {
r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
objectKind(from), from.Name(), r.to)
r.errorf(prev.Pos(), "\twith this %s",
objectKind(prev))
return // since checkInPackageBlock would report redundant errors
}
}
// Check for conflicts in lexical scope.
if from.Exported() {
for _, info := range r.packages {
r.checkInLexicalScope(from, info)
}
} else {
r.checkInLexicalScope(from, info)
}
}
func (r *renamer) checkInLocalScope(from types.Object) {
info := r.packages[from.Pkg()]
// Is this object an implicit local var for a type switch?
// Each case has its own var, whose position is the decl of y,
// but Ident in that decl does not appear in the Uses map.
//
// switch y := x.(type) { // Defs[Ident(y)] is undefined
// case int: print(y) // Implicits[CaseClause(int)] = Var(y_int)
// case string: print(y) // Implicits[CaseClause(string)] = Var(y_string)
// }
//
var isCaseVar bool
for syntax, obj := range info.Implicits {
if _, ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() {
isCaseVar = true
r.check(obj)
}
}
r.checkInLexicalScope(from, info)
// Finally, if this was a type switch, change the variable y.
if isCaseVar {
_, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...]
}
}
// checkInLexicalScope performs safety checks that a renaming does not
// change the lexical reference structure of the specified package.
//
// For objects in lexical scope, there are three kinds of conflicts:
// same-, sub-, and super-block conflicts. We will illustrate all three
// using this example:
//
// var x int
// var z int
//
// func f(y int) {
// print(x)
// print(y)
// }
//
// Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object
// with the new name already exists, defined in the same lexical block
// as the old object.
//
// Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists
// a reference to x from within (what would become) a hole in its scope.
// The definition of y in an (inner) sub-block would cast a shadow in
// the scope of the renamed variable.
//
// Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the
// converse situation: there is an existing definition of the new name
// (x) in an (enclosing) super-block, and the renaming would create a
// hole in its scope, within which there exist references to it. The
// new name casts a shadow in scope of the existing definition of x in
// the super-block.
//
// Removing the old name (and all references to it) is always safe, and
// requires no checks.
//
func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) {
b := from.Parent() // the block defining the 'from' object
if b != nil {
toBlock, to := b.LookupParent(r.to, from.Parent().End())
if toBlock == b {
// same-block conflict
r.errorf(from.Pos(), "renaming this %s %q to %q",
objectKind(from), from.Name(), r.to)
r.errorf(to.Pos(), "\tconflicts with %s in same block",
objectKind(to))
return
} else if toBlock != nil {
// Check for super-block conflict.
// The name r.to is defined in a superblock.
// Is that name referenced from within this block?
forEachLexicalRef(info, to, func(id *ast.Ident, block *types.Scope) bool {
_, obj := lexicalLookup(block, from.Name(), id.Pos())
if obj == from {
// super-block conflict
r.errorf(from.Pos(), "renaming this %s %q to %q",
objectKind(from), from.Name(), r.to)
r.errorf(id.Pos(), "\twould shadow this reference")
r.errorf(to.Pos(), "\tto the %s declared here",
objectKind(to))
return false // stop
}
return true
})
}
}
// Check for sub-block conflict.
// Is there an intervening definition of r.to between
// the block defining 'from' and some reference to it?
forEachLexicalRef(info, from, func(id *ast.Ident, block *types.Scope) bool {
// Find the block that defines the found reference.
// It may be an ancestor.
fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos())
// See what r.to would resolve to in the same scope.
toBlock, to := lexicalLookup(block, r.to, id.Pos())
if to != nil {
// sub-block conflict
if deeper(toBlock, fromBlock) {
r.errorf(from.Pos(), "renaming this %s %q to %q",
objectKind(from), from.Name(), r.to)
r.errorf(id.Pos(), "\twould cause this reference to become shadowed")
r.errorf(to.Pos(), "\tby this intervening %s definition",
objectKind(to))
return false // stop
}
}
return true
})
// Renaming a type that is used as an embedded field
// requires renaming the field too. e.g.
// type T int // if we rename this to U..
// var s struct {T}
// print(s.T) // ...this must change too
if _, ok := from.(*types.TypeName); ok {
for id, obj := range info.Uses {
if obj == from {
if field := info.Defs[id]; field != nil {
r.check(field)
}
}
}
}
}
// lexicalLookup is like (*types.Scope).LookupParent but respects the
// environment visible at pos. It assumes the relative position
// information is correct with each file.
func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) {
for b := block; b != nil; b = b.Parent() {
obj := b.Lookup(name)
// The scope of a package-level object is the entire package,
// so ignore pos in that case.
// No analogous clause is needed for file-level objects
// since no reference can appear before an import decl.
if obj != nil && (b == obj.Pkg().Scope() || obj.Pos() < pos) {
return b, obj
}
}
return nil, nil
}
// deeper reports whether block x is lexically deeper than y.
func deeper(x, y *types.Scope) bool {
if x == y || x == nil {
return false
} else if y == nil {
return true
} else {
return deeper(x.Parent(), y.Parent())
}
}
// forEachLexicalRef calls fn(id, block) for each identifier id in package
// info that is a reference to obj in lexical scope. block is the
// lexical block enclosing the reference. If fn returns false the
// iteration is terminated and findLexicalRefs returns false.
func forEachLexicalRef(info *loader.PackageInfo, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool {
ok := true
var stack []ast.Node
var visit func(n ast.Node) bool
visit = func(n ast.Node) bool {
if n == nil {
stack = stack[:len(stack)-1] // pop
return false
}
if !ok {
return false // bail out
}
stack = append(stack, n) // push
switch n := n.(type) {
case *ast.Ident:
if info.Uses[n] == obj {
block := enclosingBlock(&info.Info, stack)
if !fn(n, block) {
ok = false
}
}
return visit(nil) // pop stack
case *ast.SelectorExpr:
// don't visit n.Sel
ast.Inspect(n.X, visit)
return visit(nil) // pop stack, don't descend
case *ast.CompositeLit:
// Handle recursion ourselves for struct literals
// so we don't visit field identifiers.
tv := info.Types[n]
if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
if n.Type != nil {
ast.Inspect(n.Type, visit)
}
for _, elt := range n.Elts {
if kv, ok := elt.(*ast.KeyValueExpr); ok {
ast.Inspect(kv.Value, visit)
} else {
ast.Inspect(elt, visit)
}
}
return visit(nil) // pop stack, don't descend
}
}
return true
}
for _, f := range info.Files {
ast.Inspect(f, visit)
if len(stack) != 0 {
panic(stack)
}
if !ok {
break
}
}
return ok
}
// enclosingBlock returns the innermost block enclosing the specified
// AST node, specified in the form of a path from the root of the file,
// [file...n].
func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope {
for i := range stack {
n := stack[len(stack)-1-i]
// For some reason, go/types always associates a
// function's scope with its FuncType.
// TODO(adonovan): feature or a bug?
switch f := n.(type) {
case *ast.FuncDecl:
n = f.Type
case *ast.FuncLit:
n = f.Type
}
if b := info.Scopes[n]; b != nil {
return b
}
}
panic("no Scope for *ast.File")
}
func (r *renamer) checkLabel(label *types.Label) {
// Check there are no identical labels in the function's label block.
// (Label blocks don't nest, so this is easy.)
if prev := label.Parent().Lookup(r.to); prev != nil {
r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name())
r.errorf(prev.Pos(), "\twould conflict with this one")
}
}
// checkStructField checks that the field renaming will not cause
// conflicts at its declaration, or ambiguity or changes to any selection.
func (r *renamer) checkStructField(from *types.Var) {
// Check that the struct declaration is free of field conflicts,
// and field/method conflicts.
// go/types offers no easy way to get from a field (or interface
// method) to its declaring struct (or interface), so we must
// ascend the AST.
info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
// path matches this pattern:
// [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File]
// Ascend to FieldList.
var i int
for {
if _, ok := path[i].(*ast.FieldList); ok {
break
}
i++
}
i++
tStruct := path[i].(*ast.StructType)
i++
// Ascend past parens (unlikely).
for {
_, ok := path[i].(*ast.ParenExpr)
if !ok {
break
}
i++
}
if spec, ok := path[i].(*ast.TypeSpec); ok {
// This struct is also a named type.
// We must check for direct (non-promoted) field/field
// and method/field conflicts.
named := info.Defs[spec.Name].Type()
prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.to)
if len(indices) == 1 {
r.errorf(from.Pos(), "renaming this field %q to %q",
from.Name(), r.to)
r.errorf(prev.Pos(), "\twould conflict with this %s",
objectKind(prev))
return // skip checkSelections to avoid redundant errors
}
} else {
// This struct is not a named type.
// We need only check for direct (non-promoted) field/field conflicts.
T := info.Types[tStruct].Type.Underlying().(*types.Struct)
for i := 0; i < T.NumFields(); i++ {
if prev := T.Field(i); prev.Name() == r.to {
r.errorf(from.Pos(), "renaming this field %q to %q",
from.Name(), r.to)
r.errorf(prev.Pos(), "\twould conflict with this field")
return // skip checkSelections to avoid redundant errors
}
}
}
// Renaming an anonymous field requires renaming the type too. e.g.
// print(s.T) // if we rename T to U,
// type T int // this and
// var s struct {T} // this must change too.
if from.Anonymous() {
if named, ok := from.Type().(*types.Named); ok {
r.check(named.Obj())
} else if named, ok := deref(from.Type()).(*types.Named); ok {
r.check(named.Obj())
}
}
// Check integrity of existing (field and method) selections.
r.checkSelections(from)
}
// checkSelection checks that all uses and selections that resolve to
// the specified object would continue to do so after the renaming.
func (r *renamer) checkSelections(from types.Object) {
for pkg, info := range r.packages {
if id := someUse(info, from); id != nil {
if !r.checkExport(id, pkg, from) {
return
}
}
for syntax, sel := range info.Selections {
// There may be extant selections of only the old
// name or only the new name, so we must check both.
// (If neither, the renaming is sound.)
//
// In both cases, we wish to compare the lengths
// of the implicit field path (Selection.Index)
// to see if the renaming would change it.
//
// If a selection that resolves to 'from', when renamed,
// would yield a path of the same or shorter length,
// this indicates ambiguity or a changed referent,
// analogous to same- or sub-block lexical conflict.
//
// If a selection using the name 'to' would
// yield a path of the same or shorter length,
// this indicates ambiguity or shadowing,
// analogous to same- or super-block lexical conflict.
// TODO(adonovan): fix: derive from Types[syntax.X].Mode
// TODO(adonovan): test with pointer, value, addressable value.
isAddressable := true
if sel.Obj() == from {
if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil {
// Renaming this existing selection of
// 'from' may block access to an existing
// type member named 'to'.
delta := len(indices) - len(sel.Index())
if delta > 0 {
continue // no ambiguity
}
r.selectionConflict(from, delta, syntax, obj)
return
}
} else if sel.Obj().Name() == r.to {
if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from {
// Renaming 'from' may cause this existing
// selection of the name 'to' to change
// its meaning.
delta := len(indices) - len(sel.Index())
if delta > 0 {
continue // no ambiguity
}
r.selectionConflict(from, -delta, syntax, sel.Obj())
return
}
}
}
}
}
func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) {
r.errorf(from.Pos(), "renaming this %s %q to %q",
objectKind(from), from.Name(), r.to)
switch {
case delta < 0:
// analogous to sub-block conflict
r.errorf(syntax.Sel.Pos(),
"\twould change the referent of this selection")
r.errorf(obj.Pos(), "\tof this %s", objectKind(obj))
case delta == 0:
// analogous to same-block conflict
r.errorf(syntax.Sel.Pos(),
"\twould make this reference ambiguous")
r.errorf(obj.Pos(), "\twith this %s", objectKind(obj))
case delta > 0:
// analogous to super-block conflict
r.errorf(syntax.Sel.Pos(),
"\twould shadow this selection")
r.errorf(obj.Pos(), "\tof the %s declared here",
objectKind(obj))
}
}
// checkMethod performs safety checks for renaming a method.
// There are three hazards:
// - declaration conflicts
// - selection ambiguity/changes
// - entailed renamings of assignable concrete/interface types.
// We reject renamings initiated at concrete methods if it would
// change the assignability relation. For renamings of abstract
// methods, we rename all methods transitively coupled to it via
// assignability.
func (r *renamer) checkMethod(from *types.Func) {
// e.g. error.Error
if from.Pkg() == nil {
r.errorf(from.Pos(), "you cannot rename built-in method %s", from)
return
}
// ASSIGNABILITY: We reject renamings of concrete methods that
// would break a 'satisfy' constraint; but renamings of abstract
// methods are allowed to proceed, and we rename affected
// concrete and abstract methods as necessary. It is the
// initial method that determines the policy.
// Check for conflict at point of declaration.
// Check to ensure preservation of assignability requirements.
R := recv(from).Type()
if isInterface(R) {
// Abstract method
// declaration
prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to)
if prev != nil {
r.errorf(from.Pos(), "renaming this interface method %q to %q",
from.Name(), r.to)
r.errorf(prev.Pos(), "\twould conflict with this method")
return
}
// Check all interfaces that embed this one for
// declaration conflicts too.
for _, info := range r.packages {
// Start with named interface types (better errors)
for _, obj := range info.Defs {
if obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) {
f, _, _ := types.LookupFieldOrMethod(
obj.Type(), false, from.Pkg(), from.Name())
if f == nil {
continue
}
t, _, _ := types.LookupFieldOrMethod(
obj.Type(), false, from.Pkg(), r.to)
if t == nil {
continue
}
r.errorf(from.Pos(), "renaming this interface method %q to %q",
from.Name(), r.to)
r.errorf(t.Pos(), "\twould conflict with this method")
r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name())
}
}
// Now look at all literal interface types (includes named ones again).
for e, tv := range info.Types {
if e, ok := e.(*ast.InterfaceType); ok {
_ = e
_ = tv.Type.(*types.Interface)
// TODO(adonovan): implement same check as above.
}
}
}
// assignability
//
// Find the set of concrete or abstract methods directly
// coupled to abstract method 'from' by some
// satisfy.Constraint, and rename them too.
for key := range r.satisfy() {
// key = (lhs, rhs) where lhs is always an interface.
lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
if lsel == nil {
continue
}
rmethods := r.msets.MethodSet(key.RHS)
rsel := rmethods.Lookup(from.Pkg(), from.Name())
if rsel == nil {
continue
}
// If both sides have a method of this name,
// and one of them is m, the other must be coupled.
var coupled *types.Func
switch from {
case lsel.Obj():
coupled = rsel.Obj().(*types.Func)
case rsel.Obj():
coupled = lsel.Obj().(*types.Func)
default:
continue
}
// We must treat concrete-to-interface
// constraints like an implicit selection C.f of
// each interface method I.f, and check that the
// renaming leaves the selection unchanged and
// unambiguous.
//
// Fun fact: the implicit selection of C.f
// type I interface{f()}
// type C struct{I}
// func (C) g()
// var _ I = C{} // here
// yields abstract method I.f. This can make error
// messages less than obvious.
//
if !isInterface(key.RHS) {
// The logic below was derived from checkSelections.
rtosel := rmethods.Lookup(from.Pkg(), r.to)
if rtosel != nil {
rto := rtosel.Obj().(*types.Func)
delta := len(rsel.Index()) - len(rtosel.Index())
if delta < 0 {
continue // no ambiguity
}
// TODO(adonovan): record the constraint's position.
keyPos := token.NoPos
r.errorf(from.Pos(), "renaming this method %q to %q",
from.Name(), r.to)
if delta == 0 {
// analogous to same-block conflict
r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous",
r.to, key.RHS, key.LHS)
r.errorf(rto.Pos(), "\twith (%s).%s",
recv(rto).Type(), r.to)
} else {
// analogous to super-block conflict
r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s",
r.to, key.RHS, key.LHS)
r.errorf(coupled.Pos(), "\tfrom (%s).%s",
recv(coupled).Type(), r.to)
r.errorf(rto.Pos(), "\tto (%s).%s",
recv(rto).Type(), r.to)
}
return // one error is enough
}
}
if !r.changeMethods {
// This should be unreachable.
r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from)
r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled)
r.errorf(from.Pos(), "\tPlease file a bug report")
return
}
// Rename the coupled method to preserve assignability.
r.check(coupled)
}
} else {
// Concrete method
// declaration
prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to)
if prev != nil && len(indices) == 1 {
r.errorf(from.Pos(), "renaming this method %q to %q",
from.Name(), r.to)
r.errorf(prev.Pos(), "\twould conflict with this %s",
objectKind(prev))
return
}
// assignability
//
// Find the set of abstract methods coupled to concrete
// method 'from' by some satisfy.Constraint, and rename
// them too.
//
// Coupling may be indirect, e.g. I.f <-> C.f via type D.
//
// type I interface {f()}
// type C int
// type (C) f()
// type D struct{C}
// var _ I = D{}
//
for key := range r.satisfy() {
// key = (lhs, rhs) where lhs is always an interface.
if isInterface(key.RHS) {
continue
}
rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
if rsel == nil || rsel.Obj() != from {
continue // rhs does not have the method
}
lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
if lsel == nil {
continue
}
imeth := lsel.Obj().(*types.Func)
// imeth is the abstract method (e.g. I.f)
// and key.RHS is the concrete coupling type (e.g. D).
if !r.changeMethods {
r.errorf(from.Pos(), "renaming this method %q to %q",
from.Name(), r.to)
var pos token.Pos
var iface string
I := recv(imeth).Type()
if named, ok := I.(*types.Named); ok {
pos = named.Obj().Pos()
iface = "interface " + named.Obj().Name()
} else {
pos = from.Pos()
iface = I.String()
}
r.errorf(pos, "\twould make %s no longer assignable to %s",
key.RHS, iface)
r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)",
I, from.Name())
return // one error is enough
}
// Rename the coupled interface method to preserve assignability.
r.check(imeth)
}
}
// Check integrity of existing (field and method) selections.
// We skip this if there were errors above, to avoid redundant errors.
r.checkSelections(from)
}
func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool {
// Reject cross-package references if r.to is unexported.
// (Such references may be qualified identifiers or field/method
// selections.)
if !ast.IsExported(r.to) && pkg != from.Pkg() {
r.errorf(from.Pos(),
"renaming this %s %q to %q would make it unexported",
objectKind(from), from.Name(), r.to)
r.errorf(id.Pos(), "\tbreaking references from packages such as %q",
pkg.Path())
return false
}
return true
}
// satisfy returns the set of interface satisfaction constraints.
func (r *renamer) satisfy() map[satisfy.Constraint]bool {
if r.satisfyConstraints == nil {
// Compute on demand: it's expensive.
var f satisfy.Finder
for _, info := range r.packages {
f.Find(&info.Info, info.Files)
}
r.satisfyConstraints = f.Result
}
return r.satisfyConstraints
}
// -- helpers ----------------------------------------------------------
// recv returns the method's receiver.
func recv(meth *types.Func) *types.Var {
return meth.Type().(*types.Signature).Recv()
}
// someUse returns an arbitrary use of obj within info.
func someUse(info *loader.PackageInfo, obj types.Object) *ast.Ident {
for id, o := range info.Uses {
if o == obj {
return id
}
}
return nil
}
// -- Plundered from golang.org/x/tools/go/ssa -----------------
func isInterface(T types.Type) bool { return types.IsInterface(T) }
func deref(typ types.Type) types.Type {
if p, _ := typ.(*types.Pointer); p != nil {
return p.Elem()
}
return typ
}

375
vendor/golang.org/x/tools/refactor/rename/mvpkg.go generated vendored Normal file
View File

@@ -0,0 +1,375 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// licence that can be found in the LICENSE file.
// This file contains the implementation of the 'gomvpkg' command
// whose main function is in golang.org/x/tools/cmd/gomvpkg.
package rename
// TODO(matloob):
// - think about what happens if the package is moving across version control systems.
// - think about windows, which uses "\" as its directory separator.
// - dot imports are not supported. Make sure it's clearly documented.
import (
"bytes"
"fmt"
"go/ast"
"go/build"
"go/format"
"go/token"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"text/template"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/refactor/importgraph"
)
// Move, given a package path and a destination package path, will try
// to move the given package to the new path. The Move function will
// first check for any conflicts preventing the move, such as a
// package already existing at the destination package path. If the
// move can proceed, it builds an import graph to find all imports of
// the packages whose paths need to be renamed. This includes uses of
// the subpackages of the package to be moved as those packages will
// also need to be moved. It then renames all imports to point to the
// new paths, and then moves the packages to their new paths.
func Move(ctxt *build.Context, from, to, moveTmpl string) error {
srcDir, err := srcDir(ctxt, from)
if err != nil {
return err
}
// This should be the only place in the program that constructs
// file paths.
// TODO(matloob): test on Microsoft Windows.
fromDir := buildutil.JoinPath(ctxt, srcDir, filepath.FromSlash(from))
toDir := buildutil.JoinPath(ctxt, srcDir, filepath.FromSlash(to))
toParent := filepath.Dir(toDir)
if !buildutil.IsDir(ctxt, toParent) {
return fmt.Errorf("parent directory does not exist for path %s", toDir)
}
// Build the import graph and figure out which packages to update.
_, rev, errors := importgraph.Build(ctxt)
if len(errors) > 0 {
// With a large GOPATH tree, errors are inevitable.
// Report them but proceed.
fmt.Fprintf(os.Stderr, "While scanning Go workspace:\n")
for path, err := range errors {
fmt.Fprintf(os.Stderr, "Package %q: %s.\n", path, err)
}
}
// Determine the affected packages---the set of packages whose import
// statements need updating.
affectedPackages := map[string]bool{from: true}
destinations := make(map[string]string) // maps old import path to new import path
for pkg := range subpackages(ctxt, srcDir, from) {
for r := range rev[pkg] {
affectedPackages[r] = true
}
// Ensure directories have a trailing separator.
dest := strings.Replace(pkg,
filepath.Join(from, ""),
filepath.Join(to, ""),
1)
destinations[pkg] = filepath.ToSlash(dest)
}
// Load all the affected packages.
iprog, err := loadProgram(ctxt, affectedPackages)
if err != nil {
return err
}
// Prepare the move command, if one was supplied.
var cmd string
if moveTmpl != "" {
if cmd, err = moveCmd(moveTmpl, fromDir, toDir); err != nil {
return err
}
}
m := mover{
ctxt: ctxt,
rev: rev,
iprog: iprog,
from: from,
to: to,
fromDir: fromDir,
toDir: toDir,
affectedPackages: affectedPackages,
destinations: destinations,
cmd: cmd,
}
if err := m.checkValid(); err != nil {
return err
}
m.move()
return nil
}
// srcDir returns the absolute path of the srcdir containing pkg.
func srcDir(ctxt *build.Context, pkg string) (string, error) {
for _, srcDir := range ctxt.SrcDirs() {
path := buildutil.JoinPath(ctxt, srcDir, pkg)
if buildutil.IsDir(ctxt, path) {
return srcDir, nil
}
}
return "", fmt.Errorf("src dir not found for package: %s", pkg)
}
// subpackages returns the set of packages in the given srcDir whose
// import paths start with dir.
func subpackages(ctxt *build.Context, srcDir string, dir string) map[string]bool {
subs := map[string]bool{dir: true}
// Find all packages under srcDir whose import paths start with dir.
buildutil.ForEachPackage(ctxt, func(pkg string, err error) {
if err != nil {
log.Fatalf("unexpected error in ForEachPackage: %v", err)
}
// Only process the package or a sub-package
if !(strings.HasPrefix(pkg, dir) &&
(len(pkg) == len(dir) || pkg[len(dir)] == '/')) {
return
}
p, err := ctxt.Import(pkg, "", build.FindOnly)
if err != nil {
log.Fatalf("unexpected: package %s can not be located by build context: %s", pkg, err)
}
if p.SrcRoot == "" {
log.Fatalf("unexpected: could not determine srcDir for package %s: %s", pkg, err)
}
if p.SrcRoot != srcDir {
return
}
subs[pkg] = true
})
return subs
}
type mover struct {
// iprog contains all packages whose contents need to be updated
// with new package names or import paths.
iprog *loader.Program
ctxt *build.Context
// rev is the reverse import graph.
rev importgraph.Graph
// from and to are the source and destination import
// paths. fromDir and toDir are the source and destination
// absolute paths that package source files will be moved between.
from, to, fromDir, toDir string
// affectedPackages is the set of all packages whose contents need
// to be updated to reflect new package names or import paths.
affectedPackages map[string]bool
// destinations maps each subpackage to be moved to its
// destination path.
destinations map[string]string
// cmd, if not empty, will be executed to move fromDir to toDir.
cmd string
}
func (m *mover) checkValid() error {
const prefix = "invalid move destination"
match, err := regexp.MatchString("^[_\\pL][_\\pL\\p{Nd}]*$", path.Base(m.to))
if err != nil {
panic("regexp.MatchString failed")
}
if !match {
return fmt.Errorf("%s: %s; gomvpkg does not support move destinations "+
"whose base names are not valid go identifiers", prefix, m.to)
}
if buildutil.FileExists(m.ctxt, m.toDir) {
return fmt.Errorf("%s: %s conflicts with file %s", prefix, m.to, m.toDir)
}
if buildutil.IsDir(m.ctxt, m.toDir) {
return fmt.Errorf("%s: %s conflicts with directory %s", prefix, m.to, m.toDir)
}
for _, toSubPkg := range m.destinations {
if _, err := m.ctxt.Import(toSubPkg, "", build.FindOnly); err == nil {
return fmt.Errorf("%s: %s; package or subpackage %s already exists",
prefix, m.to, toSubPkg)
}
}
return nil
}
// moveCmd produces the version control move command used to move fromDir to toDir by
// executing the given template.
func moveCmd(moveTmpl, fromDir, toDir string) (string, error) {
tmpl, err := template.New("movecmd").Parse(moveTmpl)
if err != nil {
return "", err
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, struct {
Src string
Dst string
}{fromDir, toDir})
return buf.String(), err
}
func (m *mover) move() error {
filesToUpdate := make(map[*ast.File]bool)
// Change the moved package's "package" declaration to its new base name.
pkg, ok := m.iprog.Imported[m.from]
if !ok {
log.Fatalf("unexpected: package %s is not in import map", m.from)
}
newName := filepath.Base(m.to)
for _, f := range pkg.Files {
// Update all import comments.
for _, cg := range f.Comments {
c := cg.List[0]
if c.Slash >= f.Name.End() &&
sameLine(m.iprog.Fset, c.Slash, f.Name.End()) &&
(f.Decls == nil || c.Slash < f.Decls[0].Pos()) {
if strings.HasPrefix(c.Text, `// import "`) {
c.Text = `// import "` + m.to + `"`
break
}
if strings.HasPrefix(c.Text, `/* import "`) {
c.Text = `/* import "` + m.to + `" */`
break
}
}
}
f.Name.Name = newName // change package decl
filesToUpdate[f] = true
}
// Look through the external test packages (m.iprog.Created contains the external test packages).
for _, info := range m.iprog.Created {
// Change the "package" declaration of the external test package.
if info.Pkg.Path() == m.from+"_test" {
for _, f := range info.Files {
f.Name.Name = newName + "_test" // change package decl
filesToUpdate[f] = true
}
}
// Mark all the loaded external test packages, which import the "from" package,
// as affected packages and update the imports.
for _, imp := range info.Pkg.Imports() {
if imp.Path() == m.from {
m.affectedPackages[info.Pkg.Path()] = true
m.iprog.Imported[info.Pkg.Path()] = info
if err := importName(m.iprog, info, m.from, path.Base(m.from), newName); err != nil {
return err
}
}
}
}
// Update imports of that package to use the new import name.
// None of the subpackages will change their name---only the from package
// itself will.
for p := range m.rev[m.from] {
if err := importName(m.iprog, m.iprog.Imported[p], m.from, path.Base(m.from), newName); err != nil {
return err
}
}
// Update import paths for all imports by affected packages.
for ap := range m.affectedPackages {
info, ok := m.iprog.Imported[ap]
if !ok {
log.Fatalf("unexpected: package %s is not in import map", ap)
}
for _, f := range info.Files {
for _, imp := range f.Imports {
importPath, _ := strconv.Unquote(imp.Path.Value)
if newPath, ok := m.destinations[importPath]; ok {
imp.Path.Value = strconv.Quote(newPath)
oldName := path.Base(importPath)
if imp.Name != nil {
oldName = imp.Name.Name
}
newName := path.Base(newPath)
if imp.Name == nil && oldName != newName {
imp.Name = ast.NewIdent(oldName)
} else if imp.Name == nil || imp.Name.Name == newName {
imp.Name = nil
}
filesToUpdate[f] = true
}
}
}
}
for f := range filesToUpdate {
var buf bytes.Buffer
if err := format.Node(&buf, m.iprog.Fset, f); err != nil {
log.Printf("failed to pretty-print syntax tree: %v", err)
continue
}
tokenFile := m.iprog.Fset.File(f.Pos())
writeFile(tokenFile.Name(), buf.Bytes())
}
// Move the directories.
// If either the fromDir or toDir are contained under version control it is
// the user's responsibility to provide a custom move command that updates
// version control to reflect the move.
// TODO(matloob): If the parent directory of toDir does not exist, create it.
// For now, it's required that it does exist.
if m.cmd != "" {
// TODO(matloob): Verify that the windows and plan9 cases are correct.
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", m.cmd)
case "plan9":
cmd = exec.Command("rc", "-c", m.cmd)
default:
cmd = exec.Command("sh", "-c", m.cmd)
}
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
if err := cmd.Run(); err != nil {
return fmt.Errorf("version control system's move command failed: %v", err)
}
return nil
}
return moveDirectory(m.fromDir, m.toDir)
}
// sameLine reports whether two positions in the same file are on the same line.
func sameLine(fset *token.FileSet, x, y token.Pos) bool {
return fset.Position(x).Line == fset.Position(y).Line
}
var moveDirectory = func(from, to string) error {
return os.Rename(from, to)
}

463
vendor/golang.org/x/tools/refactor/rename/mvpkg_test.go generated vendored Normal file
View File

@@ -0,0 +1,463 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// licence that can be found in the LICENSE file.
package rename
import (
"fmt"
"go/build"
"go/token"
"io/ioutil"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strings"
"testing"
"golang.org/x/tools/go/buildutil"
)
func TestErrors(t *testing.T) {
tests := []struct {
ctxt *build.Context
from, to string
want string // regexp to match error, or "OK"
}{
// Simple example.
{
ctxt: fakeContext(map[string][]string{
"foo": {`package foo; type T int`},
"bar": {`package bar`},
"main": {`package main
import "foo"
var _ foo.T
`},
}),
from: "foo", to: "bar",
want: `invalid move destination: bar conflicts with directory .go.src.bar`,
},
// Subpackage already exists.
{
ctxt: fakeContext(map[string][]string{
"foo": {`package foo; type T int`},
"foo/sub": {`package sub`},
"bar/sub": {`package sub`},
"main": {`package main
import "foo"
var _ foo.T
`},
}),
from: "foo", to: "bar",
want: "invalid move destination: bar; package or subpackage bar/sub already exists",
},
// Invalid base name.
{
ctxt: fakeContext(map[string][]string{
"foo": {`package foo; type T int`},
"main": {`package main
import "foo"
var _ foo.T
`},
}),
from: "foo", to: "bar-v2.0",
want: "invalid move destination: bar-v2.0; gomvpkg does not " +
"support move destinations whose base names are not valid " +
"go identifiers",
},
{
ctxt: fakeContext(map[string][]string{
"foo": {``},
"bar": {`package bar`},
}),
from: "foo", to: "bar",
want: `no initial packages were loaded`,
},
}
for _, test := range tests {
ctxt := test.ctxt
got := make(map[string]string)
writeFile = func(filename string, content []byte) error {
got[filename] = string(content)
return nil
}
moveDirectory = func(from, to string) error {
for path, contents := range got {
if strings.HasPrefix(path, from) {
newPath := strings.Replace(path, from, to, 1)
delete(got, path)
got[newPath] = contents
}
}
return nil
}
err := Move(ctxt, test.from, test.to, "")
prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to)
if err == nil {
t.Errorf("%s: nil error. Expected error: %s", prefix, test.want)
continue
}
matched, err2 := regexp.MatchString(test.want, err.Error())
if err2 != nil {
t.Errorf("regexp.MatchString failed %s", err2)
continue
}
if !matched {
t.Errorf("%s: conflict does not match expectation:\n"+
"Error: %q\n"+
"Pattern: %q",
prefix, err.Error(), test.want)
}
}
}
func TestMoves(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("broken on Windows; see golang.org/issue/16384")
}
tests := []struct {
ctxt *build.Context
from, to string
want map[string]string
wantWarnings []string
}{
// Simple example.
{
ctxt: fakeContext(map[string][]string{
"foo": {`package foo; type T int`},
"main": {`package main
import "foo"
var _ foo.T
`},
}),
from: "foo", to: "bar",
want: map[string]string{
"/go/src/main/0.go": `package main
import "bar"
var _ bar.T
`,
"/go/src/bar/0.go": `package bar
type T int
`,
},
},
// Example with subpackage.
{
ctxt: fakeContext(map[string][]string{
"foo": {`package foo; type T int`},
"foo/sub": {`package sub; type T int`},
"main": {`package main
import "foo"
import "foo/sub"
var _ foo.T
var _ sub.T
`},
}),
from: "foo", to: "bar",
want: map[string]string{
"/go/src/main/0.go": `package main
import "bar"
import "bar/sub"
var _ bar.T
var _ sub.T
`,
"/go/src/bar/0.go": `package bar
type T int
`,
"/go/src/bar/sub/0.go": `package sub; type T int`,
},
},
// References into subpackages
{
ctxt: fakeContext(map[string][]string{
"foo": {`package foo; import "foo/a"; var _ a.T`},
"foo/a": {`package a; type T int`},
"foo/b": {`package b; import "foo/a"; var _ a.T`},
}),
from: "foo", to: "bar",
want: map[string]string{
"/go/src/bar/0.go": `package bar
import "bar/a"
var _ a.T
`,
"/go/src/bar/a/0.go": `package a; type T int`,
"/go/src/bar/b/0.go": `package b
import "bar/a"
var _ a.T
`,
},
},
// References into subpackages where directories have overlapped names
{
ctxt: fakeContext(map[string][]string{
"foo": {},
"foo/a": {`package a`},
"foo/aa": {`package bar`},
"foo/c": {`package c; import _ "foo/bar";`},
}),
from: "foo/a", to: "foo/spam",
want: map[string]string{
"/go/src/foo/spam/0.go": `package spam
`,
"/go/src/foo/aa/0.go": `package bar`,
"/go/src/foo/c/0.go": `package c; import _ "foo/bar";`,
},
},
// External test packages
{
ctxt: buildutil.FakeContext(map[string]map[string]string{
"foo": {
"0.go": `package foo; type T int`,
"0_test.go": `package foo_test; import "foo"; var _ foo.T`,
},
"baz": {
"0_test.go": `package baz_test; import "foo"; var _ foo.T`,
},
}),
from: "foo", to: "bar",
want: map[string]string{
"/go/src/bar/0.go": `package bar
type T int
`,
"/go/src/bar/0_test.go": `package bar_test
import "bar"
var _ bar.T
`,
"/go/src/baz/0_test.go": `package baz_test
import "bar"
var _ bar.T
`,
},
},
// package import comments
{
ctxt: fakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}),
from: "foo", to: "bar",
want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar"
`},
},
{
ctxt: fakeContext(map[string][]string{"foo": {`package foo /* import "baz" */`}}),
from: "foo", to: "bar",
want: map[string]string{"/go/src/bar/0.go": `package bar /* import "bar" */
`},
},
{
ctxt: fakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}),
from: "foo", to: "bar",
want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar"
`},
},
{
ctxt: fakeContext(map[string][]string{"foo": {`package foo
// import " this is not an import comment`}}),
from: "foo", to: "bar",
want: map[string]string{"/go/src/bar/0.go": `package bar
// import " this is not an import comment
`},
},
{
ctxt: fakeContext(map[string][]string{"foo": {`package foo
/* import " this is not an import comment */`}}),
from: "foo", to: "bar",
want: map[string]string{"/go/src/bar/0.go": `package bar
/* import " this is not an import comment */
`},
},
// Import name conflict generates a warning, not an error.
{
ctxt: fakeContext(map[string][]string{
"x": {},
"a": {`package a; type A int`},
"b": {`package b; type B int`},
"conflict": {`package conflict
import "a"
import "b"
var _ a.A
var _ b.B
`},
"ok": {`package ok
import "b"
var _ b.B
`},
}),
from: "b", to: "x/a",
want: map[string]string{
"/go/src/a/0.go": `package a; type A int`,
"/go/src/ok/0.go": `package ok
import "x/a"
var _ a.B
`,
"/go/src/conflict/0.go": `package conflict
import "a"
import "x/a"
var _ a.A
var _ b.B
`,
"/go/src/x/a/0.go": `package a
type B int
`,
},
wantWarnings: []string{
`/go/src/conflict/0.go:4:8: renaming this imported package name "b" to "a"`,
`/go/src/conflict/0.go:3:8: conflicts with imported package name in same block`,
`/go/src/conflict/0.go:3:8: skipping update of this file`,
},
},
// Rename with same base name.
{
ctxt: fakeContext(map[string][]string{
"x": {},
"y": {},
"x/foo": {`package foo
type T int
`},
"main": {`package main; import "x/foo"; var _ foo.T`},
}),
from: "x/foo", to: "y/foo",
want: map[string]string{
"/go/src/y/foo/0.go": `package foo
type T int
`,
"/go/src/main/0.go": `package main
import "y/foo"
var _ foo.T
`,
},
},
}
for _, test := range tests {
ctxt := test.ctxt
got := make(map[string]string)
// Populate got with starting file set. rewriteFile and moveDirectory
// will mutate got to produce resulting file set.
buildutil.ForEachPackage(ctxt, func(importPath string, err error) {
if err != nil {
return
}
path := filepath.Join("/go/src", importPath, "0.go")
if !buildutil.FileExists(ctxt, path) {
return
}
f, err := ctxt.OpenFile(path)
if err != nil {
t.Errorf("unexpected error opening file: %s", err)
return
}
bytes, err := ioutil.ReadAll(f)
f.Close()
if err != nil {
t.Errorf("unexpected error reading file: %s", err)
return
}
got[path] = string(bytes)
})
var warnings []string
reportError = func(posn token.Position, message string) {
warning := fmt.Sprintf("%s:%d:%d: %s",
filepath.ToSlash(posn.Filename), // for MS Windows
posn.Line,
posn.Column,
message)
warnings = append(warnings, warning)
}
writeFile = func(filename string, content []byte) error {
got[filename] = string(content)
return nil
}
moveDirectory = func(from, to string) error {
for path, contents := range got {
if !(strings.HasPrefix(path, from) &&
(len(path) == len(from) || path[len(from)] == filepath.Separator)) {
continue
}
newPath := strings.Replace(path, from, to, 1)
delete(got, path)
got[newPath] = contents
}
return nil
}
err := Move(ctxt, test.from, test.to, "")
prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to)
if err != nil {
t.Errorf("%s: unexpected error: %s", prefix, err)
continue
}
if !reflect.DeepEqual(warnings, test.wantWarnings) {
t.Errorf("%s: unexpected warnings:\n%s\nwant:\n%s",
prefix,
strings.Join(warnings, "\n"),
strings.Join(test.wantWarnings, "\n"))
}
for file, wantContent := range test.want {
k := filepath.FromSlash(file)
gotContent, ok := got[k]
delete(got, k)
if !ok {
// TODO(matloob): some testcases might have files that won't be
// rewritten
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)
}
}
}

603
vendor/golang.org/x/tools/refactor/rename/rename.go generated vendored Normal file
View File

@@ -0,0 +1,603 @@
// 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 contains the implementation of the 'gorename' command
// whose main function is in golang.org/x/tools/cmd/gorename.
// See the Usage constant for the command documentation.
package rename // import "golang.org/x/tools/refactor/rename"
import (
"bytes"
"errors"
"fmt"
"go/ast"
"go/build"
"go/format"
"go/parser"
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"regexp"
"sort"
"strconv"
"strings"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/refactor/importgraph"
"golang.org/x/tools/refactor/satisfy"
)
const Usage = `gorename: precise type-safe renaming of identifiers in Go source code.
Usage:
gorename (-from <spec> | -offset <file>:#<byte-offset>) -to <name> [-force]
You must specify the object (named entity) to rename using the -offset
or -from flag. Exactly one must be specified.
Flags:
-offset specifies the filename and byte offset of an identifier to rename.
This form is intended for use by text editors.
-from specifies the object to rename using a query notation;
This form is intended for interactive use at the command line.
A legal -from query has one of the following forms:
"encoding/json".Decoder.Decode method of package-level named type
(*"encoding/json".Decoder).Decode ditto, alternative syntax
"encoding/json".Decoder.buf field of package-level named struct type
"encoding/json".HTMLEscape package member (const, func, var, type)
"encoding/json".Decoder.Decode::x local object x within a method
"encoding/json".HTMLEscape::x local object x within a function
"encoding/json"::x object x anywhere within a package
json.go::x object x within file json.go
Double-quotes must be escaped when writing a shell command.
Quotes may be omitted for single-segment import paths such as "fmt".
For methods, the parens and '*' on the receiver type are both
optional.
It is an error if one of the ::x queries matches multiple
objects.
-to the new name.
-force causes the renaming to proceed even if conflicts were reported.
The resulting program may be ill-formed, or experience a change
in behaviour.
WARNING: this flag may even cause the renaming tool to crash.
(In due course this bug will be fixed by moving certain
analyses into the type-checker.)
-d display diffs instead of rewriting files
-v enables verbose logging.
gorename automatically computes the set of packages that might be
affected. For a local renaming, this is just the package specified by
-from or -offset, but for a potentially exported name, gorename scans
the workspace ($GOROOT and $GOPATH).
gorename rejects renamings of concrete methods that would change the
assignability relation between types and interfaces. If the interface
change was intentional, initiate the renaming at the interface method.
gorename rejects any renaming that would create a conflict at the point
of declaration, or a reference conflict (ambiguity or shadowing), or
anything else that could cause the resulting program not to compile.
Examples:
$ gorename -offset file.go:#123 -to foo
Rename the object whose identifier is at byte offset 123 within file file.go.
$ gorename -from '"bytes".Buffer.Len' -to Size
Rename the "Len" method of the *bytes.Buffer type to "Size".
---- TODO ----
Correctness:
- handle dot imports correctly
- document limitations (reflection, 'implements' algorithm).
- sketch a proof of exhaustiveness.
Features:
- support running on packages specified as *.go files on the command line
- support running on programs containing errors (loader.Config.AllowErrors)
- allow users to specify a scope other than "global" (to avoid being
stuck by neglected packages in $GOPATH that don't build).
- support renaming the package clause (no object)
- support renaming an import path (no ident or object)
(requires filesystem + SCM updates).
- detect and reject edits to autogenerated files (cgo, protobufs)
and optionally $GOROOT packages.
- report all conflicts, or at least all qualitatively distinct ones.
Sometimes we stop to avoid redundancy, but
it may give a disproportionate sense of safety in -force mode.
- support renaming all instances of a pattern, e.g.
all receiver vars of a given type,
all local variables of a given type,
all PkgNames for a given package.
- emit JSON output for other editors and tools.
`
var (
// Force enables patching of the source files even if conflicts were reported.
// The resulting program may be ill-formed.
// It may even cause gorename to crash. TODO(adonovan): fix that.
Force bool
// Diff causes the tool to display diffs instead of rewriting files.
Diff bool
// DiffCmd specifies the diff command used by the -d feature.
// (The command must accept a -u flag and two filename arguments.)
DiffCmd = "diff"
// ConflictError is returned by Main when it aborts the renaming due to conflicts.
// (It is distinguished because the interesting errors are the conflicts themselves.)
ConflictError = errors.New("renaming aborted due to conflicts")
// Verbose enables extra logging.
Verbose bool
)
var stdout io.Writer = os.Stdout
type renamer struct {
iprog *loader.Program
objsToUpdate map[types.Object]bool
hadConflicts bool
from, to string
satisfyConstraints map[satisfy.Constraint]bool
packages map[*types.Package]*loader.PackageInfo // subset of iprog.AllPackages to inspect
msets typeutil.MethodSetCache
changeMethods bool
}
var reportError = func(posn token.Position, message string) {
fmt.Fprintf(os.Stderr, "%s: %s\n", posn, message)
}
// importName renames imports of fromPath within the package specified by info.
// If fromName is not empty, importName renames only imports as fromName.
// If the renaming would lead to a conflict, the file is left unchanged.
func importName(iprog *loader.Program, info *loader.PackageInfo, fromPath, fromName, to string) error {
if fromName == to {
return nil // no-op (e.g. rename x/foo to y/foo)
}
for _, f := range info.Files {
var from types.Object
for _, imp := range f.Imports {
importPath, _ := strconv.Unquote(imp.Path.Value)
importName := path.Base(importPath)
if imp.Name != nil {
importName = imp.Name.Name
}
if importPath == fromPath && (fromName == "" || importName == fromName) {
from = info.Implicits[imp]
break
}
}
if from == nil {
continue
}
r := renamer{
iprog: iprog,
objsToUpdate: make(map[types.Object]bool),
to: to,
packages: map[*types.Package]*loader.PackageInfo{info.Pkg: info},
}
r.check(from)
if r.hadConflicts {
reportError(iprog.Fset.Position(f.Imports[0].Pos()),
"skipping update of this file")
continue // ignore errors; leave the existing name
}
if err := r.update(); err != nil {
return err
}
}
return nil
}
func Main(ctxt *build.Context, offsetFlag, fromFlag, to string) error {
// -- Parse the -from or -offset specifier ----------------------------
if (offsetFlag == "") == (fromFlag == "") {
return fmt.Errorf("exactly one of the -from and -offset flags must be specified")
}
if !isValidIdentifier(to) {
return fmt.Errorf("-to %q: not a valid identifier", to)
}
if Diff {
defer func(saved func(string, []byte) error) { writeFile = saved }(writeFile)
writeFile = diff
}
var spec *spec
var err error
if fromFlag != "" {
spec, err = parseFromFlag(ctxt, fromFlag)
} else {
spec, err = parseOffsetFlag(ctxt, offsetFlag)
}
if err != nil {
return err
}
if spec.fromName == to {
return fmt.Errorf("the old and new names are the same: %s", to)
}
// -- Load the program consisting of the initial package -------------
iprog, err := loadProgram(ctxt, map[string]bool{spec.pkg: true})
if err != nil {
return err
}
fromObjects, err := findFromObjects(iprog, spec)
if err != nil {
return err
}
// -- Load a larger program, for global renamings ---------------------
if requiresGlobalRename(fromObjects, to) {
// For a local refactoring, we needn't load more
// packages, but if the renaming affects the package's
// API, we we must load all packages that depend on the
// package defining the object, plus their tests.
if Verbose {
log.Print("Potentially global renaming; scanning workspace...")
}
// Scan the workspace and build the import graph.
_, rev, errors := importgraph.Build(ctxt)
if len(errors) > 0 {
// With a large GOPATH tree, errors are inevitable.
// Report them but proceed.
fmt.Fprintf(os.Stderr, "While scanning Go workspace:\n")
for path, err := range errors {
fmt.Fprintf(os.Stderr, "Package %q: %s.\n", path, err)
}
}
// Enumerate the set of potentially affected packages.
affectedPackages := make(map[string]bool)
for _, obj := range fromObjects {
// External test packages are never imported,
// so they will never appear in the graph.
for path := range rev.Search(obj.Pkg().Path()) {
affectedPackages[path] = true
}
}
// TODO(adonovan): allow the user to specify the scope,
// or -ignore patterns? Computing the scope when we
// don't (yet) support inputs containing errors can make
// the tool rather brittle.
// Re-load the larger program.
iprog, err = loadProgram(ctxt, affectedPackages)
if err != nil {
return err
}
fromObjects, err = findFromObjects(iprog, spec)
if err != nil {
return err
}
}
// -- Do the renaming -------------------------------------------------
r := renamer{
iprog: iprog,
objsToUpdate: make(map[types.Object]bool),
from: spec.fromName,
to: to,
packages: make(map[*types.Package]*loader.PackageInfo),
}
// A renaming initiated at an interface method indicates the
// intention to rename abstract and concrete methods as needed
// to preserve assignability.
for _, obj := range fromObjects {
if obj, ok := obj.(*types.Func); ok {
recv := obj.Type().(*types.Signature).Recv()
if recv != nil && isInterface(recv.Type().Underlying()) {
r.changeMethods = true
break
}
}
}
// Only the initially imported packages (iprog.Imported) and
// their external tests (iprog.Created) should be inspected or
// modified, as only they have type-checked functions bodies.
// The rest are just dependencies, needed only for package-level
// type information.
for _, info := range iprog.Imported {
r.packages[info.Pkg] = info
}
for _, info := range iprog.Created { // (tests)
r.packages[info.Pkg] = info
}
for _, from := range fromObjects {
r.check(from)
}
if r.hadConflicts && !Force {
return ConflictError
}
return r.update()
}
// loadProgram loads the specified set of packages (plus their tests)
// and all their dependencies, from source, through the specified build
// context. Only packages in pkgs will have their functions bodies typechecked.
func loadProgram(ctxt *build.Context, pkgs map[string]bool) (*loader.Program, error) {
conf := loader.Config{
Build: ctxt,
ParserMode: parser.ParseComments,
// TODO(adonovan): enable this. Requires making a lot of code more robust!
AllowErrors: false,
}
// Optimization: don't type-check the bodies of functions in our
// dependencies, since we only need exported package members.
conf.TypeCheckFuncBodies = func(p string) bool {
return pkgs[p] || pkgs[strings.TrimSuffix(p, "_test")]
}
if Verbose {
var list []string
for pkg := range pkgs {
list = append(list, pkg)
}
sort.Strings(list)
for _, pkg := range list {
log.Printf("Loading package: %s", pkg)
}
}
for pkg := range pkgs {
conf.ImportWithTests(pkg)
}
// Ideally we would just return conf.Load() here, but go/types
// reports certain "soft" errors that gc does not (Go issue 14596).
// As a workaround, we set AllowErrors=true and then duplicate
// the loader's error checking but allow soft errors.
// It would be nice if the loader API permitted "AllowErrors: soft".
conf.AllowErrors = true
prog, err := conf.Load()
if err != nil {
return nil, err
}
var errpkgs []string
// Report hard errors in indirectly imported packages.
for _, info := range prog.AllPackages {
if containsHardErrors(info.Errors) {
errpkgs = append(errpkgs, info.Pkg.Path())
}
}
if errpkgs != nil {
var more string
if len(errpkgs) > 3 {
more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
errpkgs = errpkgs[:3]
}
return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
strings.Join(errpkgs, ", "), more)
}
return prog, nil
}
func containsHardErrors(errors []error) bool {
for _, err := range errors {
if err, ok := err.(types.Error); ok && err.Soft {
continue
}
return true
}
return false
}
// requiresGlobalRename reports whether this renaming could potentially
// affect other packages in the Go workspace.
func requiresGlobalRename(fromObjects []types.Object, to string) bool {
var tfm bool
for _, from := range fromObjects {
if from.Exported() {
return true
}
switch objectKind(from) {
case "type", "field", "method":
tfm = true
}
}
if ast.IsExported(to) && tfm {
// A global renaming may be necessary even if we're
// exporting a previous unexported name, since if it's
// the name of a type, field or method, this could
// change selections in other packages.
// (We include "type" in this list because a type
// used as an embedded struct field entails a field
// renaming.)
return true
}
return false
}
// update updates the input files.
func (r *renamer) update() error {
// We use token.File, not filename, since a file may appear to
// belong to multiple packages and be parsed more than once.
// token.File captures this distinction; filename does not.
var nidents int
var filesToUpdate = make(map[*token.File]bool)
docRegexp := regexp.MustCompile(`\b` + r.from + `\b`)
for _, info := range r.packages {
// Mutate the ASTs and note the filenames.
for id, obj := range info.Defs {
if r.objsToUpdate[obj] {
nidents++
id.Name = r.to
filesToUpdate[r.iprog.Fset.File(id.Pos())] = true
// Perform the rename in doc comments too.
if doc := r.docComment(id); doc != nil {
for _, comment := range doc.List {
comment.Text = docRegexp.ReplaceAllString(comment.Text, r.to)
}
}
}
}
for id, obj := range info.Uses {
if r.objsToUpdate[obj] {
nidents++
id.Name = r.to
filesToUpdate[r.iprog.Fset.File(id.Pos())] = true
}
}
}
// Renaming not supported if cgo files are affected.
var generatedFileNames []string
for _, info := range r.packages {
for _, f := range info.Files {
tokenFile := r.iprog.Fset.File(f.Pos())
if filesToUpdate[tokenFile] && generated(f, tokenFile) {
generatedFileNames = append(generatedFileNames, tokenFile.Name())
}
}
}
if !Force && len(generatedFileNames) > 0 {
return fmt.Errorf("refusing to modify generated file%s containing DO NOT EDIT marker: %v", plural(len(generatedFileNames)), generatedFileNames)
}
// Write affected files.
var nerrs, npkgs int
for _, info := range r.packages {
first := true
for _, f := range info.Files {
tokenFile := r.iprog.Fset.File(f.Pos())
if filesToUpdate[tokenFile] {
if first {
npkgs++
first = false
if Verbose {
log.Printf("Updating package %s", info.Pkg.Path())
}
}
filename := tokenFile.Name()
var buf bytes.Buffer
if err := format.Node(&buf, r.iprog.Fset, f); err != nil {
log.Printf("failed to pretty-print syntax tree: %v", err)
nerrs++
continue
}
if err := writeFile(filename, buf.Bytes()); err != nil {
log.Print(err)
nerrs++
}
}
}
}
if !Diff {
fmt.Printf("Renamed %d occurrence%s in %d file%s in %d package%s.\n",
nidents, plural(nidents),
len(filesToUpdate), plural(len(filesToUpdate)),
npkgs, plural(npkgs))
}
if nerrs > 0 {
return fmt.Errorf("failed to rewrite %d file%s", nerrs, plural(nerrs))
}
return nil
}
// docComment returns the doc for an identifier.
func (r *renamer) docComment(id *ast.Ident) *ast.CommentGroup {
_, nodes, _ := r.iprog.PathEnclosingInterval(id.Pos(), id.End())
for _, node := range nodes {
switch decl := node.(type) {
case *ast.FuncDecl:
return decl.Doc
case *ast.Field:
return decl.Doc
case *ast.GenDecl:
return decl.Doc
// For {Type,Value}Spec, if the doc on the spec is absent,
// search for the enclosing GenDecl
case *ast.TypeSpec:
if decl.Doc != nil {
return decl.Doc
}
case *ast.ValueSpec:
if decl.Doc != nil {
return decl.Doc
}
case *ast.Ident:
default:
return nil
}
}
return nil
}
func plural(n int) string {
if n != 1 {
return "s"
}
return ""
}
// writeFile is a seam for testing and for the -d flag.
var writeFile = reallyWriteFile
func reallyWriteFile(filename string, content []byte) error {
return ioutil.WriteFile(filename, content, 0644)
}
func diff(filename string, content []byte) error {
renamed := fmt.Sprintf("%s.%d.renamed", filename, os.Getpid())
if err := ioutil.WriteFile(renamed, content, 0644); err != nil {
return err
}
defer os.Remove(renamed)
diff, err := exec.Command(DiffCmd, "-u", filename, renamed).CombinedOutput()
if len(diff) > 0 {
// diff exits with a non-zero status when the files don't match.
// Ignore that failure as long as we get output.
stdout.Write(diff)
return nil
}
if err != nil {
return fmt.Errorf("computing diff: %v", err)
}
return nil
}

1334
vendor/golang.org/x/tools/refactor/rename/rename_test.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

593
vendor/golang.org/x/tools/refactor/rename/spec.go generated vendored Normal file
View File

@@ -0,0 +1,593 @@
// 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
// This file contains logic related to specifying a renaming: parsing of
// the flags as a form of query, and finding the object(s) it denotes.
// See Usage for flag details.
import (
"bytes"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"go/types"
"log"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
)
// A spec specifies an entity to rename.
//
// It is populated from an -offset flag or -from query;
// see Usage for the allowed -from query forms.
//
type spec struct {
// pkg is the package containing the position
// specified by the -from or -offset flag.
// If filename == "", our search for the 'from' entity
// is restricted to this package.
pkg string
// The original name of the entity being renamed.
// If the query had a ::from component, this is that;
// otherwise it's the last segment, e.g.
// (encoding/json.Decoder).from
// encoding/json.from
fromName string
// -- The remaining fields are private to this file. All are optional. --
// The query's ::x suffix, if any.
searchFor string
// e.g. "Decoder" in "(encoding/json.Decoder).fieldOrMethod"
// or "encoding/json.Decoder
pkgMember string
// e.g. fieldOrMethod in "(encoding/json.Decoder).fieldOrMethod"
typeMember string
// Restricts the query to this file.
// Implied by -from="file.go::x" and -offset flags.
filename string
// Byte offset of the 'from' identifier within the file named 'filename'.
// -offset mode only.
offset int
}
// parseFromFlag interprets the "-from" flag value as a renaming specification.
// See Usage in rename.go for valid formats.
func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) {
var spec spec
var main string // sans "::x" suffix
switch parts := strings.Split(fromFlag, "::"); len(parts) {
case 1:
main = parts[0]
case 2:
main = parts[0]
spec.searchFor = parts[1]
if parts[1] == "" {
// error
}
default:
return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag)
}
if strings.HasSuffix(main, ".go") {
// main is "filename.go"
if spec.searchFor == "" {
return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
}
spec.filename = main
if !buildutil.FileExists(ctxt, spec.filename) {
return nil, fmt.Errorf("no such file: %s", spec.filename)
}
bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
if err != nil {
return nil, err
}
spec.pkg = bp.ImportPath
} else {
// main is one of:
// "importpath"
// "importpath".member
// (*"importpath".type).fieldormethod (parens and star optional)
if err := parseObjectSpec(&spec, main); err != nil {
return nil, err
}
}
if spec.searchFor != "" {
spec.fromName = spec.searchFor
}
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
// Sanitize the package.
bp, err := ctxt.Import(spec.pkg, cwd, build.FindOnly)
if err != nil {
return nil, fmt.Errorf("can't find package %q", spec.pkg)
}
spec.pkg = bp.ImportPath
if !isValidIdentifier(spec.fromName) {
return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName)
}
if Verbose {
log.Printf("-from spec: %+v", spec)
}
return &spec, nil
}
// parseObjectSpec parses main as one of the non-filename forms of
// object specification.
func parseObjectSpec(spec *spec, main string) error {
// Parse main as a Go expression, albeit a strange one.
e, _ := parser.ParseExpr(main)
if pkg := parseImportPath(e); pkg != "" {
// e.g. bytes or "encoding/json": a package
spec.pkg = pkg
if spec.searchFor == "" {
return fmt.Errorf("-from %q: package import path %q must have a ::name suffix",
main, main)
}
return nil
}
if e, ok := e.(*ast.SelectorExpr); ok {
x := unparen(e.X)
// Strip off star constructor, if any.
if star, ok := x.(*ast.StarExpr); ok {
x = star.X
}
if pkg := parseImportPath(x); pkg != "" {
// package member e.g. "encoding/json".HTMLEscape
spec.pkg = pkg // e.g. "encoding/json"
spec.pkgMember = e.Sel.Name // e.g. "HTMLEscape"
spec.fromName = e.Sel.Name
return nil
}
if x, ok := x.(*ast.SelectorExpr); ok {
// field/method of type e.g. ("encoding/json".Decoder).Decode
y := unparen(x.X)
if pkg := parseImportPath(y); pkg != "" {
spec.pkg = pkg // e.g. "encoding/json"
spec.pkgMember = x.Sel.Name // e.g. "Decoder"
spec.typeMember = e.Sel.Name // e.g. "Decode"
spec.fromName = e.Sel.Name
return nil
}
}
}
return fmt.Errorf("-from %q: invalid expression", main)
}
// parseImportPath returns the import path of the package denoted by e.
// Any import path may be represented as a string literal;
// single-segment import paths (e.g. "bytes") may also be represented as
// ast.Ident. parseImportPath returns "" for all other expressions.
func parseImportPath(e ast.Expr) string {
switch e := e.(type) {
case *ast.Ident:
return e.Name // e.g. bytes
case *ast.BasicLit:
if e.Kind == token.STRING {
pkgname, _ := strconv.Unquote(e.Value)
return pkgname // e.g. "encoding/json"
}
}
return ""
}
// parseOffsetFlag interprets the "-offset" flag value as a renaming specification.
func parseOffsetFlag(ctxt *build.Context, offsetFlag string) (*spec, error) {
var spec spec
// Validate -offset, e.g. file.go:#123
parts := strings.Split(offsetFlag, ":#")
if len(parts) != 2 {
return nil, fmt.Errorf("-offset %q: invalid offset specification", offsetFlag)
}
spec.filename = parts[0]
if !buildutil.FileExists(ctxt, spec.filename) {
return nil, fmt.Errorf("no such file: %s", spec.filename)
}
bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
if err != nil {
return nil, err
}
spec.pkg = bp.ImportPath
for _, r := range parts[1] {
if !isDigit(r) {
return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
}
}
spec.offset, err = strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
}
// Parse the file and check there's an identifier at that offset.
fset := token.NewFileSet()
f, err := buildutil.ParseFile(fset, ctxt, nil, wd, spec.filename, parser.ParseComments)
if err != nil {
return nil, fmt.Errorf("-offset %q: cannot parse file: %s", offsetFlag, err)
}
id := identAtOffset(fset, f, spec.offset)
if id == nil {
return nil, fmt.Errorf("-offset %q: no identifier at this position", offsetFlag)
}
spec.fromName = id.Name
return &spec, nil
}
var wd = func() string {
wd, err := os.Getwd()
if err != nil {
panic("cannot get working directory: " + err.Error())
}
return wd
}()
// For source trees built with 'go build', the -from or -offset
// spec identifies exactly one initial 'from' object to rename ,
// but certain proprietary build systems allow a single file to
// appear in multiple packages (e.g. the test package contains a
// copy of its library), so there may be multiple objects for
// the same source entity.
func findFromObjects(iprog *loader.Program, spec *spec) ([]types.Object, error) {
if spec.filename != "" {
return findFromObjectsInFile(iprog, spec)
}
// Search for objects defined in specified package.
// TODO(adonovan): the iprog.ImportMap has an entry {"main": ...}
// for main packages, even though that's not an import path.
// Seems like a bug.
//
// pkg := iprog.ImportMap[spec.pkg]
// if pkg == nil {
// return fmt.Errorf("cannot find package %s", spec.pkg) // can't happen?
// }
// info := iprog.AllPackages[pkg]
// Workaround: lookup by value.
var info *loader.PackageInfo
var pkg *types.Package
for pkg, info = range iprog.AllPackages {
if pkg.Path() == spec.pkg {
break
}
}
if info == nil {
return nil, fmt.Errorf("package %q was not loaded", spec.pkg)
}
objects, err := findObjects(info, spec)
if err != nil {
return nil, err
}
if len(objects) > 1 {
// ambiguous "*" scope query
return nil, ambiguityError(iprog.Fset, objects)
}
return objects, nil
}
func findFromObjectsInFile(iprog *loader.Program, spec *spec) ([]types.Object, error) {
var fromObjects []types.Object
for _, info := range iprog.AllPackages {
// restrict to specified filename
// NB: under certain proprietary build systems, a given
// filename may appear in multiple packages.
for _, f := range info.Files {
thisFile := iprog.Fset.File(f.Pos())
if !sameFile(thisFile.Name(), spec.filename) {
continue
}
// This package contains the query file.
if spec.offset != 0 {
// We cannot refactor generated files since position information is invalidated.
if generated(f, thisFile) {
return nil, fmt.Errorf("cannot rename identifiers in generated file containing DO NOT EDIT marker: %s", thisFile.Name())
}
// Search for a specific ident by file/offset.
id := identAtOffset(iprog.Fset, f, spec.offset)
if id == nil {
// can't happen?
return nil, fmt.Errorf("identifier not found")
}
obj := info.Uses[id]
if obj == nil {
obj = info.Defs[id]
if obj == nil {
// Ident without Object.
// Package clause?
pos := thisFile.Pos(spec.offset)
_, path, _ := iprog.PathEnclosingInterval(pos, pos)
if len(path) == 2 { // [Ident File]
// TODO(adonovan): support this case.
return nil, fmt.Errorf("cannot rename %q: renaming package clauses is not yet supported",
path[1].(*ast.File).Name.Name)
}
// Implicit y in "switch y := x.(type) {"?
if obj := typeSwitchVar(&info.Info, path); obj != nil {
return []types.Object{obj}, nil
}
// Probably a type error.
return nil, fmt.Errorf("cannot find object for %q", id.Name)
}
}
if obj.Pkg() == nil {
return nil, fmt.Errorf("cannot rename predeclared identifiers (%s)", obj)
}
fromObjects = append(fromObjects, obj)
} else {
// do a package-wide query
objects, err := findObjects(info, spec)
if err != nil {
return nil, err
}
// filter results: only objects defined in thisFile
var filtered []types.Object
for _, obj := range objects {
if iprog.Fset.File(obj.Pos()) == thisFile {
filtered = append(filtered, obj)
}
}
if len(filtered) == 0 {
return nil, fmt.Errorf("no object %q declared in file %s",
spec.fromName, spec.filename)
} else if len(filtered) > 1 {
return nil, ambiguityError(iprog.Fset, filtered)
}
fromObjects = append(fromObjects, filtered[0])
}
break
}
}
if len(fromObjects) == 0 {
// can't happen?
return nil, fmt.Errorf("file %s was not part of the loaded program", spec.filename)
}
return fromObjects, nil
}
func typeSwitchVar(info *types.Info, path []ast.Node) types.Object {
if len(path) > 3 {
// [Ident AssignStmt TypeSwitchStmt...]
if sw, ok := path[2].(*ast.TypeSwitchStmt); ok {
// choose the first case.
if len(sw.Body.List) > 0 {
obj := info.Implicits[sw.Body.List[0].(*ast.CaseClause)]
if obj != nil {
return obj
}
}
}
}
return nil
}
// On success, findObjects returns the list of objects named
// spec.fromName matching the spec. On success, the result has exactly
// one element unless spec.searchFor!="", in which case it has at least one
// element.
//
func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) {
if spec.pkgMember == "" {
if spec.searchFor == "" {
panic(spec)
}
objects := searchDefs(&info.Info, spec.searchFor)
if objects == nil {
return nil, fmt.Errorf("no object %q declared in package %q",
spec.searchFor, info.Pkg.Path())
}
return objects, nil
}
pkgMember := info.Pkg.Scope().Lookup(spec.pkgMember)
if pkgMember == nil {
return nil, fmt.Errorf("package %q has no member %q",
info.Pkg.Path(), spec.pkgMember)
}
var searchFunc *types.Func
if spec.typeMember == "" {
// package member
if spec.searchFor == "" {
return []types.Object{pkgMember}, nil
}
// Search within pkgMember, which must be a function.
searchFunc, _ = pkgMember.(*types.Func)
if searchFunc == nil {
return nil, fmt.Errorf("cannot search for %q within %s %q",
spec.searchFor, objectKind(pkgMember), pkgMember)
}
} else {
// field/method of type
// e.g. (encoding/json.Decoder).Decode
// or ::x within it.
tName, _ := pkgMember.(*types.TypeName)
if tName == nil {
return nil, fmt.Errorf("%s.%s is a %s, not a type",
info.Pkg.Path(), pkgMember.Name(), objectKind(pkgMember))
}
// search within named type.
obj, _, _ := types.LookupFieldOrMethod(tName.Type(), true, info.Pkg, spec.typeMember)
if obj == nil {
return nil, fmt.Errorf("cannot find field or method %q of %s %s.%s",
spec.typeMember, typeKind(tName.Type()), info.Pkg.Path(), tName.Name())
}
if spec.searchFor == "" {
// If it is an embedded field, return the type of the field.
if v, ok := obj.(*types.Var); ok && v.Anonymous() {
switch t := v.Type().(type) {
case *types.Pointer:
return []types.Object{t.Elem().(*types.Named).Obj()}, nil
case *types.Named:
return []types.Object{t.Obj()}, nil
}
}
return []types.Object{obj}, nil
}
searchFunc, _ = obj.(*types.Func)
if searchFunc == nil {
return nil, fmt.Errorf("cannot search for local name %q within %s (%s.%s).%s; need a function",
spec.searchFor, objectKind(obj), info.Pkg.Path(), tName.Name(),
obj.Name())
}
if isInterface(tName.Type()) {
return nil, fmt.Errorf("cannot search for local name %q within abstract method (%s.%s).%s",
spec.searchFor, info.Pkg.Path(), tName.Name(), searchFunc.Name())
}
}
// -- search within function or method --
decl := funcDecl(info, searchFunc)
if decl == nil {
return nil, fmt.Errorf("cannot find syntax for %s", searchFunc) // can't happen?
}
var objects []types.Object
for _, obj := range searchDefs(&info.Info, spec.searchFor) {
// We use positions, not scopes, to determine whether
// the obj is within searchFunc. This is clumsy, but the
// alternative, using the types.Scope tree, doesn't
// account for non-lexical objects like fields and
// interface methods.
if decl.Pos() <= obj.Pos() && obj.Pos() < decl.End() && obj != searchFunc {
objects = append(objects, obj)
}
}
if objects == nil {
return nil, fmt.Errorf("no local definition of %q within %s",
spec.searchFor, searchFunc)
}
return objects, nil
}
func funcDecl(info *loader.PackageInfo, fn *types.Func) *ast.FuncDecl {
for _, f := range info.Files {
for _, d := range f.Decls {
if d, ok := d.(*ast.FuncDecl); ok && info.Defs[d.Name] == fn {
return d
}
}
}
return nil
}
func searchDefs(info *types.Info, name string) []types.Object {
var objects []types.Object
for id, obj := range info.Defs {
if obj == nil {
// e.g. blank ident.
// TODO(adonovan): but also implicit y in
// switch y := x.(type)
// Needs some thought.
continue
}
if id.Name == name {
objects = append(objects, obj)
}
}
return objects
}
func identAtOffset(fset *token.FileSet, f *ast.File, offset int) *ast.Ident {
var found *ast.Ident
ast.Inspect(f, func(n ast.Node) bool {
if id, ok := n.(*ast.Ident); ok {
idpos := fset.Position(id.Pos()).Offset
if idpos <= offset && offset < idpos+len(id.Name) {
found = id
}
}
return found == nil // keep traversing only until found
})
return found
}
// ambiguityError returns an error describing an ambiguous "*" scope query.
func ambiguityError(fset *token.FileSet, objects []types.Object) error {
var buf bytes.Buffer
for i, obj := range objects {
if i > 0 {
buf.WriteString(", ")
}
posn := fset.Position(obj.Pos())
fmt.Fprintf(&buf, "%s at %s:%d:%d",
objectKind(obj), filepath.Base(posn.Filename), posn.Line, posn.Column)
}
return fmt.Errorf("ambiguous specifier %s matches %s",
objects[0].Name(), buf.String())
}
// Matches cgo generated comment as well as the proposed standard:
// https://golang.org/s/generatedcode
var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)
// generated reports whether ast.File is a generated file.
func generated(f *ast.File, tokenFile *token.File) bool {
// Iterate over the comments in the file
for _, commentGroup := range f.Comments {
for _, comment := range commentGroup.List {
if matched := generatedRx.MatchString(comment.Text); matched {
// Check if comment is at the beginning of the line in source
if pos := tokenFile.Position(comment.Slash); pos.Column == 1 {
return true
}
}
}
}
return false
}

105
vendor/golang.org/x/tools/refactor/rename/util.go generated vendored Normal file
View File

@@ -0,0 +1,105 @@
// 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 (
"go/ast"
"go/token"
"go/types"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"unicode"
"golang.org/x/tools/go/ast/astutil"
)
func objectKind(obj types.Object) string {
switch obj := obj.(type) {
case *types.PkgName:
return "imported package name"
case *types.TypeName:
return "type"
case *types.Var:
if obj.IsField() {
return "field"
}
case *types.Func:
if obj.Type().(*types.Signature).Recv() != nil {
return "method"
}
}
// label, func, var, const
return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
}
func typeKind(T types.Type) string {
return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(T.Underlying()).String(), "*types."))
}
// NB: for renamings, blank is not considered valid.
func isValidIdentifier(id string) bool {
if id == "" || id == "_" {
return false
}
for i, r := range id {
if !isLetter(r) && (i == 0 || !isDigit(r)) {
return false
}
}
return token.Lookup(id) == token.IDENT
}
// isLocal reports whether obj is local to some function.
// Precondition: not a struct field or interface method.
func isLocal(obj types.Object) bool {
// [... 5=stmt 4=func 3=file 2=pkg 1=universe]
var depth int
for scope := obj.Parent(); scope != nil; scope = scope.Parent() {
depth++
}
return depth >= 4
}
func isPackageLevel(obj types.Object) bool {
return obj.Pkg().Scope().Lookup(obj.Name()) == obj
}
// -- Plundered from go/scanner: ---------------------------------------
func isLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}
func isDigit(ch rune) bool {
return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
}
// -- Plundered from golang.org/x/tools/cmd/guru -----------------
// sameFile returns true if x and y have the same basename and denote
// the same file.
//
func sameFile(x, y string) bool {
if runtime.GOOS == "windows" {
x = filepath.ToSlash(x)
y = filepath.ToSlash(y)
}
if x == y {
return true
}
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
if xi, err := os.Stat(x); err == nil {
if yi, err := os.Stat(y); err == nil {
return os.SameFile(xi, yi)
}
}
}
return false
}
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }