--- /dev/null
+// Copyright 2012 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 main
+
+import (
+ "fmt"
+ "go/ast"
+ "log"
+ "reflect"
+)
+
+const debugClone = false
+
+// TODO(bradfitz): delete this function (and whole file) once
+// http://golang.org/issue/4380 is fixed.
+func clone(i interface{}) (cloned interface{}) {
+ if debugClone {
+ defer func() {
+ if !reflect.DeepEqual(i, cloned) {
+ log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned)
+ }
+ }()
+ }
+ switch v := i.(type) {
+ case nil:
+ return nil
+ case *ast.File:
+ o := &ast.File{
+ Doc: v.Doc, // shallow
+ Package: v.Package,
+ Comments: v.Comments, // shallow
+ Name: v.Name,
+ Scope: v.Scope,
+ }
+ for _, x := range v.Decls {
+ o.Decls = append(o.Decls, clone(x).(ast.Decl))
+ }
+ for _, x := range v.Imports {
+ o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec))
+ }
+ for _, x := range v.Unresolved {
+ o.Unresolved = append(o.Unresolved, x)
+ }
+ return o
+ case *ast.GenDecl:
+ o := new(ast.GenDecl)
+ *o = *v
+ o.Specs = nil
+ for _, x := range v.Specs {
+ o.Specs = append(o.Specs, clone(x).(ast.Spec))
+ }
+ return o
+ case *ast.TypeSpec:
+ o := new(ast.TypeSpec)
+ *o = *v
+ o.Type = cloneExpr(v.Type)
+ return o
+ case *ast.InterfaceType:
+ o := new(ast.InterfaceType)
+ *o = *v
+ o.Methods = clone(v.Methods).(*ast.FieldList)
+ return o
+ case *ast.FieldList:
+ if v == nil {
+ return v
+ }
+ o := new(ast.FieldList)
+ *o = *v
+ o.List = nil
+ for _, x := range v.List {
+ o.List = append(o.List, clone(x).(*ast.Field))
+ }
+ return o
+ case *ast.Field:
+ o := &ast.Field{
+ Doc: v.Doc, // shallow
+ Type: cloneExpr(v.Type),
+ Tag: clone(v.Tag).(*ast.BasicLit),
+ Comment: v.Comment, // shallow
+ }
+ for _, x := range v.Names {
+ o.Names = append(o.Names, clone(x).(*ast.Ident))
+ }
+ return o
+ case *ast.FuncType:
+ if v == nil {
+ return v
+ }
+ return &ast.FuncType{
+ Func: v.Func,
+ Params: clone(v.Params).(*ast.FieldList),
+ Results: clone(v.Results).(*ast.FieldList),
+ }
+ case *ast.FuncDecl:
+ if v == nil {
+ return v
+ }
+ return &ast.FuncDecl{
+ Recv: clone(v.Recv).(*ast.FieldList),
+ Name: v.Name,
+ Type: clone(v.Type).(*ast.FuncType),
+ Body: v.Body, // shallow
+ }
+ case *ast.ValueSpec:
+ if v == nil {
+ return v
+ }
+ o := &ast.ValueSpec{
+ Type: cloneExpr(v.Type),
+ }
+ for _, x := range v.Names {
+ o.Names = append(o.Names, x)
+ }
+ for _, x := range v.Values {
+ o.Values = append(o.Values, cloneExpr(x))
+ }
+ return o
+ case *ast.CallExpr:
+ if v == nil {
+ return v
+ }
+ o := &ast.CallExpr{}
+ *o = *v
+ o.Args = cloneExprs(v.Args)
+ o.Fun = cloneExpr(v.Fun)
+ return o
+ case *ast.SelectorExpr:
+ if v == nil {
+ return nil
+ }
+ return &ast.SelectorExpr{
+ X: cloneExpr(v.X),
+ Sel: v.Sel,
+ }
+ case *ast.ArrayType:
+ return &ast.ArrayType{
+ Lbrack: v.Lbrack,
+ Len: cloneExpr(v.Len),
+ Elt: cloneExpr(v.Elt),
+ }
+ case *ast.StructType:
+ return &ast.StructType{
+ Struct: v.Struct,
+ Fields: clone(v.Fields).(*ast.FieldList),
+ Incomplete: v.Incomplete,
+ }
+ case *ast.StarExpr:
+ return &ast.StarExpr{
+ Star: v.Star,
+ X: cloneExpr(v.X),
+ }
+ case *ast.CompositeLit:
+ return &ast.CompositeLit{
+ Type: cloneExpr(v.Type),
+ Lbrace: v.Lbrace,
+ Elts: cloneExprs(v.Elts),
+ Rbrace: v.Rbrace,
+ }
+ case *ast.UnaryExpr:
+ return &ast.UnaryExpr{
+ OpPos: v.OpPos,
+ Op: v.Op,
+ X: cloneExpr(v.X),
+ }
+ case *ast.BinaryExpr:
+ return &ast.BinaryExpr{
+ OpPos: v.OpPos,
+ Op: v.Op,
+ X: cloneExpr(v.X),
+ Y: cloneExpr(v.Y),
+ }
+ case *ast.Ellipsis:
+ return &ast.Ellipsis{
+ Ellipsis: v.Ellipsis,
+ Elt: cloneExpr(v.Elt),
+ }
+ case *ast.KeyValueExpr:
+ return &ast.KeyValueExpr{
+ Key: cloneExpr(v.Key),
+ Colon: v.Colon,
+ Value: cloneExpr(v.Value),
+ }
+ case *ast.FuncLit:
+ return &ast.FuncLit{
+ Type: clone(v.Type).(*ast.FuncType),
+ Body: v.Body, // shallow
+ }
+ case *ast.MapType:
+ return &ast.MapType{
+ Map: v.Map,
+ Key: cloneExpr(v.Key),
+ Value: cloneExpr(v.Value),
+ }
+ case *ast.ParenExpr:
+ return &ast.ParenExpr{
+ Lparen: v.Lparen,
+ X: cloneExpr(v.X),
+ Rparen: v.Rparen,
+ }
+ case *ast.Ident, *ast.BasicLit:
+ return v
+ case *ast.ImportSpec:
+ return &ast.ImportSpec{
+ Doc: v.Doc, // shallow
+ Name: v.Name,
+ Path: clone(v.Path).(*ast.BasicLit),
+ Comment: v.Comment, // shallow
+ EndPos: v.EndPos,
+ }
+ case *ast.ChanType:
+ return &ast.ChanType{
+ Begin: v.Begin,
+ Arrow: v.Arrow,
+ Dir: v.Dir,
+ Value: cloneExpr(v.Value),
+ }
+ case *ast.TypeAssertExpr:
+ return &ast.TypeAssertExpr{
+ X: cloneExpr(v.X),
+ Type: cloneExpr(v.Type),
+ }
+ case *ast.IndexExpr:
+ return &ast.IndexExpr{
+ X: cloneExpr(v.X),
+ Index: cloneExpr(v.Index),
+ Lbrack: v.Lbrack,
+ Rbrack: v.Rbrack,
+ }
+ }
+ panic(fmt.Sprintf("Uncloneable type %T", i))
+}
+
+func cloneExpr(x ast.Expr) ast.Expr {
+ if x == nil {
+ return nil
+ }
+ return clone(x).(ast.Expr)
+}
+
+func cloneExprs(x []ast.Expr) []ast.Expr {
+ if x == nil {
+ return nil
+ }
+ o := make([]ast.Expr, len(x))
+ for i, x := range x {
+ o[i] = cloneExpr(x)
+ }
+ return o
+}
return
}
+var parsedFileCache = make(map[string]*ast.File)
+
+func parseFile(filename string) (*ast.File, error) {
+ f, ok := parsedFileCache[filename]
+ if !ok {
+ var err error
+ f, err = parser.ParseFile(fset, filename, nil, 0)
+ if err != nil {
+ return nil, err
+ }
+ parsedFileCache[filename] = f
+ }
+ return clone(f).(*ast.File), nil
+}
+
// WalkPackage walks all files in package `name'.
// WalkPackage does nothing if the package has already been loaded.
func (w *Walker) WalkPackage(name string) {
files := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
for _, file := range files {
- f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
+ f, err := parseFile(filepath.Join(dir, file))
if err != nil {
log.Fatalf("error parsing package %s, file %s: %v", name, file, err)
}