341 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| 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"
 | |
| 
 | |
| 	"code.google.com/p/go.tools/astutil"
 | |
| 	"code.google.com/p/go.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, 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.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
 | |
| 	}
 | |
| }
 |