Commit d75023e1 authored by Rémy Oudompheng's avatar Rémy Oudompheng

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
parent 92755f38
...@@ -176,8 +176,9 @@ type Walker struct { ...@@ -176,8 +176,9 @@ type Walker struct {
constDep map[string]string // key's const identifier has type of future value const identifier constDep map[string]string // key's const identifier has type of future value const identifier
packageState map[string]loadState packageState map[string]loadState
interfaces map[pkgSymbol]*ast.InterfaceType interfaces map[pkgSymbol]*ast.InterfaceType
selectorFullPkg map[string]string // "http" => "net/http", updated by imports functionTypes map[pkgSymbol]string // symbol => return type
wantedPkg map[string]bool // packages requested on the command line selectorFullPkg map[string]string // "http" => "net/http", updated by imports
wantedPkg map[string]bool // packages requested on the command line
} }
func NewWalker() *Walker { func NewWalker() *Walker {
...@@ -186,6 +187,7 @@ func NewWalker() *Walker { ...@@ -186,6 +187,7 @@ func NewWalker() *Walker {
features: make(map[string]bool), features: make(map[string]bool),
packageState: make(map[string]loadState), packageState: make(map[string]loadState),
interfaces: make(map[pkgSymbol]*ast.InterfaceType), interfaces: make(map[pkgSymbol]*ast.InterfaceType),
functionTypes: make(map[pkgSymbol]string),
selectorFullPkg: make(map[string]string), selectorFullPkg: make(map[string]string),
wantedPkg: make(map[string]bool), wantedPkg: make(map[string]bool),
prevConstType: make(map[pkgSymbol]string), prevConstType: make(map[pkgSymbol]string),
...@@ -295,6 +297,15 @@ func (w *Walker) WalkPackage(name string) { ...@@ -295,6 +297,15 @@ func (w *Walker) WalkPackage(name string) {
w.recordTypes(afile) 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 { for _, afile := range apkg.Files {
w.walkFile(afile) w.walkFile(afile)
} }
...@@ -360,7 +371,6 @@ func (w *Walker) recordTypes(file *ast.File) { ...@@ -360,7 +371,6 @@ func (w *Walker) recordTypes(file *ast.File) {
func (w *Walker) walkFile(file *ast.File) { func (w *Walker) walkFile(file *ast.File) {
// Not entering a scope here; file boundaries aren't interesting. // Not entering a scope here; file boundaries aren't interesting.
for _, di := range file.Decls { for _, di := range file.Decls {
switch d := di.(type) { switch d := di.(type) {
case *ast.GenDecl: case *ast.GenDecl:
...@@ -506,11 +516,6 @@ func (w *Walker) constValueType(vi interface{}) (string, error) { ...@@ -506,11 +516,6 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
} }
func (w *Walker) varValueType(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) { switch v := vi.(type) {
case *ast.BasicLit: case *ast.BasicLit:
litType, ok := varType[v.Kind] litType, ok := varType[v.Kind]
...@@ -552,17 +557,30 @@ func (w *Walker) varValueType(vi interface{}) (string, error) { ...@@ -552,17 +557,30 @@ func (w *Walker) varValueType(vi interface{}) (string, error) {
case *ast.ParenExpr: case *ast.ParenExpr:
return w.varValueType(v.X) return w.varValueType(v.X)
case *ast.CallExpr: case *ast.CallExpr:
funStr := w.nodeString(v.Fun) var funSym pkgSymbol
node, _, ok := w.resolveName(funStr) if selnode, ok := v.Fun.(*ast.SelectorExpr); ok {
if !ok { // assume it is not a method.
return "", fmt.Errorf("unresolved named %q", funStr) pkg, ok := w.selectorFullPkg[w.nodeString(selnode.X)]
} if !ok {
if funcd, ok := node.(*ast.FuncDecl); ok { return "", fmt.Errorf("not a package: %s", w.nodeString(selnode.X))
// Assume at the top level that all functions have exactly 1 result }
return w.nodeString(w.namelessType(funcd.Type.Results.List[0].Type)), nil 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. // 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: default:
return "", fmt.Errorf("unknown const value type %T", vi) 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 ...@@ -575,19 +593,8 @@ func (w *Walker) resolveName(name string) (v interface{}, t interface{}, ok bool
for _, file := range w.curPackage.Files { for _, file := range w.curPackage.Files {
for _, di := range file.Decls { for _, di := range file.Decls {
switch d := di.(type) { switch d := di.(type) {
case *ast.FuncDecl:
if d.Name.Name == name {
return d, d.Type, true
}
case *ast.GenDecl: case *ast.GenDecl:
switch d.Tok { 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: case token.VAR:
for _, sp := range d.Specs { for _, sp := range d.Specs {
vs := sp.(*ast.ValueSpec) vs := sp.(*ast.ValueSpec)
...@@ -853,6 +860,17 @@ func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) { ...@@ -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) { func (w *Walker) walkFuncDecl(f *ast.FuncDecl) {
if !ast.IsExported(f.Name.Name) { if !ast.IsExported(f.Name.Name) {
return return
......
...@@ -9,6 +9,7 @@ pkg p1, const StrConst ideal-string ...@@ -9,6 +9,7 @@ pkg p1, const StrConst ideal-string
pkg p1, func Bar(int8, int16, int64) pkg p1, func Bar(int8, int16, int64)
pkg p1, func Bar1(int8, int16, int64) uint64 pkg p1, func Bar1(int8, int16, int64) uint64
pkg p1, func Bar2(int8, int16, int64) (uint8, uint64) pkg p1, func Bar2(int8, int16, int64) (uint8, uint64)
pkg p1, func BarE() Error
pkg p1, func TakesFunc(func(int) int) pkg p1, func TakesFunc(func(int) int)
pkg p1, method (*B) JustOnB() pkg p1, method (*B) JustOnB()
pkg p1, method (*B) OnBothTandBPtr() pkg p1, method (*B) OnBothTandBPtr()
...@@ -61,6 +62,10 @@ pkg p1, var ChecksumError error ...@@ -61,6 +62,10 @@ pkg p1, var ChecksumError error
pkg p1, var SIPtr *SI pkg p1, var SIPtr *SI
pkg p1, var SIPtr2 *SI pkg p1, var SIPtr2 *SI
pkg p1, var SIVal 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 I
pkg p1, var X int64 pkg p1, var X int64
pkg p1, var Y int pkg p1, var Y int
...@@ -19,7 +19,15 @@ const ( ...@@ -19,7 +19,15 @@ const (
ConversionConst = MyInt(5) 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 B = 2
const StrConst = "foo" const StrConst = "foo"
...@@ -87,6 +95,7 @@ func (s *S2) unexported(x int8, y int16, z int64) {} ...@@ -87,6 +95,7 @@ func (s *S2) unexported(x int8, y int16, z int64) {}
func Bar(x int8, y int16, z int64) {} func Bar(x int8, y int16, z int64) {}
func Bar1(x int8, y int16, z int64) uint64 {} func Bar1(x int8, y int16, z int64) uint64 {}
func Bar2(x int8, y int16, z int64) (uint8, uint64) {} func Bar2(x int8, y int16, z int64) (uint8, uint64) {}
func BarE() Error {}
func unexported(x int8, y int16, z int64) {} func unexported(x int8, y int16, z int64) {}
......
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 }
pkg p2, type Twoer interface, PackageTwoMeth() pkg p2, type Twoer interface, PackageTwoMeth()
...@@ -3,3 +3,7 @@ package p2 ...@@ -3,3 +3,7 @@ package p2
type Twoer interface { type Twoer interface {
PackageTwoMeth() PackageTwoMeth()
} }
func F() string {}
func G() Twoer {}
func NewError(s string) error {}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment