blob: 1775fc66773dd4cc5a408b25af8aced6ceae7381 [file] [log] [blame]
// Copyright 2013 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.
// This file implements printing of types.
package types2
import (
"bytes"
"fmt"
"unicode/utf8"
)
// A Qualifier controls how named package-level objects are printed in
// calls to TypeString, ObjectString, and SelectionString.
//
// These three formatting routines call the Qualifier for each
// package-level object O, and if the Qualifier returns a non-empty
// string p, the object is printed in the form p.O.
// If it returns an empty string, only the object name O is printed.
//
// Using a nil Qualifier is equivalent to using (*Package).Path: the
// object is qualified by the import path, e.g., "encoding/json.Marshal".
//
type Qualifier func(*Package) string
// RelativeTo returns a Qualifier that fully qualifies members of
// all packages other than pkg.
func RelativeTo(pkg *Package) Qualifier {
if pkg == nil {
return nil
}
return func(other *Package) string {
if pkg == other {
return "" // same package; unqualified
}
return other.Path()
}
}
// TypeString returns the string representation of typ.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func TypeString(typ Type, qf Qualifier) string {
var buf bytes.Buffer
WriteType(&buf, typ, qf)
return buf.String()
}
// WriteType writes the string representation of typ to buf.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
writeType(buf, typ, qf, make([]Type, 0, 8))
}
// instanceMarker is the prefix for an instantiated type
// in "non-evaluated" instance form.
const instanceMarker = '#'
func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []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 {
fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
return
}
}
visited = append(visited, typ)
switch t := typ.(type) {
case nil:
buf.WriteString("<nil>")
case *Basic:
// exported basic types go into package unsafe
// (currently this is just unsafe.Pointer)
if isExported(t.name) {
if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
writeTypeName(buf, obj, qf)
break
}
}
buf.WriteString(t.name)
case *Array:
fmt.Fprintf(buf, "[%d]", t.len)
writeType(buf, t.elem, qf, visited)
case *Slice:
buf.WriteString("[]")
writeType(buf, t.elem, qf, visited)
case *Struct:
buf.WriteString("struct{")
for i, f := range t.fields {
if i > 0 {
buf.WriteString("; ")
}
// This doesn't do the right thing for embedded type
// aliases where we should print the alias name, not
// the aliased type (see issue #44410).
if !f.embedded {
buf.WriteString(f.name)
buf.WriteByte(' ')
}
writeType(buf, f.typ, qf, visited)
if tag := t.Tag(i); tag != "" {
fmt.Fprintf(buf, " %q", tag)
}
}
buf.WriteByte('}')
case *Pointer:
buf.WriteByte('*')
writeType(buf, t.base, qf, visited)
case *Tuple:
writeTuple(buf, t, false, qf, visited)
case *Signature:
buf.WriteString("func")
writeSignature(buf, t, qf, visited)
case *Union:
// Unions only appear as (syntactic) embedded elements
// in interfaces and syntactically cannot be empty.
if t.Len() == 0 {
panic("empty union")
}
for i, t := range t.terms {
if i > 0 {
buf.WriteByte('|')
}
if t.tilde {
buf.WriteByte('~')
}
writeType(buf, t.typ, qf, visited)
}
case *Interface:
buf.WriteString("interface{")
first := true
for _, m := range t.methods {
if !first {
buf.WriteString("; ")
}
first = false
buf.WriteString(m.name)
writeSignature(buf, m.typ.(*Signature), qf, visited)
}
for _, typ := range t.embeddeds {
if !first {
buf.WriteString("; ")
}
first = false
writeType(buf, typ, qf, visited)
}
buf.WriteByte('}')
case *Map:
buf.WriteString("map[")
writeType(buf, t.key, qf, visited)
buf.WriteByte(']')
writeType(buf, t.elem, qf, visited)
case *Chan:
var s string
var parens bool
switch t.dir {
case SendRecv:
s = "chan "
// chan (<-chan T) requires parentheses
if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
parens = true
}
case SendOnly:
s = "chan<- "
case RecvOnly:
s = "<-chan "
default:
unreachable()
}
buf.WriteString(s)
if parens {
buf.WriteByte('(')
}
writeType(buf, t.elem, qf, visited)
if parens {
buf.WriteByte(')')
}
case *Named:
if t.instPos != nil {
buf.WriteByte(instanceMarker)
}
writeTypeName(buf, t.obj, qf)
if t.targs != nil {
// instantiated type
buf.WriteByte('[')
writeTypeList(buf, t.targs.list(), qf, visited)
buf.WriteByte(']')
} else if t.TParams().Len() != 0 {
// parameterized type
writeTParamList(buf, t.TParams().list(), qf, visited)
}
case *TypeParam:
s := "?"
if t.obj != nil {
// Optionally write out package for typeparams (like Named).
// TODO(danscales): this is required for import/export, so
// we maybe need a separate function that won't be changed
// for debugging purposes.
if t.obj.pkg != nil {
writePackage(buf, t.obj.pkg, qf)
}
s = t.obj.name
}
buf.WriteString(s + subscript(t.id))
case *top:
buf.WriteString("⊤")
default:
// For externally defined implementations of Type.
// Note: In this case cycles won't be caught.
buf.WriteString(t.String())
}
}
func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
for i, typ := range list {
if i > 0 {
buf.WriteString(", ")
}
writeType(buf, typ, qf, visited)
}
}
func writeTParamList(buf *bytes.Buffer, list []*TypeParam, qf Qualifier, visited []Type) {
buf.WriteString("[")
var prev Type
for i, tpar := range list {
// Determine the type parameter and its constraint.
// list is expected to hold type parameter names,
// but don't crash if that's not the case.
var bound Type
if tpar != nil {
bound = tpar.bound // should not be nil but we want to see it if it is
}
if i > 0 {
if bound != prev {
// bound changed - write previous one before advancing
buf.WriteByte(' ')
writeType(buf, prev, qf, visited)
}
buf.WriteString(", ")
}
prev = bound
if tpar != nil {
writeType(buf, tpar, qf, visited)
} else {
buf.WriteString(tpar.obj.name)
}
}
if prev != nil {
buf.WriteByte(' ')
writeType(buf, prev, qf, visited)
}
buf.WriteByte(']')
}
func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
if obj == nil {
assert(instanceHashing == 0) // we need an object for instance hashing
buf.WriteString("<Named w/o object>")
return
}
if obj.pkg != nil {
writePackage(buf, obj.pkg, qf)
}
buf.WriteString(obj.name)
if instanceHashing != 0 {
// For local defined types, use the (original!) TypeName's scope
// numbers to disambiguate.
typ := obj.typ.(*Named)
// TODO(gri) Figure out why typ.orig != typ.orig.orig sometimes
// and whether the loop can iterate more than twice.
// (It seems somehow connected to instance types.)
for typ.orig != typ {
typ = typ.orig
}
writeScopeNumbers(buf, typ.obj.parent)
}
}
// writeScopeNumbers writes the number sequence for this scope to buf
// in the form ".i.j.k" where i, j, k, etc. stand for scope numbers.
// If a scope is nil or has no parent (such as a package scope), nothing
// is written.
func writeScopeNumbers(buf *bytes.Buffer, s *Scope) {
if s != nil && s.number > 0 {
writeScopeNumbers(buf, s.parent)
fmt.Fprintf(buf, ".%d", s.number)
}
}
func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
buf.WriteByte('(')
if tup != nil {
for i, v := range tup.vars {
if i > 0 {
buf.WriteString(", ")
}
if v.name != "" {
buf.WriteString(v.name)
buf.WriteByte(' ')
}
typ := v.typ
if variadic && i == len(tup.vars)-1 {
if s, ok := typ.(*Slice); ok {
buf.WriteString("...")
typ = s.elem
} else {
// special case:
// append(s, "foo"...) leads to signature func([]byte, string...)
if t := asBasic(typ); t == nil || t.kind != String {
panic("expected string type")
}
writeType(buf, typ, qf, visited)
buf.WriteString("...")
continue
}
}
writeType(buf, typ, qf, visited)
}
}
buf.WriteByte(')')
}
// WriteSignature writes the representation of the signature sig to buf,
// without a leading "func" keyword.
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
writeSignature(buf, sig, qf, make([]Type, 0, 8))
}
func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
if sig.TParams().Len() != 0 {
writeTParamList(buf, sig.TParams().list(), qf, visited)
}
writeTuple(buf, sig.params, sig.variadic, qf, visited)
n := sig.results.Len()
if n == 0 {
// no result
return
}
buf.WriteByte(' ')
if n == 1 && sig.results.vars[0].name == "" {
// single unnamed result
writeType(buf, sig.results.vars[0].typ, qf, visited)
return
}
// multiple or named result(s)
writeTuple(buf, sig.results, false, qf, visited)
}
// subscript returns the decimal (utf8) representation of x using subscript digits.
func subscript(x uint64) string {
const w = len("₀") // all digits 0...9 have the same utf8 width
var buf [32 * w]byte
i := len(buf)
for {
i -= w
utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
x /= 10
if x == 0 {
break
}
}
return string(buf[i:])
}