mirror of
https://github.com/golang/go
synced 2024-11-18 13:04:46 -07:00
go.tools/cmd/godex: nicer formatting
- handle indentation - write non-empty tuples and signatures in multiple lines - don't crash for package unsafe LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/79920044
This commit is contained in:
parent
4a27ee3a1b
commit
8648e1a481
@ -14,6 +14,8 @@ import (
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// BUG(gri) cannot specify package paths with dots (code.google.com/p/go.tools/cmd/ssadump)
|
||||
|
||||
var (
|
||||
source = flag.String("s", "", "only consider packages from this source")
|
||||
verbose = flag.Bool("v", false, "verbose mode")
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// TODO(gri) handle indentation
|
||||
// TODO(gri) filter unexported fields of struct types?
|
||||
// TODO(gri) use tabwriter for alignment?
|
||||
|
||||
@ -26,25 +25,40 @@ func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
|
||||
type printer struct {
|
||||
pkg *types.Package
|
||||
buf bytes.Buffer
|
||||
indent int
|
||||
indent int // current indentation level
|
||||
last byte // last byte written
|
||||
}
|
||||
|
||||
func (p *printer) print(s string) {
|
||||
p.buf.WriteString(s)
|
||||
// Write the string one byte at a time. We care about the presence of
|
||||
// newlines for indentation which we will see even in the presence of
|
||||
// (non-corrupted) Unicode; no need to read one rune at a time.
|
||||
for i := 0; i < len(s); i++ {
|
||||
ch := s[i]
|
||||
if ch != '\n' && p.last == '\n' {
|
||||
// Note: This could lead to a range overflow for very large
|
||||
// indentations, but it's extremely unlikely to happen for
|
||||
// non-pathological code.
|
||||
p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent])
|
||||
}
|
||||
p.buf.WriteByte(ch)
|
||||
p.last = ch
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) printf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(&p.buf, format, args...)
|
||||
p.print(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
|
||||
// collect objects by kind
|
||||
var (
|
||||
consts []*types.Const
|
||||
typez []*types.TypeName // types without methods
|
||||
typem []*types.TypeName // types with methods
|
||||
vars []*types.Var
|
||||
funcs []*types.Func
|
||||
consts []*types.Const
|
||||
typez []*types.TypeName // types without methods
|
||||
typem []*types.TypeName // types with methods
|
||||
vars []*types.Var
|
||||
funcs []*types.Func
|
||||
builtins []*types.Builtin
|
||||
)
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
@ -56,7 +70,7 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
|
||||
case *types.Const:
|
||||
consts = append(consts, obj)
|
||||
case *types.TypeName:
|
||||
if obj.Type().(*types.Named).NumMethods() > 0 {
|
||||
if named, _ := obj.Type().(*types.Named); named != nil && named.NumMethods() > 0 {
|
||||
typem = append(typem, obj)
|
||||
} else {
|
||||
typez = append(typez, obj)
|
||||
@ -65,6 +79,9 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
|
||||
vars = append(vars, obj)
|
||||
case *types.Func:
|
||||
funcs = append(funcs, obj)
|
||||
case *types.Builtin:
|
||||
// for unsafe.Sizeof, etc.
|
||||
builtins = append(builtins, obj)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,36 +89,42 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
|
||||
|
||||
if len(consts) > 0 {
|
||||
p.print("const (\n")
|
||||
p.indent++
|
||||
for _, obj := range consts {
|
||||
p.printObj(obj)
|
||||
p.print("\n")
|
||||
}
|
||||
p.indent--
|
||||
p.print(")\n\n")
|
||||
}
|
||||
|
||||
if len(vars) > 0 {
|
||||
p.print("var (\n")
|
||||
p.indent++
|
||||
for _, obj := range vars {
|
||||
p.printObj(obj)
|
||||
p.print("\n")
|
||||
}
|
||||
p.indent--
|
||||
p.print(")\n\n")
|
||||
}
|
||||
|
||||
if len(typez) > 0 {
|
||||
p.print("type (\n")
|
||||
p.indent++
|
||||
for _, obj := range typez {
|
||||
p.printf("\t%s ", obj.Name())
|
||||
types.WriteType(&p.buf, p.pkg, obj.Type().Underlying())
|
||||
p.printf("%s ", obj.Name())
|
||||
p.writeType(p.pkg, obj.Type().Underlying())
|
||||
p.print("\n")
|
||||
}
|
||||
p.indent--
|
||||
p.print(")\n\n")
|
||||
}
|
||||
|
||||
for _, obj := range typem {
|
||||
p.printf("type %s ", obj.Name())
|
||||
typ := obj.Type().(*types.Named)
|
||||
types.WriteType(&p.buf, p.pkg, typ.Underlying())
|
||||
p.writeType(p.pkg, typ.Underlying())
|
||||
p.print("\n")
|
||||
for i, n := 0, typ.NumMethods(); i < n; i++ {
|
||||
p.printFunc(typ.Method(i))
|
||||
@ -115,15 +138,20 @@ func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) boo
|
||||
p.print("\n")
|
||||
}
|
||||
|
||||
// TODO(gri) better handling of builtins (package unsafe only)
|
||||
for _, obj := range builtins {
|
||||
p.printf("func %s() // builtin\n", obj.Name())
|
||||
}
|
||||
|
||||
p.print("\n")
|
||||
}
|
||||
|
||||
func (p *printer) printObj(obj types.Object) {
|
||||
p.printf("\t %s", obj.Name())
|
||||
p.printf("%s", obj.Name())
|
||||
// don't write untyped types (for constants)
|
||||
if typ := obj.Type(); typed(typ) {
|
||||
p.print(" ")
|
||||
types.WriteType(&p.buf, p.pkg, typ)
|
||||
p.writeType(p.pkg, typ)
|
||||
}
|
||||
// write constant value
|
||||
if obj, ok := obj.(*types.Const); ok {
|
||||
@ -140,11 +168,11 @@ func (p *printer) printFunc(obj *types.Func) {
|
||||
p.print(name)
|
||||
p.print(" ")
|
||||
}
|
||||
types.WriteType(&p.buf, p.pkg, recv.Type())
|
||||
p.writeType(p.pkg, recv.Type())
|
||||
p.print(") ")
|
||||
}
|
||||
p.print(obj.Name())
|
||||
types.WriteSignature(&p.buf, p.pkg, sig)
|
||||
p.writeSignature(p.pkg, sig)
|
||||
}
|
||||
|
||||
func typed(typ types.Type) bool {
|
||||
|
246
cmd/godex/writetype.go
Normal file
246
cmd/godex/writetype.go
Normal file
@ -0,0 +1,246 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// This file implements writing of types. The functionlity is lifted
|
||||
// directly from go/types, but now contains various modifications for
|
||||
// nicer output.
|
||||
//
|
||||
// TODO(gri) back-port once we have a fixed interface and once the
|
||||
// go/types API is not frozen anymore for the 1.3 release; and remove
|
||||
// this implementation if possible.
|
||||
|
||||
func (p *printer) writeType(this *types.Package, typ types.Type) {
|
||||
p.writeTypeInternal(this, typ, make([]types.Type, 8))
|
||||
}
|
||||
|
||||
// From go/types - leave for now to ease back-porting this code.
|
||||
const GcCompatibilityMode = false
|
||||
|
||||
func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) {
|
||||
// Theoretically, this is a quadratic lookup algorithm, but in
|
||||
// practice deeply nested composite types with unnamed component
|
||||
// types are uncommon. This code is likely more efficient than
|
||||
// using a map.
|
||||
for _, t := range visited {
|
||||
if t == typ {
|
||||
p.printf("○%T", typ) // cycle to typ
|
||||
return
|
||||
}
|
||||
}
|
||||
visited = append(visited, typ)
|
||||
|
||||
switch t := typ.(type) {
|
||||
case nil:
|
||||
p.print("<nil>")
|
||||
|
||||
case *types.Basic:
|
||||
if t.Kind() == types.UnsafePointer {
|
||||
p.print("unsafe.")
|
||||
}
|
||||
if GcCompatibilityMode {
|
||||
// forget the alias names
|
||||
switch t.Kind() {
|
||||
case types.Byte:
|
||||
t = types.Typ[types.Uint8]
|
||||
case types.Rune:
|
||||
t = types.Typ[types.Int32]
|
||||
}
|
||||
}
|
||||
p.print(t.Name())
|
||||
|
||||
case *types.Array:
|
||||
p.printf("[%d]", t.Len())
|
||||
p.writeTypeInternal(this, t.Elem(), visited)
|
||||
|
||||
case *types.Slice:
|
||||
p.print("[]")
|
||||
p.writeTypeInternal(this, t.Elem(), visited)
|
||||
|
||||
case *types.Struct:
|
||||
// TODO(gri) filter fields?
|
||||
n := t.NumFields()
|
||||
if n == 0 {
|
||||
p.print("struct{}")
|
||||
return
|
||||
}
|
||||
|
||||
p.print("struct {\n")
|
||||
p.indent++
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.Field(i)
|
||||
if !f.Anonymous() {
|
||||
p.printf("%s ", f.Name())
|
||||
}
|
||||
p.writeTypeInternal(this, f.Type(), visited)
|
||||
if tag := t.Tag(i); tag != "" {
|
||||
p.printf(" %q", tag)
|
||||
}
|
||||
p.print("\n")
|
||||
}
|
||||
p.indent--
|
||||
p.print("}")
|
||||
|
||||
case *types.Pointer:
|
||||
p.print("*")
|
||||
p.writeTypeInternal(this, t.Elem(), visited)
|
||||
|
||||
case *types.Tuple:
|
||||
p.writeTuple(this, t, false, visited)
|
||||
|
||||
case *types.Signature:
|
||||
p.print("func")
|
||||
p.writeSignatureInternal(this, t, visited)
|
||||
|
||||
case *types.Interface:
|
||||
// We write the source-level methods and embedded types rather
|
||||
// than the actual method set since resolved method signatures
|
||||
// may have non-printable cycles if parameters have anonymous
|
||||
// interface types that (directly or indirectly) embed the
|
||||
// current interface. For instance, consider the result type
|
||||
// of m:
|
||||
//
|
||||
// type T interface{
|
||||
// m() interface{ T }
|
||||
// }
|
||||
//
|
||||
// TODO(gri) filter methods?
|
||||
n := t.NumMethods()
|
||||
if n == 0 {
|
||||
p.print("interface{}")
|
||||
return
|
||||
}
|
||||
|
||||
p.print("interface {\n")
|
||||
p.indent++
|
||||
if GcCompatibilityMode {
|
||||
// print flattened interface
|
||||
// (useful to compare against gc-generated interfaces)
|
||||
for i := 0; i < n; i++ {
|
||||
m := t.Method(i)
|
||||
p.print(m.Name())
|
||||
p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
|
||||
p.print("\n")
|
||||
}
|
||||
} else {
|
||||
// print explicit interface methods and embedded types
|
||||
for i, n := 0, t.NumExplicitMethods(); i < n; i++ {
|
||||
m := t.ExplicitMethod(i)
|
||||
p.print(m.Name())
|
||||
p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
|
||||
p.print("\n")
|
||||
}
|
||||
for i, n := 0, t.NumEmbeddeds(); i < n; i++ {
|
||||
typ := t.Embedded(i)
|
||||
p.writeTypeInternal(this, typ, visited)
|
||||
p.print("\n")
|
||||
}
|
||||
}
|
||||
p.indent--
|
||||
p.print("}")
|
||||
|
||||
case *types.Map:
|
||||
p.print("map[")
|
||||
p.writeTypeInternal(this, t.Key(), visited)
|
||||
p.print("]")
|
||||
p.writeTypeInternal(this, t.Elem(), visited)
|
||||
|
||||
case *types.Chan:
|
||||
var s string
|
||||
var parens bool
|
||||
switch t.Dir() {
|
||||
case types.SendRecv:
|
||||
s = "chan "
|
||||
// chan (<-chan T) requires parentheses
|
||||
if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly {
|
||||
parens = true
|
||||
}
|
||||
case types.SendOnly:
|
||||
s = "chan<- "
|
||||
case types.RecvOnly:
|
||||
s = "<-chan "
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
p.print(s)
|
||||
if parens {
|
||||
p.print("(")
|
||||
}
|
||||
p.writeTypeInternal(this, t.Elem(), visited)
|
||||
if parens {
|
||||
p.print(")")
|
||||
}
|
||||
|
||||
case *types.Named:
|
||||
s := "<Named w/o object>"
|
||||
if obj := t.Obj(); obj != nil {
|
||||
if pkg := obj.Pkg(); pkg != nil {
|
||||
if pkg != this {
|
||||
p.print(pkg.Path())
|
||||
p.print(".")
|
||||
}
|
||||
// TODO(gri): function-local named types should be displayed
|
||||
// differently from named types at package level to avoid
|
||||
// ambiguity.
|
||||
}
|
||||
s = obj.Name()
|
||||
}
|
||||
p.print(s)
|
||||
|
||||
default:
|
||||
// For externally defined implementations of Type.
|
||||
p.print(t.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) {
|
||||
p.print("(")
|
||||
for i, n := 0, tup.Len(); i < n; i++ {
|
||||
if i > 0 {
|
||||
p.print(", ")
|
||||
}
|
||||
v := tup.At(i)
|
||||
if name := v.Name(); name != "" {
|
||||
p.print(name)
|
||||
p.print(" ")
|
||||
}
|
||||
typ := v.Type()
|
||||
if variadic && i == n-1 {
|
||||
p.print("...")
|
||||
typ = typ.(*types.Slice).Elem()
|
||||
}
|
||||
p.writeTypeInternal(this, typ, visited)
|
||||
}
|
||||
p.print(")")
|
||||
}
|
||||
|
||||
func (p *printer) writeSignature(this *types.Package, sig *types.Signature) {
|
||||
p.writeSignatureInternal(this, sig, make([]types.Type, 8))
|
||||
}
|
||||
|
||||
func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) {
|
||||
p.writeTuple(this, sig.Params(), sig.Variadic(), visited)
|
||||
|
||||
res := sig.Results()
|
||||
n := res.Len()
|
||||
if n == 0 {
|
||||
// no result
|
||||
return
|
||||
}
|
||||
|
||||
p.print(" ")
|
||||
if n == 1 && res.At(0).Name() == "" {
|
||||
// single unnamed result
|
||||
p.writeTypeInternal(this, res.At(0).Type(), visited)
|
||||
return
|
||||
}
|
||||
|
||||
// multiple or named result(s)
|
||||
p.writeTuple(this, res, false, visited)
|
||||
}
|
Loading…
Reference in New Issue
Block a user