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 {
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
......
......@@ -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
......@@ -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) {}
......
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()
......@@ -3,3 +3,7 @@ package p2
type Twoer interface {
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