mirror of
https://github.com/golang/go
synced 2024-11-22 09:04:42 -07:00
cmd/api: follow constant references
For gccgo. Also removes bunch of special cases. Fixes #2906 R=golang-dev, remyoudompheng CC=golang-dev https://golang.org/cl/5644050
This commit is contained in:
parent
4539d1f307
commit
c15a42ed76
@ -3,6 +3,11 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Api computes the exported API of a set of Go packages.
|
// 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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -167,7 +172,8 @@ type Walker struct {
|
|||||||
lastConstType string
|
lastConstType string
|
||||||
curPackageName string
|
curPackageName string
|
||||||
curPackage *ast.Package
|
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
|
packageState map[string]loadState
|
||||||
interfaces map[pkgSymbol]*ast.InterfaceType
|
interfaces map[pkgSymbol]*ast.InterfaceType
|
||||||
selectorFullPkg map[string]string // "http" => "net/http", updated by imports
|
selectorFullPkg map[string]string // "http" => "net/http", updated by imports
|
||||||
@ -182,6 +188,7 @@ func NewWalker() *Walker {
|
|||||||
interfaces: make(map[pkgSymbol]*ast.InterfaceType),
|
interfaces: make(map[pkgSymbol]*ast.InterfaceType),
|
||||||
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,34 +206,10 @@ const (
|
|||||||
// the cases we can't handle yet.
|
// the cases we can't handle yet.
|
||||||
func (w *Walker) hardCodedConstantType(name string) (typ string, ok bool) {
|
func (w *Walker) hardCodedConstantType(name string) (typ string, ok bool) {
|
||||||
switch w.scope[0] {
|
switch w.scope[0] {
|
||||||
case "pkg compress/gzip", "pkg compress/zlib":
|
case "pkg syscall":
|
||||||
switch name {
|
switch name {
|
||||||
case "NoCompression", "BestSpeed", "BestCompression", "DefaultCompression":
|
case "darwinAMD64":
|
||||||
return "ideal-int", true
|
return "ideal-bool", 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
@ -306,7 +289,7 @@ func (w *Walker) WalkPackage(name string) {
|
|||||||
|
|
||||||
w.curPackageName = name
|
w.curPackageName = name
|
||||||
w.curPackage = apkg
|
w.curPackage = apkg
|
||||||
w.prevConstType = map[string]string{}
|
w.constDep = map[string]string{}
|
||||||
|
|
||||||
for _, afile := range apkg.Files {
|
for _, afile := range apkg.Files {
|
||||||
w.recordTypes(afile)
|
w.recordTypes(afile)
|
||||||
@ -316,6 +299,8 @@ func (w *Walker) WalkPackage(name string) {
|
|||||||
w.walkFile(afile)
|
w.walkFile(afile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.resolveConstantDeps()
|
||||||
|
|
||||||
// Now that we're done walking types, vars and consts
|
// Now that we're done walking types, vars and consts
|
||||||
// in the *ast.Package, use go/doc to do the rest
|
// in the *ast.Package, use go/doc to do the rest
|
||||||
// (functions and methods). This is done here because
|
// (functions and methods). This is done here because
|
||||||
@ -447,8 +432,16 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
|
|||||||
case *ast.UnaryExpr:
|
case *ast.UnaryExpr:
|
||||||
return w.constValueType(v.X)
|
return w.constValueType(v.X)
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
// e.g. compress/gzip's BestSpeed == flate.BestSpeed
|
lhs := w.nodeString(v.X)
|
||||||
return "", errTODO
|
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:
|
case *ast.Ident:
|
||||||
if v.Name == "iota" {
|
if v.Name == "iota" {
|
||||||
return "ideal-int", nil // hack.
|
return "ideal-int", nil // hack.
|
||||||
@ -460,10 +453,10 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
|
|||||||
// Hack.
|
// Hack.
|
||||||
return "ideal-int", nil
|
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 t, nil
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("can't resolve existing constant %q", v.Name)
|
return constDepPrefix + v.Name, nil
|
||||||
case *ast.BinaryExpr:
|
case *ast.BinaryExpr:
|
||||||
left, err := w.constValueType(v.X)
|
left, err := w.constValueType(v.X)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -474,6 +467,8 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if left != right {
|
if left != right {
|
||||||
|
// TODO(bradfitz): encode the real rules here,
|
||||||
|
// rather than this mess.
|
||||||
if left == "ideal-int" && right == "ideal-float" {
|
if left == "ideal-int" && right == "ideal-float" {
|
||||||
return "ideal-float", nil // math.Log2E
|
return "ideal-float", nil // math.Log2E
|
||||||
}
|
}
|
||||||
@ -487,6 +482,17 @@ func (w *Walker) constValueType(vi interface{}) (string, error) {
|
|||||||
// Hack, for package time.
|
// Hack, for package time.
|
||||||
return "Duration", nil
|
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 "", fmt.Errorf("in BinaryExpr, unhandled type mismatch; left=%q, right=%q", left, right)
|
||||||
}
|
}
|
||||||
return left, nil
|
return left, nil
|
||||||
@ -601,11 +607,13 @@ func (w *Walker) resolveName(name string) (v interface{}, t interface{}, ok bool
|
|||||||
return nil, nil, false
|
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) {
|
func (w *Walker) walkConst(vs *ast.ValueSpec) {
|
||||||
for _, ident := range vs.Names {
|
for _, ident := range vs.Names {
|
||||||
if !ast.IsExported(ident.Name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
litType := ""
|
litType := ""
|
||||||
if vs.Type != nil {
|
if vs.Type != nil {
|
||||||
litType = w.nodeString(vs.Type)
|
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 == "" {
|
if litType == "" {
|
||||||
log.Fatalf("unknown kind in const %q", ident.Name)
|
log.Fatalf("unknown kind in const %q", ident.Name)
|
||||||
}
|
}
|
||||||
w.lastConstType = litType
|
w.lastConstType = litType
|
||||||
|
|
||||||
w.emitFeature(fmt.Sprintf("const %s %s", ident, litType))
|
w.prevConstType[pkgSymbol{w.curPackageName, ident.Name}] = litType
|
||||||
w.prevConstType[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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
src/cmd/api/testdata/src/pkg/p1/golden.txt
vendored
2
src/cmd/api/testdata/src/pkg/p1/golden.txt
vendored
@ -1,6 +1,8 @@
|
|||||||
pkg p1, const A ideal-int
|
pkg p1, const A ideal-int
|
||||||
pkg p1, const A64 int64
|
pkg p1, const A64 int64
|
||||||
|
pkg p1, const AIsLowerA ideal-int
|
||||||
pkg p1, const B ideal-int
|
pkg p1, const B ideal-int
|
||||||
|
pkg p1, const ConstChase2 ideal-int
|
||||||
pkg p1, const ConversionConst MyInt
|
pkg p1, const ConversionConst MyInt
|
||||||
pkg p1, const FloatConst ideal-float
|
pkg p1, const FloatConst ideal-float
|
||||||
pkg p1, const StrConst ideal-string
|
pkg p1, const StrConst ideal-string
|
||||||
|
5
src/cmd/api/testdata/src/pkg/p1/p1.go
vendored
5
src/cmd/api/testdata/src/pkg/p1/p1.go
vendored
@ -5,9 +5,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
ConstChase2 = constChase // forward declaration to unexported ident
|
||||||
|
constChase = AIsLowerA // forward declaration to exported ident
|
||||||
|
|
||||||
A = 1
|
A = 1
|
||||||
a = 11
|
a = 11
|
||||||
A64 int64 = 1
|
A64 int64 = 1
|
||||||
|
|
||||||
|
AIsLowerA = a // previously declared
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
Loading…
Reference in New Issue
Block a user