// 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("") 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("") 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:]) }