]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/api: record return type of functions for variable typecheck.
authorRémy Oudompheng <oudomphe@phare.normalesup.org>
Tue, 21 Feb 2012 06:37:25 +0000 (07:37 +0100)
committerRémy Oudompheng <oudomphe@phare.normalesup.org>
Tue, 21 Feb 2012 06:37:25 +0000 (07:37 +0100)
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

src/cmd/api/goapi.go
src/cmd/api/testdata/src/pkg/p1/golden.txt
src/cmd/api/testdata/src/pkg/p1/p1.go
src/cmd/api/testdata/src/pkg/p2/golden.txt
src/cmd/api/testdata/src/pkg/p2/p2.go

index 6f484e29b726d4d09b59e8defa015aacac599250..ee0f92328e311921280d625fd51c71b44a068491 100644 (file)
@@ -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
index 7c6d8c8a85f724402db5e86727fe6b85cad3771b..3a1b3f5350d73482610a85bbbd87775d35673321 100644 (file)
@@ -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
index 5f51da64a333a0d2f625b49b1ec09a620f1b27c0..9d2afa913e42d152cbbf393401fbf885f784286f 100644 (file)
@@ -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) {}
 
index 111427b4ab40b2440bad2b7bb2418c359254f0e3..4271620c74848bb593b6f8dfd1e11ecfda830849 100644 (file)
@@ -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()
index 695d08da6af56bc4a75d51413452abbbfc7c5f20..6b107b50790459a497fc34ec52567dd7e419333c 100644 (file)
@@ -3,3 +3,7 @@ package p2
 type Twoer interface {
        PackageTwoMeth()
 }
+
+func F() string               {}
+func G() Twoer                {}
+func NewError(s string) error {}