diff --git a/src/cmd/api/clone.go b/src/cmd/api/clone.go new file mode 100644 index 00000000000..180215f4b9c --- /dev/null +++ b/src/cmd/api/clone.go @@ -0,0 +1,251 @@ +// 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 +} diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 9b7e0902776..a61497c52dc 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -353,6 +353,21 @@ func fileDeps(f *ast.File) (pkgs []string) { 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) { @@ -386,7 +401,7 @@ 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) } diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go index a98ca1e9115..a18a6418fa4 100644 --- a/src/cmd/api/testdata/src/pkg/p1/p1.go +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -167,3 +167,25 @@ const ( foo2 string = "foo2" truth = foo == "foo" || foo2 == "foo2" ) + +func ellipsis(...string) {} + +var x = &S{ + Public: nil, + private: nil, + publicTime: time.Now(), +} + +var parenExpr = (1 + 5) + +var funcLit = func() {} + +var m map[string]int + +var chanVar chan int + +var ifaceVar interface{} = 5 + +var assertVar = ifaceVar.(int) + +var indexVar = m["foo"]