| // 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 preceding 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() | 
 | } |