From: Rémy Oudompheng Date: Tue, 21 Feb 2012 06:37:25 +0000 (+0100) Subject: cmd/api: record return type of functions for variable typecheck. X-Git-Tag: weekly.2012-02-22~56 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d75023e1d144793dcf83ba45c3857656134c4fa0;p=gostls13.git cmd/api: record return type of functions for variable typecheck. Also cleanup the resolveName method. Fixes failure on go/build declaration: var ToolDir = filepath.Join(...) R=golang-dev, bradfitz CC=golang-dev, remy https://golang.org/cl/5681043 --- diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 6f484e29b7..ee0f92328e 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -176,8 +176,9 @@ type Walker struct { constDep map[string]string // key's const identifier has type of future value const identifier packageState map[string]loadState interfaces map[pkgSymbol]*ast.InterfaceType - selectorFullPkg map[string]string // "http" => "net/http", updated by imports - wantedPkg map[string]bool // packages requested on the command line + functionTypes map[pkgSymbol]string // symbol => return type + selectorFullPkg map[string]string // "http" => "net/http", updated by imports + wantedPkg map[string]bool // packages requested on the command line } func NewWalker() *Walker { @@ -186,6 +187,7 @@ func NewWalker() *Walker { features: make(map[string]bool), packageState: make(map[string]loadState), interfaces: make(map[pkgSymbol]*ast.InterfaceType), + functionTypes: make(map[pkgSymbol]string), selectorFullPkg: make(map[string]string), wantedPkg: make(map[string]bool), prevConstType: make(map[pkgSymbol]string), @@ -295,6 +297,15 @@ func (w *Walker) WalkPackage(name string) { w.recordTypes(afile) } + // Register all function declarations first. + for _, afile := range apkg.Files { + for _, di := range afile.Decls { + if d, ok := di.(*ast.FuncDecl); ok { + w.peekFuncDecl(d) + } + } + } + for _, afile := range apkg.Files { w.walkFile(afile) } @@ -360,7 +371,6 @@ func (w *Walker) recordTypes(file *ast.File) { func (w *Walker) walkFile(file *ast.File) { // Not entering a scope here; file boundaries aren't interesting. - for _, di := range file.Decls { switch d := di.(type) { case *ast.GenDecl: @@ -506,11 +516,6 @@ func (w *Walker) constValueType(vi interface{}) (string, error) { } func (w *Walker) varValueType(vi interface{}) (string, error) { - valStr := w.nodeString(vi) - if strings.HasPrefix(valStr, "errors.New(") { - return "error", nil - } - switch v := vi.(type) { case *ast.BasicLit: litType, ok := varType[v.Kind] @@ -552,17 +557,30 @@ func (w *Walker) varValueType(vi interface{}) (string, error) { case *ast.ParenExpr: return w.varValueType(v.X) case *ast.CallExpr: - funStr := w.nodeString(v.Fun) - node, _, ok := w.resolveName(funStr) - if !ok { - return "", fmt.Errorf("unresolved named %q", funStr) - } - if funcd, ok := node.(*ast.FuncDecl); ok { - // Assume at the top level that all functions have exactly 1 result - return w.nodeString(w.namelessType(funcd.Type.Results.List[0].Type)), nil + var funSym pkgSymbol + if selnode, ok := v.Fun.(*ast.SelectorExpr); ok { + // assume it is not a method. + pkg, ok := w.selectorFullPkg[w.nodeString(selnode.X)] + if !ok { + return "", fmt.Errorf("not a package: %s", w.nodeString(selnode.X)) + } + funSym = pkgSymbol{pkg, selnode.Sel.Name} + if retType, ok := w.functionTypes[funSym]; ok { + if ast.IsExported(retType) && pkg != w.curPackageName { + // otherpkg.F returning an exported type from otherpkg. + return pkg + "." + retType, nil + } else { + return retType, nil + } + } + } else { + funSym = pkgSymbol{w.curPackageName, w.nodeString(v.Fun)} + if retType, ok := w.functionTypes[funSym]; ok { + return retType, nil + } } // maybe a function call; maybe a conversion. Need to lookup type. - return "", fmt.Errorf("resolved name %q to a %T: %#v", funStr, node, node) + return "", fmt.Errorf("not a known function %q", w.nodeString(v.Fun)) default: return "", fmt.Errorf("unknown const value type %T", vi) } @@ -575,19 +593,8 @@ func (w *Walker) resolveName(name string) (v interface{}, t interface{}, ok bool for _, file := range w.curPackage.Files { for _, di := range file.Decls { switch d := di.(type) { - case *ast.FuncDecl: - if d.Name.Name == name { - return d, d.Type, true - } case *ast.GenDecl: switch d.Tok { - case token.TYPE: - for _, sp := range d.Specs { - ts := sp.(*ast.TypeSpec) - if ts.Name.Name == name { - return ts, ts.Type, true - } - } case token.VAR: for _, sp := range d.Specs { vs := sp.(*ast.ValueSpec) @@ -853,6 +860,17 @@ func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) { } } +func (w *Walker) peekFuncDecl(f *ast.FuncDecl) { + if f.Recv != nil { + return + } + // Record return type for later use. + if f.Type.Results != nil && len(f.Type.Results.List) == 1 { + retType := w.nodeString(w.namelessType(f.Type.Results.List[0].Type)) + w.functionTypes[pkgSymbol{w.curPackageName, f.Name.Name}] = retType + } +} + func (w *Walker) walkFuncDecl(f *ast.FuncDecl) { if !ast.IsExported(f.Name.Name) { return diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt index 7c6d8c8a85..3a1b3f5350 100644 --- a/src/cmd/api/testdata/src/pkg/p1/golden.txt +++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt @@ -9,6 +9,7 @@ pkg p1, const StrConst ideal-string pkg p1, func Bar(int8, int16, int64) pkg p1, func Bar1(int8, int16, int64) uint64 pkg p1, func Bar2(int8, int16, int64) (uint8, uint64) +pkg p1, func BarE() Error pkg p1, func TakesFunc(func(int) int) pkg p1, method (*B) JustOnB() pkg p1, method (*B) OnBothTandBPtr() @@ -61,6 +62,10 @@ pkg p1, var ChecksumError error pkg p1, var SIPtr *SI pkg p1, var SIPtr2 *SI pkg p1, var SIVal SI +pkg p1, var V string +pkg p1, var VError Error +pkg p1, var V1 uint64 +pkg p1, var V2 p2.Twoer pkg p1, var X I pkg p1, var X int64 pkg p1, var Y int diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go index 5f51da64a3..9d2afa913e 100644 --- a/src/cmd/api/testdata/src/pkg/p1/p1.go +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -19,7 +19,15 @@ const ( ConversionConst = MyInt(5) ) -var ChecksumError = errors.New("gzip checksum error") +// Variables from function calls. +var ( + V = ptwo.F() + VError = BarE() + V1 = Bar1(1, 2, 3) + V2 = ptwo.G() +) + +var ChecksumError = ptwo.NewError("gzip checksum error") const B = 2 const StrConst = "foo" @@ -87,6 +95,7 @@ func (s *S2) unexported(x int8, y int16, z int64) {} func Bar(x int8, y int16, z int64) {} func Bar1(x int8, y int16, z int64) uint64 {} func Bar2(x int8, y int16, z int64) (uint8, uint64) {} +func BarE() Error {} func unexported(x int8, y int16, z int64) {} diff --git a/src/cmd/api/testdata/src/pkg/p2/golden.txt b/src/cmd/api/testdata/src/pkg/p2/golden.txt index 111427b4ab..4271620c74 100644 --- a/src/cmd/api/testdata/src/pkg/p2/golden.txt +++ b/src/cmd/api/testdata/src/pkg/p2/golden.txt @@ -1,2 +1,5 @@ +pkg p2, func F() string +pkg p2, func G() Twoer +pkg p2, func NewError(string) error pkg p2, type Twoer interface { PackageTwoMeth } pkg p2, type Twoer interface, PackageTwoMeth() diff --git a/src/cmd/api/testdata/src/pkg/p2/p2.go b/src/cmd/api/testdata/src/pkg/p2/p2.go index 695d08da6a..6b107b5079 100644 --- a/src/cmd/api/testdata/src/pkg/p2/p2.go +++ b/src/cmd/api/testdata/src/pkg/p2/p2.go @@ -3,3 +3,7 @@ package p2 type Twoer interface { PackageTwoMeth() } + +func F() string {} +func G() Twoer {} +func NewError(s string) error {}