1
0
mirror of https://github.com/golang/go synced 2024-11-05 11:36:10 -07:00

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:
Alan Donovan 2015-06-01 15:21:03 -04:00
parent af0fde4393
commit b9fbbb8e31
5 changed files with 23 additions and 34 deletions

View File

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

View File

@ -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 {

View File

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

View File

@ -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]

View File

@ -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