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

1
vendor/golang.org/x/tools/refactor/README generated vendored Normal file
View File

@@ -0,0 +1 @@
golang.org/x/tools/refactor: libraries for refactoring tools.

373
vendor/golang.org/x/tools/refactor/eg/eg.go generated vendored Normal file
View File

@@ -0,0 +1,373 @@
// 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 eg implements the example-based refactoring tool whose
// command-line is defined in golang.org/x/tools/cmd/eg.
package eg // import "golang.org/x/tools/refactor/eg"
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/printer"
"go/token"
"go/types"
"os"
)
const Help = `
This tool implements example-based refactoring of expressions.
The transformation is specified as a Go file defining two functions,
'before' and 'after', of identical types. Each function body consists
of a single statement: either a return statement with a single
(possibly multi-valued) expression, or an expression statement. The
'before' expression specifies a pattern and the 'after' expression its
replacement.
package P
import ( "errors"; "fmt" )
func before(s string) error { return fmt.Errorf("%s", s) }
func after(s string) error { return errors.New(s) }
The expression statement form is useful when the expression has no
result, for example:
func before(msg string) { log.Fatalf("%s", msg) }
func after(msg string) { log.Fatal(msg) }
The parameters of both functions are wildcards that may match any
expression assignable to that type. If the pattern contains multiple
occurrences of the same parameter, each must match the same expression
in the input for the pattern to match. If the replacement contains
multiple occurrences of the same parameter, the expression will be
duplicated, possibly changing the side-effects.
The tool analyses all Go code in the packages specified by the
arguments, replacing all occurrences of the pattern with the
substitution.
So, the transform above would change this input:
err := fmt.Errorf("%s", "error: " + msg)
to this output:
err := errors.New("error: " + msg)
Identifiers, including qualified identifiers (p.X) are considered to
match only if they denote the same object. This allows correct
matching even in the presence of dot imports, named imports and
locally shadowed package names in the input program.
Matching of type syntax is semantic, not syntactic: type syntax in the
pattern matches type syntax in the input if the types are identical.
Thus, func(x int) matches func(y int).
This tool was inspired by other example-based refactoring tools,
'gofmt -r' for Go and Refaster for Java.
LIMITATIONS
===========
EXPRESSIVENESS
Only refactorings that replace one expression with another, regardless
of the expression's context, may be expressed. Refactoring arbitrary
statements (or sequences of statements) is a less well-defined problem
and is less amenable to this approach.
A pattern that contains a function literal (and hence statements)
never matches.
There is no way to generalize over related types, e.g. to express that
a wildcard may have any integer type, for example.
It is not possible to replace an expression by one of a different
type, even in contexts where this is legal, such as x in fmt.Print(x).
The struct literals T{x} and T{K: x} cannot both be matched by a single
template.
SAFETY
Verifying that a transformation does not introduce type errors is very
complex in the general case. An innocuous-looking replacement of one
constant by another (e.g. 1 to 2) may cause type errors relating to
array types and indices, for example. The tool performs only very
superficial checks of type preservation.
IMPORTS
Although the matching algorithm is fully aware of scoping rules, the
replacement algorithm is not, so the replacement code may contain
incorrect identifier syntax for imported objects if there are dot
imports, named imports or locally shadowed package names in the input
program.
Imports are added as needed, but they are not removed as needed.
Run 'goimports' on the modified file for now.
Dot imports are forbidden in the template.
TIPS
====
Sometimes a little creativity is required to implement the desired
migration. This section lists a few tips and tricks.
To remove the final parameter from a function, temporarily change the
function signature so that the final parameter is variadic, as this
allows legal calls both with and without the argument. Then use eg to
remove the final argument from all callers, and remove the variadic
parameter by hand. The reverse process can be used to add a final
parameter.
To add or remove parameters other than the final one, you must do it in
stages: (1) declare a variant function f' with a different name and the
desired parameters; (2) use eg to transform calls to f into calls to f',
changing the arguments as needed; (3) change the declaration of f to
match f'; (4) use eg to rename f' to f in all calls; (5) delete f'.
`
// TODO(adonovan): expand upon the above documentation as an HTML page.
// A Transformer represents a single example-based transformation.
type Transformer struct {
fset *token.FileSet
verbose bool
info *types.Info // combined type info for template/input/output ASTs
seenInfos map[*types.Info]bool
wildcards map[*types.Var]bool // set of parameters in func before()
env map[string]ast.Expr // maps parameter name to wildcard binding
importedObjs map[types.Object]*ast.SelectorExpr // objects imported by after().
before, after ast.Expr
afterStmts []ast.Stmt
allowWildcards bool
// Working state of Transform():
nsubsts int // number of substitutions made
currentPkg *types.Package // package of current call
}
// NewTransformer returns a transformer based on the specified template,
// a single-file package containing "before" and "after" functions as
// described in the package documentation.
// tmplInfo is the type information for tmplFile.
//
func NewTransformer(fset *token.FileSet, tmplPkg *types.Package, tmplFile *ast.File, tmplInfo *types.Info, verbose bool) (*Transformer, error) {
// Check the template.
beforeSig := funcSig(tmplPkg, "before")
if beforeSig == nil {
return nil, fmt.Errorf("no 'before' func found in template")
}
afterSig := funcSig(tmplPkg, "after")
if afterSig == nil {
return nil, fmt.Errorf("no 'after' func found in template")
}
// TODO(adonovan): should we also check the names of the params match?
if !types.Identical(afterSig, beforeSig) {
return nil, fmt.Errorf("before %s and after %s functions have different signatures",
beforeSig, afterSig)
}
for _, imp := range tmplFile.Imports {
if imp.Name != nil && imp.Name.Name == "." {
// Dot imports are currently forbidden. We
// make the simplifying assumption that all
// imports are regular, without local renames.
return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
}
}
var beforeDecl, afterDecl *ast.FuncDecl
for _, decl := range tmplFile.Decls {
if decl, ok := decl.(*ast.FuncDecl); ok {
switch decl.Name.Name {
case "before":
beforeDecl = decl
case "after":
afterDecl = decl
}
}
}
before, err := soleExpr(beforeDecl)
if err != nil {
return nil, fmt.Errorf("before: %s", err)
}
afterStmts, after, err := stmtAndExpr(afterDecl)
if err != nil {
return nil, fmt.Errorf("after: %s", err)
}
wildcards := make(map[*types.Var]bool)
for i := 0; i < beforeSig.Params().Len(); i++ {
wildcards[beforeSig.Params().At(i)] = true
}
// checkExprTypes returns an error if Tb (type of before()) is not
// safe to replace with Ta (type of after()).
//
// Only superficial checks are performed, and they may result in both
// false positives and negatives.
//
// Ideally, we would only require that the replacement be assignable
// to the context of a specific pattern occurrence, but the type
// checker doesn't record that information and it's complex to deduce.
// A Go type cannot capture all the constraints of a given expression
// context, which may include the size, constness, signedness,
// namedness or constructor of its type, and even the specific value
// of the replacement. (Consider the rule that array literal keys
// must be unique.) So we cannot hope to prove the safety of a
// transformation in general.
Tb := tmplInfo.TypeOf(before)
Ta := tmplInfo.TypeOf(after)
if types.AssignableTo(Tb, Ta) {
// safe: replacement is assignable to pattern.
} else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 {
// safe: pattern has void type (must appear in an ExprStmt).
} else {
return nil, fmt.Errorf("%s is not a safe replacement for %s", Ta, Tb)
}
tr := &Transformer{
fset: fset,
verbose: verbose,
wildcards: wildcards,
allowWildcards: true,
seenInfos: make(map[*types.Info]bool),
importedObjs: make(map[types.Object]*ast.SelectorExpr),
before: before,
after: after,
afterStmts: afterStmts,
}
// Combine type info from the template and input packages, and
// type info for the synthesized ASTs too. This saves us
// having to book-keep where each ast.Node originated as we
// construct the resulting hybrid AST.
tr.info = &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
mergeTypeInfo(tr.info, tmplInfo)
// Compute set of imported objects required by after().
// TODO(adonovan): reject dot-imports in pattern
ast.Inspect(after, func(n ast.Node) bool {
if n, ok := n.(*ast.SelectorExpr); ok {
if _, ok := tr.info.Selections[n]; !ok {
// qualified ident
obj := tr.info.Uses[n.Sel]
tr.importedObjs[obj] = n
return false // prune
}
}
return true // recur
})
return tr, nil
}
// WriteAST is a convenience function that writes AST f to the specified file.
func WriteAST(fset *token.FileSet, filename string, f *ast.File) (err error) {
fh, err := os.Create(filename)
if err != nil {
return err
}
defer func() {
if err2 := fh.Close(); err != nil {
err = err2 // prefer earlier error
}
}()
return format.Node(fh, fset, f)
}
// -- utilities --------------------------------------------------------
// funcSig returns the signature of the specified package-level function.
func funcSig(pkg *types.Package, name string) *types.Signature {
if f, ok := pkg.Scope().Lookup(name).(*types.Func); ok {
return f.Type().(*types.Signature)
}
return nil
}
// soleExpr returns the sole expression in the before/after template function.
func soleExpr(fn *ast.FuncDecl) (ast.Expr, error) {
if fn.Body == nil {
return nil, fmt.Errorf("no body")
}
if len(fn.Body.List) != 1 {
return nil, fmt.Errorf("must contain a single statement")
}
switch stmt := fn.Body.List[0].(type) {
case *ast.ReturnStmt:
if len(stmt.Results) != 1 {
return nil, fmt.Errorf("return statement must have a single operand")
}
return stmt.Results[0], nil
case *ast.ExprStmt:
return stmt.X, nil
}
return nil, fmt.Errorf("must contain a single return or expression statement")
}
// stmtAndExpr returns the expression in the last return statement as well as the preceeding lines.
func stmtAndExpr(fn *ast.FuncDecl) ([]ast.Stmt, ast.Expr, error) {
if fn.Body == nil {
return nil, nil, fmt.Errorf("no body")
}
n := len(fn.Body.List)
if n == 0 {
return nil, nil, fmt.Errorf("must contain at least one statement")
}
stmts, last := fn.Body.List[:n-1], fn.Body.List[n-1]
switch last := last.(type) {
case *ast.ReturnStmt:
if len(last.Results) != 1 {
return nil, nil, fmt.Errorf("return statement must have a single operand")
}
return stmts, last.Results[0], nil
case *ast.ExprStmt:
return stmts, last.X, nil
}
return nil, nil, fmt.Errorf("must end with a single return or expression statement")
}
// mergeTypeInfo adds type info from src to dst.
func mergeTypeInfo(dst, src *types.Info) {
for k, v := range src.Types {
dst.Types[k] = v
}
for k, v := range src.Defs {
dst.Defs[k] = v
}
for k, v := range src.Uses {
dst.Uses[k] = v
}
for k, v := range src.Selections {
dst.Selections[k] = v
}
}
// (debugging only)
func astString(fset *token.FileSet, n ast.Node) string {
var buf bytes.Buffer
printer.Fprint(&buf, fset, n)
return buf.String()
}

166
vendor/golang.org/x/tools/refactor/eg/eg_test.go generated vendored Normal file
View File

@@ -0,0 +1,166 @@
// 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.
// No testdata on Android.
// +build !android
package eg_test
import (
"bytes"
"flag"
exact "go/constant"
"go/parser"
"go/token"
"go/types"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/refactor/eg"
)
// TODO(adonovan): more tests:
// - of command-line tool
// - of all parts of syntax
// - of applying a template to a package it imports:
// the replacement syntax should use unqualified names for its objects.
var (
updateFlag = flag.Bool("update", false, "update the golden files")
verboseFlag = flag.Bool("verbose", false, "show matcher information")
)
func Test(t *testing.T) {
switch runtime.GOOS {
case "windows":
t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
}
conf := loader.Config{
Fset: token.NewFileSet(),
ParserMode: parser.ParseComments,
}
// Each entry is a single-file package.
// (Multi-file packages aren't interesting for this test.)
// Order matters: each non-template package is processed using
// the preceding template package.
for _, filename := range []string{
"testdata/A.template",
"testdata/A1.go",
"testdata/A2.go",
"testdata/B.template",
"testdata/B1.go",
"testdata/C.template",
"testdata/C1.go",
"testdata/D.template",
"testdata/D1.go",
"testdata/E.template",
"testdata/E1.go",
"testdata/F.template",
"testdata/F1.go",
"testdata/G.template",
"testdata/G1.go",
"testdata/H.template",
"testdata/H1.go",
"testdata/I.template",
"testdata/I1.go",
"testdata/J.template",
"testdata/J1.go",
"testdata/bad_type.template",
"testdata/no_before.template",
"testdata/no_after_return.template",
"testdata/type_mismatch.template",
"testdata/expr_type_mismatch.template",
} {
pkgname := strings.TrimSuffix(filepath.Base(filename), ".go")
conf.CreateFromFilenames(pkgname, filename)
}
iprog, err := conf.Load()
if err != nil {
t.Fatal(err)
}
var xform *eg.Transformer
for _, info := range iprog.Created {
file := info.Files[0]
filename := iprog.Fset.File(file.Pos()).Name() // foo.go
if strings.HasSuffix(filename, "template") {
// a new template
shouldFail, _ := info.Pkg.Scope().Lookup("shouldFail").(*types.Const)
xform, err = eg.NewTransformer(iprog.Fset, info.Pkg, file, &info.Info, *verboseFlag)
if err != nil {
if shouldFail == nil {
t.Errorf("NewTransformer(%s): %s", filename, err)
} else if want := exact.StringVal(shouldFail.Val()); !strings.Contains(err.Error(), want) {
t.Errorf("NewTransformer(%s): got error %q, want error %q", filename, err, want)
}
} else if shouldFail != nil {
t.Errorf("NewTransformer(%s) succeeded unexpectedly; want error %q",
filename, shouldFail.Val())
}
continue
}
if xform == nil {
t.Errorf("%s: no previous template", filename)
continue
}
// apply previous template to this package
n := xform.Transform(&info.Info, info.Pkg, file)
if n == 0 {
t.Errorf("%s: no matches", filename)
continue
}
got := filename + "t" // foo.got
golden := filename + "lden" // foo.golden
// Write actual output to foo.got.
if err := eg.WriteAST(iprog.Fset, got, file); err != nil {
t.Error(err)
}
defer os.Remove(got)
// Compare foo.got with foo.golden.
var cmd *exec.Cmd
switch runtime.GOOS {
case "plan9":
cmd = exec.Command("/bin/diff", "-c", golden, got)
default:
cmd = exec.Command("/usr/bin/diff", "-u", golden, got)
}
buf := new(bytes.Buffer)
cmd.Stdout = buf
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
t.Errorf("eg tests for %s failed: %s.\n%s\n", filename, err, buf)
if *updateFlag {
t.Logf("Updating %s...", golden)
if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
t.Errorf("Update failed: %s", err)
}
}
}
}
}

249
vendor/golang.org/x/tools/refactor/eg/match.go generated vendored Normal file
View File

@@ -0,0 +1,249 @@
// 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 eg
import (
"fmt"
"go/ast"
exact "go/constant"
"go/token"
"go/types"
"log"
"os"
"reflect"
"golang.org/x/tools/go/ast/astutil"
)
// matchExpr reports whether pattern x matches y.
//
// If tr.allowWildcards, Idents in x that refer to parameters are
// treated as wildcards, and match any y that is assignable to the
// parameter type; matchExpr records this correspondence in tr.env.
// Otherwise, matchExpr simply reports whether the two trees are
// equivalent.
//
// A wildcard appearing more than once in the pattern must
// consistently match the same tree.
//
func (tr *Transformer) matchExpr(x, y ast.Expr) bool {
if x == nil && y == nil {
return true
}
if x == nil || y == nil {
return false
}
x = unparen(x)
y = unparen(y)
// Is x a wildcard? (a reference to a 'before' parameter)
if xobj, ok := tr.wildcardObj(x); ok {
return tr.matchWildcard(xobj, y)
}
// Object identifiers (including pkg-qualified ones)
// are handled semantically, not syntactically.
xobj := isRef(x, tr.info)
yobj := isRef(y, tr.info)
if xobj != nil {
return xobj == yobj
}
if yobj != nil {
return false
}
// TODO(adonovan): audit: we cannot assume these ast.Exprs
// contain non-nil pointers. e.g. ImportSpec.Name may be a
// nil *ast.Ident.
if reflect.TypeOf(x) != reflect.TypeOf(y) {
return false
}
switch x := x.(type) {
case *ast.Ident:
log.Fatalf("unexpected Ident: %s", astString(tr.fset, x))
case *ast.BasicLit:
y := y.(*ast.BasicLit)
xval := exact.MakeFromLiteral(x.Value, x.Kind, 0)
yval := exact.MakeFromLiteral(y.Value, y.Kind, 0)
return exact.Compare(xval, token.EQL, yval)
case *ast.FuncLit:
// func literals (and thus statement syntax) never match.
return false
case *ast.CompositeLit:
y := y.(*ast.CompositeLit)
return (x.Type == nil) == (y.Type == nil) &&
(x.Type == nil || tr.matchType(x.Type, y.Type)) &&
tr.matchExprs(x.Elts, y.Elts)
case *ast.SelectorExpr:
y := y.(*ast.SelectorExpr)
return tr.matchSelectorExpr(x, y) &&
tr.info.Selections[x].Obj() == tr.info.Selections[y].Obj()
case *ast.IndexExpr:
y := y.(*ast.IndexExpr)
return tr.matchExpr(x.X, y.X) &&
tr.matchExpr(x.Index, y.Index)
case *ast.SliceExpr:
y := y.(*ast.SliceExpr)
return tr.matchExpr(x.X, y.X) &&
tr.matchExpr(x.Low, y.Low) &&
tr.matchExpr(x.High, y.High) &&
tr.matchExpr(x.Max, y.Max) &&
x.Slice3 == y.Slice3
case *ast.TypeAssertExpr:
y := y.(*ast.TypeAssertExpr)
return tr.matchExpr(x.X, y.X) &&
tr.matchType(x.Type, y.Type)
case *ast.CallExpr:
y := y.(*ast.CallExpr)
match := tr.matchExpr // function call
if tr.info.Types[x.Fun].IsType() {
match = tr.matchType // type conversion
}
return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() &&
match(x.Fun, y.Fun) &&
tr.matchExprs(x.Args, y.Args)
case *ast.StarExpr:
y := y.(*ast.StarExpr)
return tr.matchExpr(x.X, y.X)
case *ast.UnaryExpr:
y := y.(*ast.UnaryExpr)
return x.Op == y.Op &&
tr.matchExpr(x.X, y.X)
case *ast.BinaryExpr:
y := y.(*ast.BinaryExpr)
return x.Op == y.Op &&
tr.matchExpr(x.X, y.X) &&
tr.matchExpr(x.Y, y.Y)
case *ast.KeyValueExpr:
y := y.(*ast.KeyValueExpr)
return tr.matchExpr(x.Key, y.Key) &&
tr.matchExpr(x.Value, y.Value)
}
panic(fmt.Sprintf("unhandled AST node type: %T", x))
}
func (tr *Transformer) matchExprs(xx, yy []ast.Expr) bool {
if len(xx) != len(yy) {
return false
}
for i := range xx {
if !tr.matchExpr(xx[i], yy[i]) {
return false
}
}
return true
}
// matchType reports whether the two type ASTs denote identical types.
func (tr *Transformer) matchType(x, y ast.Expr) bool {
tx := tr.info.Types[x].Type
ty := tr.info.Types[y].Type
return types.Identical(tx, ty)
}
func (tr *Transformer) wildcardObj(x ast.Expr) (*types.Var, bool) {
if x, ok := x.(*ast.Ident); ok && x != nil && tr.allowWildcards {
if xobj, ok := tr.info.Uses[x].(*types.Var); ok && tr.wildcards[xobj] {
return xobj, true
}
}
return nil, false
}
func (tr *Transformer) matchSelectorExpr(x, y *ast.SelectorExpr) bool {
if xobj, ok := tr.wildcardObj(x.X); ok {
field := x.Sel.Name
yt := tr.info.TypeOf(y.X)
o, _, _ := types.LookupFieldOrMethod(yt, true, tr.currentPkg, field)
if o != nil {
tr.env[xobj.Name()] = y.X // record binding
return true
}
}
return tr.matchExpr(x.X, y.X)
}
func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool {
name := xobj.Name()
if tr.verbose {
fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ",
tr.fset.Position(y.Pos()), name, astString(tr.fset, y))
}
// Check that y is assignable to the declared type of the param.
yt := tr.info.TypeOf(y)
if yt == nil {
// y has no type.
// Perhaps it is an *ast.Ellipsis in [...]T{}, or
// an *ast.KeyValueExpr in T{k: v}.
// Clearly these pseudo-expressions cannot match a
// wildcard, but it would nice if we had a way to ignore
// the difference between T{v} and T{k:v} for structs.
return false
}
if !types.AssignableTo(yt, xobj.Type()) {
if tr.verbose {
fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type())
}
return false
}
// A wildcard matches any expression.
// If it appears multiple times in the pattern, it must match
// the same expression each time.
if old, ok := tr.env[name]; ok {
// found existing binding
tr.allowWildcards = false
r := tr.matchExpr(old, y)
if tr.verbose {
fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n",
r, astString(tr.fset, old))
}
tr.allowWildcards = true
return r
}
if tr.verbose {
fmt.Fprintf(os.Stderr, "primary match\n")
}
tr.env[name] = y // record binding
return true
}
// -- utilities --------------------------------------------------------
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
// isRef returns the object referred to by this (possibly qualified)
// identifier, or nil if the node is not a referring identifier.
func isRef(n ast.Node, info *types.Info) types.Object {
switch n := n.(type) {
case *ast.Ident:
return info.Uses[n]
case *ast.SelectorExpr:
if _, ok := info.Selections[n]; !ok {
// qualified ident
return info.Uses[n.Sel]
}
}
return nil
}

401
vendor/golang.org/x/tools/refactor/eg/rewrite.go generated vendored Normal file
View File

@@ -0,0 +1,401 @@
// 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 eg
// This file defines the AST rewriting pass.
// Most of it was plundered directly from
// $GOROOT/src/cmd/gofmt/rewrite.go (after convergent evolution).
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"os"
"reflect"
"sort"
"strconv"
"strings"
"golang.org/x/tools/go/ast/astutil"
)
// transformItem takes a reflect.Value representing a variable of type ast.Node
// transforms its child elements recursively with apply, and then transforms the
// actual element if it contains an expression.
func (tr *Transformer) transformItem(rv reflect.Value) (reflect.Value, bool, map[string]ast.Expr) {
// don't bother if val is invalid to start with
if !rv.IsValid() {
return reflect.Value{}, false, nil
}
rv, changed, newEnv := tr.apply(tr.transformItem, rv)
e := rvToExpr(rv)
if e == nil {
return rv, changed, newEnv
}
savedEnv := tr.env
tr.env = make(map[string]ast.Expr) // inefficient! Use a slice of k/v pairs
if tr.matchExpr(tr.before, e) {
if tr.verbose {
fmt.Fprintf(os.Stderr, "%s matches %s",
astString(tr.fset, tr.before), astString(tr.fset, e))
if len(tr.env) > 0 {
fmt.Fprintf(os.Stderr, " with:")
for name, ast := range tr.env {
fmt.Fprintf(os.Stderr, " %s->%s",
name, astString(tr.fset, ast))
}
}
fmt.Fprintf(os.Stderr, "\n")
}
tr.nsubsts++
// Clone the replacement tree, performing parameter substitution.
// We update all positions to n.Pos() to aid comment placement.
rv = tr.subst(tr.env, reflect.ValueOf(tr.after),
reflect.ValueOf(e.Pos()))
changed = true
newEnv = tr.env
}
tr.env = savedEnv
return rv, changed, newEnv
}
// Transform applies the transformation to the specified parsed file,
// whose type information is supplied in info, and returns the number
// of replacements that were made.
//
// It mutates the AST in place (the identity of the root node is
// unchanged), and may add nodes for which no type information is
// available in info.
//
// Derived from rewriteFile in $GOROOT/src/cmd/gofmt/rewrite.go.
//
func (tr *Transformer) Transform(info *types.Info, pkg *types.Package, file *ast.File) int {
if !tr.seenInfos[info] {
tr.seenInfos[info] = true
mergeTypeInfo(tr.info, info)
}
tr.currentPkg = pkg
tr.nsubsts = 0
if tr.verbose {
fmt.Fprintf(os.Stderr, "before: %s\n", astString(tr.fset, tr.before))
fmt.Fprintf(os.Stderr, "after: %s\n", astString(tr.fset, tr.after))
fmt.Fprintf(os.Stderr, "afterStmts: %s\n", tr.afterStmts)
}
o, changed, _ := tr.apply(tr.transformItem, reflect.ValueOf(file))
if changed {
panic("BUG")
}
file2 := o.Interface().(*ast.File)
// By construction, the root node is unchanged.
if file != file2 {
panic("BUG")
}
// Add any necessary imports.
// TODO(adonovan): remove no-longer needed imports too.
if tr.nsubsts > 0 {
pkgs := make(map[string]*types.Package)
for obj := range tr.importedObjs {
pkgs[obj.Pkg().Path()] = obj.Pkg()
}
for _, imp := range file.Imports {
path, _ := strconv.Unquote(imp.Path.Value)
delete(pkgs, path)
}
delete(pkgs, pkg.Path()) // don't import self
// NB: AddImport may completely replace the AST!
// It thus renders info and tr.info no longer relevant to file.
var paths []string
for path := range pkgs {
paths = append(paths, path)
}
sort.Strings(paths)
for _, path := range paths {
astutil.AddImport(tr.fset, file, path)
}
}
tr.currentPkg = nil
return tr.nsubsts
}
// setValue is a wrapper for x.SetValue(y); it protects
// the caller from panics if x cannot be changed to y.
func setValue(x, y reflect.Value) {
// don't bother if y is invalid to start with
if !y.IsValid() {
return
}
defer func() {
if x := recover(); x != nil {
if s, ok := x.(string); ok &&
(strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) {
// x cannot be set to y - ignore this rewrite
return
}
panic(x)
}
}()
x.Set(y)
}
// Values/types for special cases.
var (
objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
scopePtrNil = reflect.ValueOf((*ast.Scope)(nil))
identType = reflect.TypeOf((*ast.Ident)(nil))
selectorExprType = reflect.TypeOf((*ast.SelectorExpr)(nil))
objectPtrType = reflect.TypeOf((*ast.Object)(nil))
statementType = reflect.TypeOf((*ast.Stmt)(nil)).Elem()
positionType = reflect.TypeOf(token.NoPos)
scopePtrType = reflect.TypeOf((*ast.Scope)(nil))
)
// apply replaces each AST field x in val with f(x), returning val.
// To avoid extra conversions, f operates on the reflect.Value form.
// f takes a reflect.Value representing the variable to modify of type ast.Node.
// It returns a reflect.Value containing the transformed value of type ast.Node,
// whether any change was made, and a map of identifiers to ast.Expr (so we can
// do contextually correct substitutions in the parent statements).
func (tr *Transformer) apply(f func(reflect.Value) (reflect.Value, bool, map[string]ast.Expr), val reflect.Value) (reflect.Value, bool, map[string]ast.Expr) {
if !val.IsValid() {
return reflect.Value{}, false, nil
}
// *ast.Objects introduce cycles and are likely incorrect after
// rewrite; don't follow them but replace with nil instead
if val.Type() == objectPtrType {
return objectPtrNil, false, nil
}
// similarly for scopes: they are likely incorrect after a rewrite;
// replace them with nil
if val.Type() == scopePtrType {
return scopePtrNil, false, nil
}
switch v := reflect.Indirect(val); v.Kind() {
case reflect.Slice:
// no possible rewriting of statements.
if v.Type().Elem() != statementType {
changed := false
var envp map[string]ast.Expr
for i := 0; i < v.Len(); i++ {
e := v.Index(i)
o, localchanged, env := f(e)
if localchanged {
changed = true
// we clobber envp here,
// which means if we have two sucessive
// replacements inside the same statement
// we will only generate the setup for one of them.
envp = env
}
setValue(e, o)
}
return val, changed, envp
}
// statements are rewritten.
var out []ast.Stmt
for i := 0; i < v.Len(); i++ {
e := v.Index(i)
o, changed, env := f(e)
if changed {
for _, s := range tr.afterStmts {
t := tr.subst(env, reflect.ValueOf(s), reflect.Value{}).Interface()
out = append(out, t.(ast.Stmt))
}
}
setValue(e, o)
out = append(out, e.Interface().(ast.Stmt))
}
return reflect.ValueOf(out), false, nil
case reflect.Struct:
changed := false
var envp map[string]ast.Expr
for i := 0; i < v.NumField(); i++ {
e := v.Field(i)
o, localchanged, env := f(e)
if localchanged {
changed = true
envp = env
}
setValue(e, o)
}
return val, changed, envp
case reflect.Interface:
e := v.Elem()
o, changed, env := f(e)
setValue(v, o)
return val, changed, env
}
return val, false, nil
}
// subst returns a copy of (replacement) pattern with values from env
// substituted in place of wildcards and pos used as the position of
// tokens from the pattern. if env == nil, subst returns a copy of
// pattern and doesn't change the line number information.
func (tr *Transformer) subst(env map[string]ast.Expr, pattern, pos reflect.Value) reflect.Value {
if !pattern.IsValid() {
return reflect.Value{}
}
// *ast.Objects introduce cycles and are likely incorrect after
// rewrite; don't follow them but replace with nil instead
if pattern.Type() == objectPtrType {
return objectPtrNil
}
// similarly for scopes: they are likely incorrect after a rewrite;
// replace them with nil
if pattern.Type() == scopePtrType {
return scopePtrNil
}
// Wildcard gets replaced with map value.
if env != nil && pattern.Type() == identType {
id := pattern.Interface().(*ast.Ident)
if old, ok := env[id.Name]; ok {
return tr.subst(nil, reflect.ValueOf(old), reflect.Value{})
}
}
// Emit qualified identifiers in the pattern by appropriate
// (possibly qualified) identifier in the input.
//
// The template cannot contain dot imports, so all identifiers
// for imported objects are explicitly qualified.
//
// We assume (unsoundly) that there are no dot or named
// imports in the input code, nor are any imported package
// names shadowed, so the usual normal qualified identifier
// syntax may be used.
// TODO(adonovan): fix: avoid this assumption.
//
// A refactoring may be applied to a package referenced by the
// template. Objects belonging to the current package are
// denoted by unqualified identifiers.
//
if tr.importedObjs != nil && pattern.Type() == selectorExprType {
obj := isRef(pattern.Interface().(*ast.SelectorExpr), tr.info)
if obj != nil {
if sel, ok := tr.importedObjs[obj]; ok {
var id ast.Expr
if obj.Pkg() == tr.currentPkg {
id = sel.Sel // unqualified
} else {
id = sel // pkg-qualified
}
// Return a clone of id.
saved := tr.importedObjs
tr.importedObjs = nil // break cycle
r := tr.subst(nil, reflect.ValueOf(id), pos)
tr.importedObjs = saved
return r
}
}
}
if pos.IsValid() && pattern.Type() == positionType {
// use new position only if old position was valid in the first place
if old := pattern.Interface().(token.Pos); !old.IsValid() {
return pattern
}
return pos
}
// Otherwise copy.
switch p := pattern; p.Kind() {
case reflect.Slice:
v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
for i := 0; i < p.Len(); i++ {
v.Index(i).Set(tr.subst(env, p.Index(i), pos))
}
return v
case reflect.Struct:
v := reflect.New(p.Type()).Elem()
for i := 0; i < p.NumField(); i++ {
v.Field(i).Set(tr.subst(env, p.Field(i), pos))
}
return v
case reflect.Ptr:
v := reflect.New(p.Type()).Elem()
if elem := p.Elem(); elem.IsValid() {
v.Set(tr.subst(env, elem, pos).Addr())
}
// Duplicate type information for duplicated ast.Expr.
// All ast.Node implementations are *structs,
// so this case catches them all.
if e := rvToExpr(v); e != nil {
updateTypeInfo(tr.info, e, p.Interface().(ast.Expr))
}
return v
case reflect.Interface:
v := reflect.New(p.Type()).Elem()
if elem := p.Elem(); elem.IsValid() {
v.Set(tr.subst(env, elem, pos))
}
return v
}
return pattern
}
// -- utilities -------------------------------------------------------
func rvToExpr(rv reflect.Value) ast.Expr {
if rv.CanInterface() {
if e, ok := rv.Interface().(ast.Expr); ok {
return e
}
}
return nil
}
// updateTypeInfo duplicates type information for the existing AST old
// so that it also applies to duplicated AST new.
func updateTypeInfo(info *types.Info, new, old ast.Expr) {
switch new := new.(type) {
case *ast.Ident:
orig := old.(*ast.Ident)
if obj, ok := info.Defs[orig]; ok {
info.Defs[new] = obj
}
if obj, ok := info.Uses[orig]; ok {
info.Uses[new] = obj
}
case *ast.SelectorExpr:
orig := old.(*ast.SelectorExpr)
if sel, ok := info.Selections[orig]; ok {
info.Selections[new] = sel
}
}
if tv, ok := info.Types[old]; ok {
info.Types[new] = tv
}
}

View File

@@ -0,0 +1,13 @@
// +build ignore
package template
// Basic test of type-aware expression refactoring.
import (
"errors"
"fmt"
)
func before(s string) error { return fmt.Errorf("%s", s) }
func after(s string) error { return errors.New(s) }

51
vendor/golang.org/x/tools/refactor/eg/testdata/A1.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
// +build ignore
package A1
import (
. "fmt"
myfmt "fmt"
"os"
"strings"
)
func example(n int) {
x := "foo" + strings.Repeat("\t", n)
// Match, despite named import.
myfmt.Errorf("%s", x)
// Match, despite dot import.
Errorf("%s", x)
// Match: multiple matches in same function are possible.
myfmt.Errorf("%s", x)
// No match: wildcarded operand has the wrong type.
myfmt.Errorf("%s", 3)
// No match: function operand doesn't match.
myfmt.Printf("%s", x)
// No match again, dot import.
Printf("%s", x)
// Match.
myfmt.Fprint(os.Stderr, myfmt.Errorf("%s", x+"foo"))
// No match: though this literally matches the template,
// fmt doesn't resolve to a package here.
var fmt struct{ Errorf func(string, string) }
fmt.Errorf("%s", x)
// Recursive matching:
// Match: both matches are well-typed, so both succeed.
myfmt.Errorf("%s", myfmt.Errorf("%s", x+"foo").Error())
// Outer match succeeds, inner doesn't: 3 has wrong type.
myfmt.Errorf("%s", myfmt.Errorf("%s", 3).Error())
// Inner match succeeds, outer doesn't: the inner replacement
// has the wrong type (error not string).
myfmt.Errorf("%s", myfmt.Errorf("%s", x+"foo"))
}

View File

@@ -0,0 +1,52 @@
// +build ignore
package A1
import (
"errors"
. "fmt"
myfmt "fmt"
"os"
"strings"
)
func example(n int) {
x := "foo" + strings.Repeat("\t", n)
// Match, despite named import.
errors.New(x)
// Match, despite dot import.
errors.New(x)
// Match: multiple matches in same function are possible.
errors.New(x)
// No match: wildcarded operand has the wrong type.
myfmt.Errorf("%s", 3)
// No match: function operand doesn't match.
myfmt.Printf("%s", x)
// No match again, dot import.
Printf("%s", x)
// Match.
myfmt.Fprint(os.Stderr, errors.New(x+"foo"))
// No match: though this literally matches the template,
// fmt doesn't resolve to a package here.
var fmt struct{ Errorf func(string, string) }
fmt.Errorf("%s", x)
// Recursive matching:
// Match: both matches are well-typed, so both succeed.
errors.New(errors.New(x + "foo").Error())
// Outer match succeeds, inner doesn't: 3 has wrong type.
errors.New(myfmt.Errorf("%s", 3).Error())
// Inner match succeeds, outer doesn't: the inner replacement
// has the wrong type (error not string).
myfmt.Errorf("%s", errors.New(x+"foo"))
}

12
vendor/golang.org/x/tools/refactor/eg/testdata/A2.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// +build ignore
package A2
// This refactoring causes addition of "errors" import.
// TODO(adonovan): fix: it should also remove "fmt".
import myfmt "fmt"
func example(n int) {
myfmt.Errorf("%s", "")
}

View File

@@ -0,0 +1,15 @@
// +build ignore
package A2
// This refactoring causes addition of "errors" import.
// TODO(adonovan): fix: it should also remove "fmt".
import (
"errors"
myfmt "fmt"
)
func example(n int) {
errors.New("")
}

View File

@@ -0,0 +1,9 @@
package template
// Basic test of expression refactoring.
// (Types are not important in this case; it could be done with gofmt -r.)
import "time"
func before(t time.Time) time.Duration { return time.Now().Sub(t) }
func after(t time.Time) time.Duration { return time.Since(t) }

17
vendor/golang.org/x/tools/refactor/eg/testdata/B1.go generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// +build ignore
package B1
import "time"
var startup = time.Now()
func example() time.Duration {
before := time.Now()
time.Sleep(1)
return time.Now().Sub(before)
}
func msSinceStartup() int64 {
return int64(time.Now().Sub(startup) / time.Millisecond)
}

View File

@@ -0,0 +1,17 @@
// +build ignore
package B1
import "time"
var startup = time.Now()
func example() time.Duration {
before := time.Now()
time.Sleep(1)
return time.Since(before)
}
func msSinceStartup() int64 {
return int64(time.Since(startup) / time.Millisecond)
}

View File

@@ -0,0 +1,10 @@
package template
// Test of repeated use of wildcard in pattern.
// NB: multiple patterns would be required to handle variants such as
// s[:len(s)], s[x:len(s)], etc, since a wildcard can't match nothing at all.
// TODO(adonovan): support multiple templates in a single pass.
func before(s string) string { return s[:len(s)] }
func after(s string) string { return s }

22
vendor/golang.org/x/tools/refactor/eg/testdata/C1.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// +build ignore
package C1
import "strings"
func example() {
x := "foo"
println(x[:len(x)])
// Match, but the transformation is not sound w.r.t. possible side effects.
println(strings.Repeat("*", 3)[:len(strings.Repeat("*", 3))])
// No match, since second use of wildcard doesn't match first.
println(strings.Repeat("*", 3)[:len(strings.Repeat("*", 2))])
// Recursive match demonstrating bottom-up rewrite:
// only after the inner replacement occurs does the outer syntax match.
println((x[:len(x)])[:len(x[:len(x)])])
// -> (x[:len(x)])
// -> x
}

View File

@@ -0,0 +1,22 @@
// +build ignore
package C1
import "strings"
func example() {
x := "foo"
println(x)
// Match, but the transformation is not sound w.r.t. possible side effects.
println(strings.Repeat("*", 3))
// No match, since second use of wildcard doesn't match first.
println(strings.Repeat("*", 3)[:len(strings.Repeat("*", 2))])
// Recursive match demonstrating bottom-up rewrite:
// only after the inner replacement occurs does the outer syntax match.
println(x)
// -> (x[:len(x)])
// -> x
}

View File

@@ -0,0 +1,8 @@
package template
import "fmt"
// Test of semantic (not syntactic) matching of basic literals.
func before() (int, error) { return fmt.Println(123, "a") }
func after() (int, error) { return fmt.Println(456, "!") }

12
vendor/golang.org/x/tools/refactor/eg/testdata/D1.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// +build ignore
package D1
import "fmt"
func example() {
fmt.Println(123, "a") // match
fmt.Println(0x7b, `a`) // match
fmt.Println(0173, "\x61") // match
fmt.Println(100+20+3, "a"+"") // no match: constant expressions, but not basic literals
}

View File

@@ -0,0 +1,12 @@
// +build ignore
package D1
import "fmt"
func example() {
fmt.Println(456, "!") // match
fmt.Println(456, "!") // match
fmt.Println(456, "!") // match
fmt.Println(100+20+3, "a"+"") // no match: constant expressions, but not basic literals
}

View File

@@ -0,0 +1,12 @@
package template
import (
"fmt"
"log"
"os"
)
// Replace call to void function by call to non-void function.
func before(x interface{}) { log.Fatal(x) }
func after(x interface{}) { fmt.Fprintf(os.Stderr, "warning: %v", x) }

9
vendor/golang.org/x/tools/refactor/eg/testdata/E1.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// +build ignore
package E1
import "log"
func example() {
log.Fatal("oops") // match
}

View File

@@ -0,0 +1,13 @@
// +build ignore
package E1
import (
"fmt"
"log"
"os"
)
func example() {
fmt.Fprintf(os.Stderr, "warning: %v", "oops") // match
}

View File

@@ -0,0 +1,8 @@
package templates
// Test
import "sync"
func before(s sync.RWMutex) { s.Lock() }
func after(s sync.RWMutex) { s.RLock() }

48
vendor/golang.org/x/tools/refactor/eg/testdata/F1.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
// +build ignore
package F1
import "sync"
func example(n int) {
var x struct {
mutex sync.RWMutex
}
var y struct {
sync.RWMutex
}
type l struct {
sync.RWMutex
}
var z struct {
l
}
var a struct {
*l
}
var b struct{ Lock func() }
// Match
x.mutex.Lock()
// Match
y.Lock()
// Match indirect
z.Lock()
// Should be no match however currently matches due to:
// https://golang.org/issue/8584
// Will start failing when this is fixed then just change golden to
// No match pointer indirect
// a.Lock()
a.Lock()
// No match
b.Lock()
}

View File

@@ -0,0 +1,48 @@
// +build ignore
package F1
import "sync"
func example(n int) {
var x struct {
mutex sync.RWMutex
}
var y struct {
sync.RWMutex
}
type l struct {
sync.RWMutex
}
var z struct {
l
}
var a struct {
*l
}
var b struct{ Lock func() }
// Match
x.mutex.RLock()
// Match
y.RLock()
// Match indirect
z.RLock()
// Should be no match however currently matches due to:
// https://golang.org/issue/8584
// Will start failing when this is fixed then just change golden to
// No match pointer indirect
// a.Lock()
a.RLock()
// No match
b.Lock()
}

View File

@@ -0,0 +1,10 @@
package templates
import (
"go/ast" // defines many unencapsulated structs
"go/token"
)
func before(from, to token.Pos) ast.BadExpr { return ast.BadExpr{From: from, To: to} }
func after(from, to token.Pos) ast.BadExpr { return ast.BadExpr{from, to} }

12
vendor/golang.org/x/tools/refactor/eg/testdata/G1.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// +build ignore
package G1
import "go/ast"
func example() {
_ = ast.BadExpr{From: 123, To: 456} // match
_ = ast.BadExpr{123, 456} // no match
_ = ast.BadExpr{From: 123} // no match
_ = ast.BadExpr{To: 456} // no match
}

View File

@@ -0,0 +1,12 @@
// +build ignore
package G1
import "go/ast"
func example() {
_ = ast.BadExpr{123, 456} // match
_ = ast.BadExpr{123, 456} // no match
_ = ast.BadExpr{From: 123} // no match
_ = ast.BadExpr{To: 456} // no match
}

View File

@@ -0,0 +1,9 @@
package templates
import (
"go/ast" // defines many unencapsulated structs
"go/token"
)
func before(from, to token.Pos) ast.BadExpr { return ast.BadExpr{from, to} }
func after(from, to token.Pos) ast.BadExpr { return ast.BadExpr{From: from, To: to} }

12
vendor/golang.org/x/tools/refactor/eg/testdata/H1.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// +build ignore
package H1
import "go/ast"
func example() {
_ = ast.BadExpr{From: 123, To: 456} // no match
_ = ast.BadExpr{123, 456} // match
_ = ast.BadExpr{From: 123} // no match
_ = ast.BadExpr{To: 456} // no match
}

View File

@@ -0,0 +1,12 @@
// +build ignore
package H1
import "go/ast"
func example() {
_ = ast.BadExpr{From: 123, To: 456} // no match
_ = ast.BadExpr{From: 123, To: 456} // match
_ = ast.BadExpr{From: 123} // no match
_ = ast.BadExpr{To: 456} // no match
}

View File

@@ -0,0 +1,14 @@
// +build ignore
package templates
import (
"errors"
"fmt"
)
func before(s string) error { return fmt.Errorf("%s", s) }
func after(s string) error {
n := fmt.Sprintf("error - %s", s)
return errors.New(n)
}

9
vendor/golang.org/x/tools/refactor/eg/testdata/I1.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// +build ignore
package I1
import "fmt"
func example() {
_ = fmt.Errorf("%s", "foo")
}

View File

@@ -0,0 +1,14 @@
// +build ignore
package I1
import (
"errors"
"fmt"
)
func example() {
n := fmt.Sprintf("error - %s", "foo")
_ = errors.New(n)
}

View File

@@ -0,0 +1,11 @@
// +build ignore
package templates
import ()
func before(x int) int { return x + x + x }
func after(x int) int {
temp := x + x
return temp + x
}

10
vendor/golang.org/x/tools/refactor/eg/testdata/J1.go generated vendored Normal file
View File

@@ -0,0 +1,10 @@
// +build ignore
package I1
import "fmt"
func example() {
temp := 5
fmt.Print(temp + temp + temp)
}

View File

@@ -0,0 +1,11 @@
// +build ignore
package I1
import "fmt"
func example() {
temp := 5
temp := temp + temp
fmt.Print(temp + temp)
}

View File

@@ -0,0 +1,8 @@
package template
// Test in which replacement has a different type.
const shouldFail = "int is not a safe replacement for string"
func before() interface{} { return "three" }
func after() interface{} { return 3 }

View File

@@ -0,0 +1,15 @@
package template
import (
"crypto/x509"
"fmt"
)
// This test demonstrates a false negative: according to the language
// rules this replacement should be ok, but types.Assignable doesn't work
// in the expected way (elementwise assignability) for tuples.
// Perhaps that's even a type-checker bug?
const shouldFail = "(n int, err error) is not a safe replacement for (key interface{}, err error)"
func before() (interface{}, error) { return x509.ParsePKCS8PrivateKey(nil) }
func after() (interface{}, error) { return fmt.Print() }

View File

@@ -0,0 +1,4 @@
package template
func before() int { return 0 }
func after() int { println(); return 0 }

View File

@@ -0,0 +1,5 @@
package template
const shouldFail = "no 'before' func found in template"
func Before() {}

View File

@@ -0,0 +1,6 @@
package template
const shouldFail = "different signatures"
func before() int { return 0 }
func after() string { return "" }

167
vendor/golang.org/x/tools/refactor/importgraph/graph.go generated vendored Normal file
View File

@@ -0,0 +1,167 @@
// 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 importgraph computes the forward and reverse import
// dependency graphs for all packages in a Go workspace.
package importgraph // import "golang.org/x/tools/refactor/importgraph"
import (
"go/build"
"sync"
"golang.org/x/tools/go/buildutil"
)
// A Graph is an import dependency graph, either forward or reverse.
//
// The graph maps each node (a package import path) to the set of its
// successors in the graph. For a forward graph, this is the set of
// imported packages (prerequisites); for a reverse graph, it is the set
// of importing packages (clients).
//
// Graph construction inspects all imports in each package's directory,
// including those in _test.go files, so the resulting graph may be cyclic.
type Graph map[string]map[string]bool
func (g Graph) addEdge(from, to string) {
edges := g[from]
if edges == nil {
edges = make(map[string]bool)
g[from] = edges
}
edges[to] = true
}
// Search returns all the nodes of the graph reachable from
// any of the specified roots, by following edges forwards.
// Relationally, this is the reflexive transitive closure.
func (g Graph) Search(roots ...string) map[string]bool {
seen := make(map[string]bool)
var visit func(x string)
visit = func(x string) {
if !seen[x] {
seen[x] = true
for y := range g[x] {
visit(y)
}
}
}
for _, root := range roots {
visit(root)
}
return seen
}
// Build scans the specified Go workspace and builds the forward and
// reverse import dependency graphs for all its packages.
// It also returns a mapping from canonical import paths to errors for packages
// whose loading was not entirely successful.
// A package may appear in the graph and in the errors mapping.
// All package paths are canonical and may contain "/vendor/".
func Build(ctxt *build.Context) (forward, reverse Graph, errors map[string]error) {
type importEdge struct {
from, to string
}
type pathError struct {
path string
err error
}
ch := make(chan interface{})
go func() {
sema := make(chan int, 20) // I/O concurrency limiting semaphore
var wg sync.WaitGroup
buildutil.ForEachPackage(ctxt, func(path string, err error) {
if err != nil {
ch <- pathError{path, err}
return
}
wg.Add(1)
go func() {
defer wg.Done()
sema <- 1
bp, err := ctxt.Import(path, "", 0)
<-sema
if err != nil {
if _, ok := err.(*build.NoGoError); ok {
// empty directory is not an error
} else {
ch <- pathError{path, err}
}
// Even in error cases, Import usually returns a package.
}
// absolutize resolves an import path relative
// to the current package bp.
// The absolute form may contain "vendor".
//
// The vendoring feature slows down Build by 3×.
// Here are timings from a 1400 package workspace:
// 1100ms: current code (with vendor check)
// 880ms: with a nonblocking cache around ctxt.IsDir
// 840ms: nonblocking cache with duplicate suppression
// 340ms: original code (no vendor check)
// TODO(adonovan): optimize, somehow.
memo := make(map[string]string)
absolutize := func(path string) string {
canon, ok := memo[path]
if !ok {
sema <- 1
bp2, _ := ctxt.Import(path, bp.Dir, build.FindOnly)
<-sema
if bp2 != nil {
canon = bp2.ImportPath
} else {
canon = path
}
memo[path] = canon
}
return canon
}
if bp != nil {
for _, imp := range bp.Imports {
ch <- importEdge{path, absolutize(imp)}
}
for _, imp := range bp.TestImports {
ch <- importEdge{path, absolutize(imp)}
}
for _, imp := range bp.XTestImports {
ch <- importEdge{path, absolutize(imp)}
}
}
}()
})
wg.Wait()
close(ch)
}()
forward = make(Graph)
reverse = make(Graph)
for e := range ch {
switch e := e.(type) {
case pathError:
if errors == nil {
errors = make(map[string]error)
}
errors[e.path] = e.err
case importEdge:
if e.to == "C" {
continue // "C" is fake
}
forward.addEdge(e.from, e.to)
reverse.addEdge(e.to, e.from)
}
}
return forward, reverse, errors
}

View File

@@ -0,0 +1,99 @@
// Copyright 2015 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.
// Incomplete std lib sources on Android.
// +build !android
package importgraph_test
import (
"go/build"
"sort"
"testing"
"golang.org/x/tools/refactor/importgraph"
_ "crypto/hmac" // just for test, below
)
const this = "golang.org/x/tools/refactor/importgraph"
func TestBuild(t *testing.T) {
forward, reverse, errors := importgraph.Build(&build.Default)
// Test direct edges.
// We throw in crypto/hmac to prove that external test files
// (such as this one) are inspected.
for _, p := range []string{"go/build", "testing", "crypto/hmac"} {
if !forward[this][p] {
t.Errorf("forward[importgraph][%s] not found", p)
}
if !reverse[p][this] {
t.Errorf("reverse[%s][importgraph] not found", p)
}
}
// Test non-existent direct edges
for _, p := range []string{"errors", "reflect"} {
if forward[this][p] {
t.Errorf("unexpected: forward[importgraph][%s] found", p)
}
if reverse[p][this] {
t.Errorf("unexpected: reverse[%s][importgraph] found", p)
}
}
// Test Search is reflexive.
if !forward.Search(this)[this] {
t.Errorf("irreflexive: forward.Search(importgraph)[importgraph] not found")
}
if !reverse.Search(this)[this] {
t.Errorf("irrefexive: reverse.Search(importgraph)[importgraph] not found")
}
// Test Search is transitive. (There is no direct edge to these packages.)
for _, p := range []string{"errors", "reflect", "unsafe"} {
if !forward.Search(this)[p] {
t.Errorf("intransitive: forward.Search(importgraph)[%s] not found", p)
}
if !reverse.Search(p)[this] {
t.Errorf("intransitive: reverse.Search(%s)[importgraph] not found", p)
}
}
// Test strongly-connected components. Because A's external
// test package can depend on B, and vice versa, most of the
// standard libraries are mutually dependent when their external
// tests are considered.
//
// For any nodes x, y in the same SCC, y appears in the results
// of both forward and reverse searches starting from x
if !forward.Search("fmt")["io"] ||
!forward.Search("io")["fmt"] ||
!reverse.Search("fmt")["io"] ||
!reverse.Search("io")["fmt"] {
t.Errorf("fmt and io are not mutually reachable despite being in the same SCC")
}
// debugging
if false {
for path, err := range errors {
t.Logf("%s: %s", path, err)
}
printSorted := func(direction string, g importgraph.Graph, start string) {
t.Log(direction)
var pkgs []string
for pkg := range g.Search(start) {
pkgs = append(pkgs, pkg)
}
sort.Strings(pkgs)
for _, pkg := range pkgs {
t.Logf("\t%s", pkg)
}
}
printSorted("forward", forward, this)
printSorted("reverse", reverse, this)
}
}

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) }

705
vendor/golang.org/x/tools/refactor/satisfy/find.go generated vendored Normal file
View File

@@ -0,0 +1,705 @@
// 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 satisfy inspects the type-checked ASTs of Go packages and
// reports the set of discovered type constraints of the form (lhs, rhs
// Type) where lhs is a non-trivial interface, rhs satisfies this
// interface, and this fact is necessary for the package to be
// well-typed.
//
// THIS PACKAGE IS EXPERIMENTAL AND MAY CHANGE AT ANY TIME.
//
// It is provided only for the gorename tool. Ideally this
// functionality will become part of the type-checker in due course,
// since it is computing it anyway, and it is robust for ill-typed
// inputs, which this package is not.
//
package satisfy // import "golang.org/x/tools/refactor/satisfy"
// NOTES:
//
// We don't care about numeric conversions, so we don't descend into
// types or constant expressions. This is unsound because
// constant expressions can contain arbitrary statements, e.g.
// const x = len([1]func(){func() {
// ...
// }})
//
// TODO(adonovan): make this robust against ill-typed input.
// Or move it into the type-checker.
//
// Assignability conversions are possible in the following places:
// - in assignments y = x, y := x, var y = x.
// - from call argument types to formal parameter types
// - in append and delete calls
// - from return operands to result parameter types
// - in composite literal T{k:v}, from k and v to T's field/element/key type
// - in map[key] from key to the map's key type
// - in comparisons x==y and switch x { case y: }.
// - in explicit conversions T(x)
// - in sends ch <- x, from x to the channel element type
// - in type assertions x.(T) and switch x.(type) { case T: }
//
// The results of this pass provide information equivalent to the
// ssa.MakeInterface and ssa.ChangeInterface instructions.
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types/typeutil"
)
// A Constraint records the fact that the RHS type does and must
// satisify the LHS type, which is an interface.
// The names are suggestive of an assignment statement LHS = RHS.
type Constraint struct {
LHS, RHS types.Type
}
// A Finder inspects the type-checked ASTs of Go packages and
// accumulates the set of type constraints (x, y) such that x is
// assignable to y, y is an interface, and both x and y have methods.
//
// In other words, it returns the subset of the "implements" relation
// that is checked during compilation of a package. Refactoring tools
// will need to preserve at least this part of the relation to ensure
// continued compilation.
//
type Finder struct {
Result map[Constraint]bool
msetcache typeutil.MethodSetCache
// per-Find state
info *types.Info
sig *types.Signature
}
// Find inspects a single package, populating Result with its pairs of
// constrained types.
//
// The result is non-canonical and thus may contain duplicates (but this
// tends to preserves names of interface types better).
//
// The package must be free of type errors, and
// info.{Defs,Uses,Selections,Types} must have been populated by the
// type-checker.
//
func (f *Finder) Find(info *types.Info, files []*ast.File) {
if f.Result == nil {
f.Result = make(map[Constraint]bool)
}
f.info = info
for _, file := range files {
for _, d := range file.Decls {
switch d := d.(type) {
case *ast.GenDecl:
if d.Tok == token.VAR { // ignore consts
for _, spec := range d.Specs {
f.valueSpec(spec.(*ast.ValueSpec))
}
}
case *ast.FuncDecl:
if d.Body != nil {
f.sig = f.info.Defs[d.Name].Type().(*types.Signature)
f.stmt(d.Body)
f.sig = nil
}
}
}
}
f.info = nil
}
var (
tInvalid = types.Typ[types.Invalid]
tUntypedBool = types.Typ[types.UntypedBool]
tUntypedNil = types.Typ[types.UntypedNil]
)
// exprN visits an expression in a multi-value context.
func (f *Finder) exprN(e ast.Expr) types.Type {
typ := f.info.Types[e].Type.(*types.Tuple)
switch e := e.(type) {
case *ast.ParenExpr:
return f.exprN(e.X)
case *ast.CallExpr:
// x, err := f(args)
sig := f.expr(e.Fun).Underlying().(*types.Signature)
f.call(sig, e.Args)
case *ast.IndexExpr:
// y, ok := x[i]
x := f.expr(e.X)
f.assign(f.expr(e.Index), x.Underlying().(*types.Map).Key())
case *ast.TypeAssertExpr:
// y, ok := x.(T)
f.typeAssert(f.expr(e.X), typ.At(0).Type())
case *ast.UnaryExpr: // must be receive <-
// y, ok := <-x
f.expr(e.X)
default:
panic(e)
}
return typ
}
func (f *Finder) call(sig *types.Signature, args []ast.Expr) {
if len(args) == 0 {
return
}
// Ellipsis call? e.g. f(x, y, z...)
if _, ok := args[len(args)-1].(*ast.Ellipsis); ok {
for i, arg := range args {
// The final arg is a slice, and so is the final param.
f.assign(sig.Params().At(i).Type(), f.expr(arg))
}
return
}
var argtypes []types.Type
// Gather the effective actual parameter types.
if tuple, ok := f.info.Types[args[0]].Type.(*types.Tuple); ok {
// f(g()) call where g has multiple results?
f.expr(args[0])
// unpack the tuple
for i := 0; i < tuple.Len(); i++ {
argtypes = append(argtypes, tuple.At(i).Type())
}
} else {
for _, arg := range args {
argtypes = append(argtypes, f.expr(arg))
}
}
// Assign the actuals to the formals.
if !sig.Variadic() {
for i, argtype := range argtypes {
f.assign(sig.Params().At(i).Type(), argtype)
}
} else {
// The first n-1 parameters are assigned normally.
nnormals := sig.Params().Len() - 1
for i, argtype := range argtypes[:nnormals] {
f.assign(sig.Params().At(i).Type(), argtype)
}
// Remaining args are assigned to elements of varargs slice.
tElem := sig.Params().At(nnormals).Type().(*types.Slice).Elem()
for i := nnormals; i < len(argtypes); i++ {
f.assign(tElem, argtypes[i])
}
}
}
func (f *Finder) builtin(obj *types.Builtin, sig *types.Signature, args []ast.Expr, T types.Type) types.Type {
switch obj.Name() {
case "make", "new":
// skip the type operand
for _, arg := range args[1:] {
f.expr(arg)
}
case "append":
s := f.expr(args[0])
if _, ok := args[len(args)-1].(*ast.Ellipsis); ok && len(args) == 2 {
// append(x, y...) including append([]byte, "foo"...)
f.expr(args[1])
} else {
// append(x, y, z)
tElem := s.Underlying().(*types.Slice).Elem()
for _, arg := range args[1:] {
f.assign(tElem, f.expr(arg))
}
}
case "delete":
m := f.expr(args[0])
k := f.expr(args[1])
f.assign(m.Underlying().(*types.Map).Key(), k)
default:
// ordinary call
f.call(sig, args)
}
return T
}
func (f *Finder) extract(tuple types.Type, i int) types.Type {
if tuple, ok := tuple.(*types.Tuple); ok && i < tuple.Len() {
return tuple.At(i).Type()
}
return tInvalid
}
func (f *Finder) valueSpec(spec *ast.ValueSpec) {
var T types.Type
if spec.Type != nil {
T = f.info.Types[spec.Type].Type
}
switch len(spec.Values) {
case len(spec.Names): // e.g. var x, y = f(), g()
for _, value := range spec.Values {
v := f.expr(value)
if T != nil {
f.assign(T, v)
}
}
case 1: // e.g. var x, y = f()
tuple := f.exprN(spec.Values[0])
for i := range spec.Names {
if T != nil {
f.assign(T, f.extract(tuple, i))
}
}
}
}
// assign records pairs of distinct types that are related by
// assignability, where the left-hand side is an interface and both
// sides have methods.
//
// It should be called for all assignability checks, type assertions,
// explicit conversions and comparisons between two types, unless the
// types are uninteresting (e.g. lhs is a concrete type, or the empty
// interface; rhs has no methods).
//
func (f *Finder) assign(lhs, rhs types.Type) {
if types.Identical(lhs, rhs) {
return
}
if !isInterface(lhs) {
return
}
if f.msetcache.MethodSet(lhs).Len() == 0 {
return
}
if f.msetcache.MethodSet(rhs).Len() == 0 {
return
}
// record the pair
f.Result[Constraint{lhs, rhs}] = true
}
// typeAssert must be called for each type assertion x.(T) where x has
// interface type I.
func (f *Finder) typeAssert(I, T types.Type) {
// Type assertions are slightly subtle, because they are allowed
// to be "impossible", e.g.
//
// var x interface{f()}
// _ = x.(interface{f()int}) // legal
//
// (In hindsight, the language spec should probably not have
// allowed this, but it's too late to fix now.)
//
// This means that a type assert from I to T isn't exactly a
// constraint that T is assignable to I, but for a refactoring
// tool it is a conditional constraint that, if T is assignable
// to I before a refactoring, it should remain so after.
if types.AssignableTo(T, I) {
f.assign(I, T)
}
}
// compare must be called for each comparison x==y.
func (f *Finder) compare(x, y types.Type) {
if types.AssignableTo(x, y) {
f.assign(y, x)
} else if types.AssignableTo(y, x) {
f.assign(x, y)
}
}
// expr visits a true expression (not a type or defining ident)
// and returns its type.
func (f *Finder) expr(e ast.Expr) types.Type {
tv := f.info.Types[e]
if tv.Value != nil {
return tv.Type // prune the descent for constants
}
// tv.Type may be nil for an ast.Ident.
switch e := e.(type) {
case *ast.BadExpr, *ast.BasicLit:
// no-op
case *ast.Ident:
// (referring idents only)
if obj, ok := f.info.Uses[e]; ok {
return obj.Type()
}
if e.Name == "_" { // e.g. "for _ = range x"
return tInvalid
}
panic("undefined ident: " + e.Name)
case *ast.Ellipsis:
if e.Elt != nil {
f.expr(e.Elt)
}
case *ast.FuncLit:
saved := f.sig
f.sig = tv.Type.(*types.Signature)
f.stmt(e.Body)
f.sig = saved
case *ast.CompositeLit:
switch T := deref(tv.Type).Underlying().(type) {
case *types.Struct:
for i, elem := range e.Elts {
if kv, ok := elem.(*ast.KeyValueExpr); ok {
f.assign(f.info.Uses[kv.Key.(*ast.Ident)].Type(), f.expr(kv.Value))
} else {
f.assign(T.Field(i).Type(), f.expr(elem))
}
}
case *types.Map:
for _, elem := range e.Elts {
elem := elem.(*ast.KeyValueExpr)
f.assign(T.Key(), f.expr(elem.Key))
f.assign(T.Elem(), f.expr(elem.Value))
}
case *types.Array, *types.Slice:
tElem := T.(interface {
Elem() types.Type
}).Elem()
for _, elem := range e.Elts {
if kv, ok := elem.(*ast.KeyValueExpr); ok {
// ignore the key
f.assign(tElem, f.expr(kv.Value))
} else {
f.assign(tElem, f.expr(elem))
}
}
default:
panic("unexpected composite literal type: " + tv.Type.String())
}
case *ast.ParenExpr:
f.expr(e.X)
case *ast.SelectorExpr:
if _, ok := f.info.Selections[e]; ok {
f.expr(e.X) // selection
} else {
return f.info.Uses[e.Sel].Type() // qualified identifier
}
case *ast.IndexExpr:
x := f.expr(e.X)
i := f.expr(e.Index)
if ux, ok := x.Underlying().(*types.Map); ok {
f.assign(ux.Key(), i)
}
case *ast.SliceExpr:
f.expr(e.X)
if e.Low != nil {
f.expr(e.Low)
}
if e.High != nil {
f.expr(e.High)
}
if e.Max != nil {
f.expr(e.Max)
}
case *ast.TypeAssertExpr:
x := f.expr(e.X)
f.typeAssert(x, f.info.Types[e.Type].Type)
case *ast.CallExpr:
if tvFun := f.info.Types[e.Fun]; tvFun.IsType() {
// conversion
arg0 := f.expr(e.Args[0])
f.assign(tvFun.Type, arg0)
} else {
// function call
if id, ok := unparen(e.Fun).(*ast.Ident); ok {
if obj, ok := f.info.Uses[id].(*types.Builtin); ok {
sig := f.info.Types[id].Type.(*types.Signature)
return f.builtin(obj, sig, e.Args, tv.Type)
}
}
// ordinary call
f.call(f.expr(e.Fun).Underlying().(*types.Signature), e.Args)
}
case *ast.StarExpr:
f.expr(e.X)
case *ast.UnaryExpr:
f.expr(e.X)
case *ast.BinaryExpr:
x := f.expr(e.X)
y := f.expr(e.Y)
if e.Op == token.EQL || e.Op == token.NEQ {
f.compare(x, y)
}
case *ast.KeyValueExpr:
f.expr(e.Key)
f.expr(e.Value)
case *ast.ArrayType,
*ast.StructType,
*ast.FuncType,
*ast.InterfaceType,
*ast.MapType,
*ast.ChanType:
panic(e)
}
if tv.Type == nil {
panic(fmt.Sprintf("no type for %T", e))
}
return tv.Type
}
func (f *Finder) stmt(s ast.Stmt) {
switch s := s.(type) {
case *ast.BadStmt,
*ast.EmptyStmt,
*ast.BranchStmt:
// no-op
case *ast.DeclStmt:
d := s.Decl.(*ast.GenDecl)
if d.Tok == token.VAR { // ignore consts
for _, spec := range d.Specs {
f.valueSpec(spec.(*ast.ValueSpec))
}
}
case *ast.LabeledStmt:
f.stmt(s.Stmt)
case *ast.ExprStmt:
f.expr(s.X)
case *ast.SendStmt:
ch := f.expr(s.Chan)
val := f.expr(s.Value)
f.assign(ch.Underlying().(*types.Chan).Elem(), val)
case *ast.IncDecStmt:
f.expr(s.X)
case *ast.AssignStmt:
switch s.Tok {
case token.ASSIGN, token.DEFINE:
// y := x or y = x
var rhsTuple types.Type
if len(s.Lhs) != len(s.Rhs) {
rhsTuple = f.exprN(s.Rhs[0])
}
for i := range s.Lhs {
var lhs, rhs types.Type
if rhsTuple == nil {
rhs = f.expr(s.Rhs[i]) // 1:1 assignment
} else {
rhs = f.extract(rhsTuple, i) // n:1 assignment
}
if id, ok := s.Lhs[i].(*ast.Ident); ok {
if id.Name != "_" {
if obj, ok := f.info.Defs[id]; ok {
lhs = obj.Type() // definition
}
}
}
if lhs == nil {
lhs = f.expr(s.Lhs[i]) // assignment
}
f.assign(lhs, rhs)
}
default:
// y op= x
f.expr(s.Lhs[0])
f.expr(s.Rhs[0])
}
case *ast.GoStmt:
f.expr(s.Call)
case *ast.DeferStmt:
f.expr(s.Call)
case *ast.ReturnStmt:
formals := f.sig.Results()
switch len(s.Results) {
case formals.Len(): // 1:1
for i, result := range s.Results {
f.assign(formals.At(i).Type(), f.expr(result))
}
case 1: // n:1
tuple := f.exprN(s.Results[0])
for i := 0; i < formals.Len(); i++ {
f.assign(formals.At(i).Type(), f.extract(tuple, i))
}
}
case *ast.SelectStmt:
f.stmt(s.Body)
case *ast.BlockStmt:
for _, s := range s.List {
f.stmt(s)
}
case *ast.IfStmt:
if s.Init != nil {
f.stmt(s.Init)
}
f.expr(s.Cond)
f.stmt(s.Body)
if s.Else != nil {
f.stmt(s.Else)
}
case *ast.SwitchStmt:
if s.Init != nil {
f.stmt(s.Init)
}
var tag types.Type = tUntypedBool
if s.Tag != nil {
tag = f.expr(s.Tag)
}
for _, cc := range s.Body.List {
cc := cc.(*ast.CaseClause)
for _, cond := range cc.List {
f.compare(tag, f.info.Types[cond].Type)
}
for _, s := range cc.Body {
f.stmt(s)
}
}
case *ast.TypeSwitchStmt:
if s.Init != nil {
f.stmt(s.Init)
}
var I types.Type
switch ass := s.Assign.(type) {
case *ast.ExprStmt: // x.(type)
I = f.expr(unparen(ass.X).(*ast.TypeAssertExpr).X)
case *ast.AssignStmt: // y := x.(type)
I = f.expr(unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
}
for _, cc := range s.Body.List {
cc := cc.(*ast.CaseClause)
for _, cond := range cc.List {
tCase := f.info.Types[cond].Type
if tCase != tUntypedNil {
f.typeAssert(I, tCase)
}
}
for _, s := range cc.Body {
f.stmt(s)
}
}
case *ast.CommClause:
if s.Comm != nil {
f.stmt(s.Comm)
}
for _, s := range s.Body {
f.stmt(s)
}
case *ast.ForStmt:
if s.Init != nil {
f.stmt(s.Init)
}
if s.Cond != nil {
f.expr(s.Cond)
}
if s.Post != nil {
f.stmt(s.Post)
}
f.stmt(s.Body)
case *ast.RangeStmt:
x := f.expr(s.X)
// No conversions are involved when Tok==DEFINE.
if s.Tok == token.ASSIGN {
if s.Key != nil {
k := f.expr(s.Key)
var xelem types.Type
// keys of array, *array, slice, string aren't interesting
switch ux := x.Underlying().(type) {
case *types.Chan:
xelem = ux.Elem()
case *types.Map:
xelem = ux.Key()
}
if xelem != nil {
f.assign(xelem, k)
}
}
if s.Value != nil {
val := f.expr(s.Value)
var xelem types.Type
// values of strings aren't interesting
switch ux := x.Underlying().(type) {
case *types.Array:
xelem = ux.Elem()
case *types.Chan:
xelem = ux.Elem()
case *types.Map:
xelem = ux.Elem()
case *types.Pointer: // *array
xelem = deref(ux).(*types.Array).Elem()
case *types.Slice:
xelem = ux.Elem()
}
if xelem != nil {
f.assign(xelem, val)
}
}
}
f.stmt(s.Body)
default:
panic(s)
}
}
// -- Plundered from golang.org/x/tools/go/ssa -----------------
// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
if p, ok := typ.Underlying().(*types.Pointer); ok {
return p.Elem()
}
return typ
}
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
func isInterface(T types.Type) bool { return types.IsInterface(T) }