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:
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
Block a user