]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/api: follow constant references
authorBrad Fitzpatrick <bradfitz@golang.org>
Thu, 9 Feb 2012 23:05:26 +0000 (10:05 +1100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 9 Feb 2012 23:05:26 +0000 (10:05 +1100)
For gccgo. Also removes bunch of special cases.

Fixes #2906

R=golang-dev, remyoudompheng
CC=golang-dev
https://golang.org/cl/5644050

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

index ef1b8c5d938d2676f13c59307e3a054896187095..62ab606c65b9cbc0aa1619e8aba09c08d4b93fcc 100644 (file)
@@ -3,6 +3,11 @@
 // license that can be found in the LICENSE file.
 
 // Api computes the exported API of a set of Go packages.
+//
+// BUG(bradfitz): Note that this tool is only currently suitable
+// for use on the Go standard library, not arbitrary packages.
+// Once the Go AST has type information, this tool will be more
+// reliable without hard-coded hacks throughout.
 package main
 
 import (
@@ -167,7 +172,8 @@ type Walker struct {
        lastConstType   string
        curPackageName  string
        curPackage      *ast.Package
-       prevConstType   map[string]string // identifier -> "ideal-int"
+       prevConstType   map[pkgSymbol]string
+       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
@@ -182,6 +188,7 @@ func NewWalker() *Walker {
                interfaces:      make(map[pkgSymbol]*ast.InterfaceType),
                selectorFullPkg: make(map[string]string),
                wantedPkg:       make(map[string]bool),
+               prevConstType:   make(map[pkgSymbol]string),
        }
 }
 
@@ -199,34 +206,10 @@ const (
 // the cases we can't handle yet.
 func (w *Walker) hardCodedConstantType(name string) (typ string, ok bool) {
        switch w.scope[0] {
-       case "pkg compress/gzip", "pkg compress/zlib":
+       case "pkg syscall":
                switch name {
-               case "NoCompression", "BestSpeed", "BestCompression", "DefaultCompression":
-                       return "ideal-int", true
-               }
-       case "pkg os":
-               switch name {
-               case "WNOHANG", "WSTOPPED", "WUNTRACED":
-                       return "ideal-int", true
-               }
-       case "pkg path/filepath":
-               switch name {
-               case "Separator", "ListSeparator":
-                       return "char", true
-               }
-       case "pkg unicode/utf8":
-               switch name {
-               case "RuneError":
-                       return "char", true
-               }
-       case "pkg text/scanner":
-               // TODO: currently this tool only resolves const types
-               // that reference other constant types if they appear
-               // in the right order.  the scanner package has
-               // ScanIdents and such coming before the Ident/Int/etc
-               // tokens, hence this hack.
-               if strings.HasPrefix(name, "Scan") || name == "SkipComments" {
-                       return "ideal-int", true
+               case "darwinAMD64":
+                       return "ideal-bool", true
                }
        }
        return "", false
@@ -306,7 +289,7 @@ func (w *Walker) WalkPackage(name string) {
 
        w.curPackageName = name
        w.curPackage = apkg
-       w.prevConstType = map[string]string{}
+       w.constDep = map[string]string{}
 
        for _, afile := range apkg.Files {
                w.recordTypes(afile)
@@ -316,6 +299,8 @@ func (w *Walker) WalkPackage(name string) {
                w.walkFile(afile)
        }
 
+       w.resolveConstantDeps()
+
        // Now that we're done walking types, vars and consts
        // in the *ast.Package, use go/doc to do the rest
        // (functions and methods). This is done here because
@@ -447,8 +432,16 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
        case *ast.UnaryExpr:
                return w.constValueType(v.X)
        case *ast.SelectorExpr:
-               // e.g. compress/gzip's BestSpeed == flate.BestSpeed
-               return "", errTODO
+               lhs := w.nodeString(v.X)
+               rhs := w.nodeString(v.Sel)
+               pkg, ok := w.selectorFullPkg[lhs]
+               if !ok {
+                       return "", fmt.Errorf("unknown constant reference; unknown package in expression %s.%s", lhs, rhs)
+               }
+               if t, ok := w.prevConstType[pkgSymbol{pkg, rhs}]; ok {
+                       return t, nil
+               }
+               return "", fmt.Errorf("unknown constant reference to %s.%s", lhs, rhs)
        case *ast.Ident:
                if v.Name == "iota" {
                        return "ideal-int", nil // hack.
@@ -460,10 +453,10 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
                        // Hack.
                        return "ideal-int", nil
                }
-               if t, ok := w.prevConstType[v.Name]; ok {
+               if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, v.Name}]; ok {
                        return t, nil
                }
-               return "", fmt.Errorf("can't resolve existing constant %q", v.Name)
+               return constDepPrefix + v.Name, nil
        case *ast.BinaryExpr:
                left, err := w.constValueType(v.X)
                if err != nil {
@@ -474,6 +467,8 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
                        return "", err
                }
                if left != right {
+                       // TODO(bradfitz): encode the real rules here,
+                       // rather than this mess.
                        if left == "ideal-int" && right == "ideal-float" {
                                return "ideal-float", nil // math.Log2E
                        }
@@ -487,6 +482,17 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
                                // Hack, for package time.
                                return "Duration", nil
                        }
+                       if left == "ideal-int" && !strings.HasPrefix(right, "ideal-") {
+                               return right, nil
+                       }
+                       if right == "ideal-int" && !strings.HasPrefix(left, "ideal-") {
+                               return left, nil
+                       }
+                       if strings.HasPrefix(left, constDepPrefix) && strings.HasPrefix(right, constDepPrefix) {
+                               // Just pick one.
+                               // e.g. text/scanner GoTokens const-dependency:ScanIdents, const-dependency:ScanFloats
+                               return left, nil
+                       }
                        return "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right)
                }
                return left, nil
@@ -601,11 +607,13 @@ func (w *Walker) resolveName(name string) (v interface{}, t interface{}, ok bool
        return nil, nil, false
 }
 
+// constDepPrefix is a magic prefix that is used by constValueType
+// and walkConst to signal that a type isn't known yet. These are
+// resolved at the end of walking of a package's files.
+const constDepPrefix = "const-dependency:"
+
 func (w *Walker) walkConst(vs *ast.ValueSpec) {
        for _, ident := range vs.Names {
-               if !ast.IsExported(ident.Name) {
-                       continue
-               }
                litType := ""
                if vs.Type != nil {
                        litType = w.nodeString(vs.Type)
@@ -627,13 +635,44 @@ func (w *Walker) walkConst(vs *ast.ValueSpec) {
                                }
                        }
                }
+               if strings.HasPrefix(litType, constDepPrefix) {
+                       dep := litType[len(constDepPrefix):]
+                       w.constDep[ident.Name] = dep
+                       continue
+               }
                if litType == "" {
                        log.Fatalf("unknown kind in const %q", ident.Name)
                }
                w.lastConstType = litType
 
-               w.emitFeature(fmt.Sprintf("const %s %s", ident, litType))
-               w.prevConstType[ident.Name] = litType
+               w.prevConstType[pkgSymbol{w.curPackageName, ident.Name}] = litType
+
+               if ast.IsExported(ident.Name) {
+                       w.emitFeature(fmt.Sprintf("const %s %s", ident, litType))
+               }
+       }
+}
+
+func (w *Walker) resolveConstantDeps() {
+       var findConstType func(string) string
+       findConstType = func(ident string) string {
+               if dep, ok := w.constDep[ident]; ok {
+                       return findConstType(dep)
+               }
+               if t, ok := w.prevConstType[pkgSymbol{w.curPackageName, ident}]; ok {
+                       return t
+               }
+               return ""
+       }
+       for ident := range w.constDep {
+               if !ast.IsExported(ident) {
+                       continue
+               }
+               t := findConstType(ident)
+               if t == "" {
+                       log.Fatalf("failed to resolve constant %q", ident)
+               }
+               w.emitFeature(fmt.Sprintf("const %s %s", ident, t))
        }
 }
 
index 2693240a795ec5def0789fa4f3a2b55049783720..7c6d8c8a85f724402db5e86727fe6b85cad3771b 100644 (file)
@@ -1,6 +1,8 @@
 pkg p1, const A ideal-int
 pkg p1, const A64 int64
+pkg p1, const AIsLowerA ideal-int
 pkg p1, const B ideal-int
+pkg p1, const ConstChase2 ideal-int
 pkg p1, const ConversionConst MyInt
 pkg p1, const FloatConst ideal-float
 pkg p1, const StrConst ideal-string
index 3829686e6b23887dc1a1c2b1e81237c533dc3803..5f51da64a333a0d2f625b49b1ec09a620f1b27c0 100644 (file)
@@ -5,9 +5,14 @@ import (
 )
 
 const (
+       ConstChase2 = constChase // forward declaration to unexported ident
+       constChase  = AIsLowerA  // forward declaration to exported ident
+
        A         = 1
        a         = 11
        A64 int64 = 1
+
+       AIsLowerA = a // previously declared
 )
 
 const (