refactor/eg: refactor to break dependency on go/loader
Change-Id: I0a865a694b911437944743f60bfaa2a937d85c9a Reviewed-on: https://go-review.googlesource.com/14133 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
		
							parent
							
								
									af0fde4393
								
							
						
					
					
						commit
						b9fbbb8e31
					
				| 
						 | 
					@ -90,7 +90,7 @@ func doMain() error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Analyze the template.
 | 
						// Analyze the template.
 | 
				
			||||||
	template := iprog.Created[0]
 | 
						template := iprog.Created[0]
 | 
				
			||||||
	xform, err := eg.NewTransformer(iprog.Fset, template, *verboseFlag)
 | 
						xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,6 @@ import (
 | 
				
			||||||
	"go/token"
 | 
						"go/token"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/tools/go/loader"
 | 
					 | 
				
			||||||
	"golang.org/x/tools/go/types"
 | 
						"golang.org/x/tools/go/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,19 +130,13 @@ 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'.
 | 
					match f'; (4) use eg to rename f' to f in all calls; (5) delete f'.
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO(adonovan): allow the tool to be invoked using relative package
 | 
					 | 
				
			||||||
// directory names (./foo).  Requires changes to go/loader.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO(adonovan): expand upon the above documentation as an HTML page.
 | 
					// TODO(adonovan): expand upon the above documentation as an HTML page.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO(adonovan): eliminate dependency on loader.PackageInfo.
 | 
					 | 
				
			||||||
// Move its TypeOf method into go/types.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// A Transformer represents a single example-based transformation.
 | 
					// A Transformer represents a single example-based transformation.
 | 
				
			||||||
type Transformer struct {
 | 
					type Transformer struct {
 | 
				
			||||||
	fset           *token.FileSet
 | 
						fset           *token.FileSet
 | 
				
			||||||
	verbose        bool
 | 
						verbose        bool
 | 
				
			||||||
	info           loader.PackageInfo // combined type info for template/input/output ASTs
 | 
						info           *types.Info // combined type info for template/input/output ASTs
 | 
				
			||||||
	seenInfos      map[*types.Info]bool
 | 
						seenInfos      map[*types.Info]bool
 | 
				
			||||||
	wildcards      map[*types.Var]bool                // set of parameters in func before()
 | 
						wildcards      map[*types.Var]bool                // set of parameters in func before()
 | 
				
			||||||
	env            map[string]ast.Expr                // maps parameter name to wildcard binding
 | 
						env            map[string]ast.Expr                // maps parameter name to wildcard binding
 | 
				
			||||||
| 
						 | 
					@ -157,16 +150,17 @@ type Transformer struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTransformer returns a transformer based on the specified template,
 | 
					// NewTransformer returns a transformer based on the specified template,
 | 
				
			||||||
// a package containing "before" and "after" functions as described
 | 
					// a single-file package containing "before" and "after" functions as
 | 
				
			||||||
// in the package documentation.
 | 
					// described in the package documentation.
 | 
				
			||||||
 | 
					// tmplInfo is the type information for tmplFile.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose bool) (*Transformer, error) {
 | 
					func NewTransformer(fset *token.FileSet, tmplPkg *types.Package, tmplFile *ast.File, tmplInfo *types.Info, verbose bool) (*Transformer, error) {
 | 
				
			||||||
	// Check the template.
 | 
						// Check the template.
 | 
				
			||||||
	beforeSig := funcSig(template.Pkg, "before")
 | 
						beforeSig := funcSig(tmplPkg, "before")
 | 
				
			||||||
	if beforeSig == nil {
 | 
						if beforeSig == nil {
 | 
				
			||||||
		return nil, fmt.Errorf("no 'before' func found in template")
 | 
							return nil, fmt.Errorf("no 'before' func found in template")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	afterSig := funcSig(template.Pkg, "after")
 | 
						afterSig := funcSig(tmplPkg, "after")
 | 
				
			||||||
	if afterSig == nil {
 | 
						if afterSig == nil {
 | 
				
			||||||
		return nil, fmt.Errorf("no 'after' func found in template")
 | 
							return nil, fmt.Errorf("no 'after' func found in template")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -177,18 +171,17 @@ func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose b
 | 
				
			||||||
			beforeSig, afterSig)
 | 
								beforeSig, afterSig)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	templateFile := template.Files[0]
 | 
						for _, imp := range tmplFile.Imports {
 | 
				
			||||||
	for _, imp := range templateFile.Imports {
 | 
					 | 
				
			||||||
		if imp.Name != nil && imp.Name.Name == "." {
 | 
							if imp.Name != nil && imp.Name.Name == "." {
 | 
				
			||||||
			// Dot imports are currently forbidden.  We
 | 
								// Dot imports are currently forbidden.  We
 | 
				
			||||||
			// make the simplifying assumption that all
 | 
								// make the simplifying assumption that all
 | 
				
			||||||
			// imports are regular, without local renames.
 | 
								// imports are regular, without local renames.
 | 
				
			||||||
			//TODO document
 | 
								// TODO(adonovan): document
 | 
				
			||||||
			return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
 | 
								return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var beforeDecl, afterDecl *ast.FuncDecl
 | 
						var beforeDecl, afterDecl *ast.FuncDecl
 | 
				
			||||||
	for _, decl := range templateFile.Decls {
 | 
						for _, decl := range tmplFile.Decls {
 | 
				
			||||||
		if decl, ok := decl.(*ast.FuncDecl); ok {
 | 
							if decl, ok := decl.(*ast.FuncDecl); ok {
 | 
				
			||||||
			switch decl.Name.Name {
 | 
								switch decl.Name.Name {
 | 
				
			||||||
			case "before":
 | 
								case "before":
 | 
				
			||||||
| 
						 | 
					@ -228,8 +221,8 @@ func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose b
 | 
				
			||||||
	// of the replacement.  (Consider the rule that array literal keys
 | 
						// of the replacement.  (Consider the rule that array literal keys
 | 
				
			||||||
	// must be unique.)  So we cannot hope to prove the safety of a
 | 
						// must be unique.)  So we cannot hope to prove the safety of a
 | 
				
			||||||
	// transformation in general.
 | 
						// transformation in general.
 | 
				
			||||||
	Tb := template.TypeOf(before)
 | 
						Tb := tmplInfo.TypeOf(before)
 | 
				
			||||||
	Ta := template.TypeOf(after)
 | 
						Ta := tmplInfo.TypeOf(after)
 | 
				
			||||||
	if types.AssignableTo(Tb, Ta) {
 | 
						if types.AssignableTo(Tb, Ta) {
 | 
				
			||||||
		// safe: replacement is assignable to pattern.
 | 
							// safe: replacement is assignable to pattern.
 | 
				
			||||||
	} else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 {
 | 
						} else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 {
 | 
				
			||||||
| 
						 | 
					@ -253,19 +246,16 @@ func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose b
 | 
				
			||||||
	// type info for the synthesized ASTs too.  This saves us
 | 
						// type info for the synthesized ASTs too.  This saves us
 | 
				
			||||||
	// having to book-keep where each ast.Node originated as we
 | 
						// having to book-keep where each ast.Node originated as we
 | 
				
			||||||
	// construct the resulting hybrid AST.
 | 
						// construct the resulting hybrid AST.
 | 
				
			||||||
	//
 | 
						tr.info = &types.Info{
 | 
				
			||||||
	// TODO(adonovan): move type utility methods of PackageInfo to
 | 
					 | 
				
			||||||
	// types.Info, or at least into go/types.typeutil.
 | 
					 | 
				
			||||||
	tr.info.Info = types.Info{
 | 
					 | 
				
			||||||
		Types:      make(map[ast.Expr]types.TypeAndValue),
 | 
							Types:      make(map[ast.Expr]types.TypeAndValue),
 | 
				
			||||||
		Defs:       make(map[*ast.Ident]types.Object),
 | 
							Defs:       make(map[*ast.Ident]types.Object),
 | 
				
			||||||
		Uses:       make(map[*ast.Ident]types.Object),
 | 
							Uses:       make(map[*ast.Ident]types.Object),
 | 
				
			||||||
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
 | 
							Selections: make(map[*ast.SelectorExpr]*types.Selection),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mergeTypeInfo(&tr.info.Info, &template.Info)
 | 
						mergeTypeInfo(tr.info, tmplInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compute set of imported objects required by after().
 | 
						// Compute set of imported objects required by after().
 | 
				
			||||||
	// TODO reject dot-imports in pattern
 | 
						// TODO(adonovan): reject dot-imports in pattern
 | 
				
			||||||
	ast.Inspect(after, func(n ast.Node) bool {
 | 
						ast.Inspect(after, func(n ast.Node) bool {
 | 
				
			||||||
		if n, ok := n.(*ast.SelectorExpr); ok {
 | 
							if n, ok := n.(*ast.SelectorExpr); ok {
 | 
				
			||||||
			if _, ok := tr.info.Selections[n]; !ok {
 | 
								if _, ok := tr.info.Selections[n]; !ok {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,7 +100,7 @@ func Test(t *testing.T) {
 | 
				
			||||||
		if strings.HasSuffix(filename, "template") {
 | 
							if strings.HasSuffix(filename, "template") {
 | 
				
			||||||
			// a new template
 | 
								// a new template
 | 
				
			||||||
			shouldFail, _ := info.Pkg.Scope().Lookup("shouldFail").(*types.Const)
 | 
								shouldFail, _ := info.Pkg.Scope().Lookup("shouldFail").(*types.Const)
 | 
				
			||||||
			xform, err = eg.NewTransformer(iprog.Fset, info, *verboseFlag)
 | 
								xform, err = eg.NewTransformer(iprog.Fset, info.Pkg, file, &info.Info, *verboseFlag)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				if shouldFail == nil {
 | 
									if shouldFail == nil {
 | 
				
			||||||
					t.Errorf("NewTransformer(%s): %s", filename, err)
 | 
										t.Errorf("NewTransformer(%s): %s", filename, err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/tools/go/ast/astutil"
 | 
						"golang.org/x/tools/go/ast/astutil"
 | 
				
			||||||
	"golang.org/x/tools/go/exact"
 | 
						"golang.org/x/tools/go/exact"
 | 
				
			||||||
	"golang.org/x/tools/go/loader"
 | 
					 | 
				
			||||||
	"golang.org/x/tools/go/types"
 | 
						"golang.org/x/tools/go/types"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,8 +41,8 @@ func (tr *Transformer) matchExpr(x, y ast.Expr) bool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Object identifiers (including pkg-qualified ones)
 | 
						// Object identifiers (including pkg-qualified ones)
 | 
				
			||||||
	// are handled semantically, not syntactically.
 | 
						// are handled semantically, not syntactically.
 | 
				
			||||||
	xobj := isRef(x, &tr.info)
 | 
						xobj := isRef(x, tr.info)
 | 
				
			||||||
	yobj := isRef(y, &tr.info)
 | 
						yobj := isRef(y, tr.info)
 | 
				
			||||||
	if xobj != nil {
 | 
						if xobj != nil {
 | 
				
			||||||
		return xobj == yobj
 | 
							return xobj == yobj
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -231,7 +230,7 @@ func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// isRef returns the object referred to by this (possibly qualified)
 | 
					// isRef returns the object referred to by this (possibly qualified)
 | 
				
			||||||
// identifier, or nil if the node is not a referring identifier.
 | 
					// identifier, or nil if the node is not a referring identifier.
 | 
				
			||||||
func isRef(n ast.Node, info *loader.PackageInfo) types.Object {
 | 
					func isRef(n ast.Node, info *types.Info) types.Object {
 | 
				
			||||||
	switch n := n.(type) {
 | 
						switch n := n.(type) {
 | 
				
			||||||
	case *ast.Ident:
 | 
						case *ast.Ident:
 | 
				
			||||||
		return info.Uses[n]
 | 
							return info.Uses[n]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ import (
 | 
				
			||||||
func (tr *Transformer) Transform(info *types.Info, pkg *types.Package, file *ast.File) int {
 | 
					func (tr *Transformer) Transform(info *types.Info, pkg *types.Package, file *ast.File) int {
 | 
				
			||||||
	if !tr.seenInfos[info] {
 | 
						if !tr.seenInfos[info] {
 | 
				
			||||||
		tr.seenInfos[info] = true
 | 
							tr.seenInfos[info] = true
 | 
				
			||||||
		mergeTypeInfo(&tr.info.Info, info)
 | 
							mergeTypeInfo(tr.info, info)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tr.currentPkg = pkg
 | 
						tr.currentPkg = pkg
 | 
				
			||||||
	tr.nsubsts = 0
 | 
						tr.nsubsts = 0
 | 
				
			||||||
| 
						 | 
					@ -234,7 +234,7 @@ func (tr *Transformer) subst(env map[string]ast.Expr, pattern, pos reflect.Value
 | 
				
			||||||
	// denoted by unqualified identifiers.
 | 
						// denoted by unqualified identifiers.
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	if tr.importedObjs != nil && pattern.Type() == selectorExprType {
 | 
						if tr.importedObjs != nil && pattern.Type() == selectorExprType {
 | 
				
			||||||
		obj := isRef(pattern.Interface().(*ast.SelectorExpr), &tr.info)
 | 
							obj := isRef(pattern.Interface().(*ast.SelectorExpr), tr.info)
 | 
				
			||||||
		if obj != nil {
 | 
							if obj != nil {
 | 
				
			||||||
			if sel, ok := tr.importedObjs[obj]; ok {
 | 
								if sel, ok := tr.importedObjs[obj]; ok {
 | 
				
			||||||
				var id ast.Expr
 | 
									var id ast.Expr
 | 
				
			||||||
| 
						 | 
					@ -288,7 +288,7 @@ func (tr *Transformer) subst(env map[string]ast.Expr, pattern, pos reflect.Value
 | 
				
			||||||
		// All ast.Node implementations are *structs,
 | 
							// All ast.Node implementations are *structs,
 | 
				
			||||||
		// so this case catches them all.
 | 
							// so this case catches them all.
 | 
				
			||||||
		if e := rvToExpr(v); e != nil {
 | 
							if e := rvToExpr(v); e != nil {
 | 
				
			||||||
			updateTypeInfo(&tr.info.Info, e, p.Interface().(ast.Expr))
 | 
								updateTypeInfo(tr.info, e, p.Interface().(ast.Expr))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return v
 | 
							return v
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue