From f07cde3d91da1b9e207b43aa4ce4b822555d1f8b Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 6 Apr 2016 18:00:18 -0400 Subject: [PATCH] refactor/eg: fix build after deletion of x/tools/go/types Change-Id: Ic8d9c37230b3359a3caa7514c89eae9595f2fb80 Reviewed-on: https://go-review.googlesource.com/21584 Reviewed-by: Robert Griesemer --- refactor/eg/eg14.go | 347 --------------------------------------- refactor/eg/eg14_test.go | 162 ------------------ refactor/eg/match.go | 6 +- refactor/eg/match14.go | 251 ---------------------------- refactor/eg/rewrite14.go | 346 -------------------------------------- 5 files changed, 3 insertions(+), 1109 deletions(-) delete mode 100644 refactor/eg/eg14.go delete mode 100644 refactor/eg/eg14_test.go delete mode 100644 refactor/eg/match14.go delete mode 100644 refactor/eg/rewrite14.go diff --git a/refactor/eg/eg14.go b/refactor/eg/eg14.go deleted file mode 100644 index d6790fe441..0000000000 --- a/refactor/eg/eg14.go +++ /dev/null @@ -1,347 +0,0 @@ -// 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. - -// +build !go1.5 - -// 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" - "os" - - "golang.org/x/tools/go/types" -) - -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 - 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. - // TODO(adonovan): document - 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) - } - after, err := soleExpr(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, - } - - // 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") -} - -// 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() -} diff --git a/refactor/eg/eg14_test.go b/refactor/eg/eg14_test.go deleted file mode 100644 index 814383ef3f..0000000000 --- a/refactor/eg/eg14_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// 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. - -// +build !go1.5 - -// No testdata on Android. - -// +build !android - -package eg_test - -import ( - "bytes" - "flag" - "go/parser" - "go/token" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "testing" - - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/types" - "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/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) - } - } - } - } -} diff --git a/refactor/eg/match.go b/refactor/eg/match.go index 8d989bcaa7..457f5e1f94 100644 --- a/refactor/eg/match.go +++ b/refactor/eg/match.go @@ -9,6 +9,7 @@ package eg import ( "fmt" "go/ast" + exact "go/constant" "go/token" "go/types" "log" @@ -16,7 +17,6 @@ import ( "reflect" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/exact" ) // matchExpr reports whether pattern x matches y. @@ -69,8 +69,8 @@ func (tr *Transformer) matchExpr(x, y ast.Expr) bool { case *ast.BasicLit: y := y.(*ast.BasicLit) - xval := exact.MakeFromLiteral(x.Value, x.Kind) - yval := exact.MakeFromLiteral(y.Value, y.Kind) + 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: diff --git a/refactor/eg/match14.go b/refactor/eg/match14.go deleted file mode 100644 index 10b84ab112..0000000000 --- a/refactor/eg/match14.go +++ /dev/null @@ -1,251 +0,0 @@ -// 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. - -// +build !go1.5 - -package eg - -import ( - "fmt" - "go/ast" - "go/token" - "log" - "os" - "reflect" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/exact" - "golang.org/x/tools/go/types" -) - -// 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) - yval := exact.MakeFromLiteral(y.Value, y.Kind) - 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 -} diff --git a/refactor/eg/rewrite14.go b/refactor/eg/rewrite14.go deleted file mode 100644 index 01b4fe2dc4..0000000000 --- a/refactor/eg/rewrite14.go +++ /dev/null @@ -1,346 +0,0 @@ -// 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. - -// +build !go1.5 - -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" - "os" - "reflect" - "sort" - "strconv" - "strings" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types" -) - -// 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)) - } - - var f func(rv reflect.Value) reflect.Value - f = func(rv reflect.Value) reflect.Value { - // don't bother if val is invalid to start with - if !rv.IsValid() { - return reflect.Value{} - } - - rv = apply(f, rv) - - e := rvToExpr(rv) - if e != nil { - 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())) - } - tr.env = savedEnv - } - - return rv - } - file2 := apply(f, reflect.ValueOf(file)).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)) - positionType = reflect.TypeOf(token.NoPos) - callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) - 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. -func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value { - if !val.IsValid() { - return reflect.Value{} - } - - // *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 - } - - // similarly for scopes: they are likely incorrect after a rewrite; - // replace them with nil - if val.Type() == scopePtrType { - return scopePtrNil - } - - switch v := reflect.Indirect(val); v.Kind() { - case reflect.Slice: - for i := 0; i < v.Len(); i++ { - e := v.Index(i) - setValue(e, f(e)) - } - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - e := v.Field(i) - setValue(e, f(e)) - } - case reflect.Interface: - e := v.Elem() - setValue(v, f(e)) - } - return val -} - -// 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 - } -}