2014-03-26 09:38:40 -06:00
|
|
|
// 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.
|
|
|
|
|
2015-10-09 14:35:07 -06:00
|
|
|
// +build go1.5
|
|
|
|
|
2014-04-02 09:49:21 -06:00
|
|
|
// This file implements writing of types. The functionality is lifted
|
2014-03-26 09:38:40 -06:00
|
|
|
// 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.
|
|
|
|
|
2014-04-02 09:49:21 -06:00
|
|
|
package main
|
|
|
|
|
2016-03-17 13:25:34 -06:00
|
|
|
import "go/types"
|
2014-04-02 09:49:21 -06:00
|
|
|
|
2014-03-26 09:38:40 -06:00
|
|
|
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:
|
|
|
|
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 }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
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)
|
|
|
|
}
|