aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/api
diff options
context:
space:
mode:
authorRémy Oudompheng <oudomphe@phare.normalesup.org>2012-02-21 07:37:25 +0100
committerRémy Oudompheng <oudomphe@phare.normalesup.org>2012-02-21 07:37:25 +0100
commitd75023e1d144793dcf83ba45c3857656134c4fa0 (patch)
tree8f81755fdf0cfe5672f4c051bdf166b164bf70dc /src/cmd/api
parent92755f38ae86b7e2e476392a0d7be987f69f6b61 (diff)
downloadgo-d75023e1d144793dcf83ba45c3857656134c4fa0.tar.xz
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
Diffstat (limited to 'src/cmd/api')
-rw-r--r--src/cmd/api/goapi.go74
-rw-r--r--src/cmd/api/testdata/src/pkg/p1/golden.txt5
-rw-r--r--src/cmd/api/testdata/src/pkg/p1/p1.go11
-rw-r--r--src/cmd/api/testdata/src/pkg/p2/golden.txt3
-rw-r--r--src/cmd/api/testdata/src/pkg/p2/p2.go4
5 files changed, 68 insertions, 29 deletions
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 {}