1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file implements printing of types.
15 // A Qualifier controls how named package-level objects are printed in
16 // calls to TypeString, ObjectString, and SelectionString.
18 // These three formatting routines call the Qualifier for each
19 // package-level object O, and if the Qualifier returns a non-empty
20 // string p, the object is printed in the form p.O.
21 // If it returns an empty string, only the object name O is printed.
23 // Using a nil Qualifier is equivalent to using (*Package).Path: the
24 // object is qualified by the import path, e.g., "encoding/json.Marshal".
26 type Qualifier func(*Package) string
28 // RelativeTo returns a Qualifier that fully qualifies members of
29 // all packages other than pkg.
30 func RelativeTo(pkg *Package) Qualifier {
34 return func(other *Package) string {
36 return "" // same package; unqualified
42 // If gcCompatibilityMode is set, printing of types is modified
43 // to match the representation of some types in the gc compiler:
45 // - byte and rune lose their alias name and simply stand for
46 // uint8 and int32 respectively
47 // - embedded interfaces get flattened (the embedding info is lost,
48 // and certain recursive interface types cannot be printed anymore)
50 // This makes it easier to compare packages computed with the type-
51 // checker vs packages imported from gc export data.
53 // Caution: This flag affects all uses of WriteType, globally.
54 // It is only provided for testing in conjunction with
57 // This flag is exported in the x/tools/go/types package. We don't
58 // need it at the moment in the std repo and so we don't export it
59 // anymore. We should eventually try to remove it altogether.
60 // TODO(gri) remove this
61 var gcCompatibilityMode bool
63 // TypeString returns the string representation of typ.
64 // The Qualifier controls the printing of
65 // package-level objects, and may be nil.
66 func TypeString(typ Type, qf Qualifier) string {
68 WriteType(&buf, typ, qf)
72 // WriteType writes the string representation of typ to buf.
73 // The Qualifier controls the printing of
74 // package-level objects, and may be nil.
75 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
76 writeType(buf, typ, qf, make([]Type, 0, 8))
79 // instanceMarker is the prefix for an instantiated type
80 // in "non-evaluated" instance form.
81 const instanceMarker = '#'
83 func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
84 // Theoretically, this is a quadratic lookup algorithm, but in
85 // practice deeply nested composite types with unnamed component
86 // types are uncommon. This code is likely more efficient than
88 for _, t := range visited {
90 fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
94 visited = append(visited, typ)
96 switch t := typ.(type) {
98 buf.WriteString("<nil>")
101 // exported basic types go into package unsafe
102 // (currently this is just unsafe.Pointer)
103 if isExported(t.name) {
104 if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
105 writeTypeName(buf, obj, qf)
110 if gcCompatibilityMode {
111 // forget the alias names
119 buf.WriteString(t.name)
122 fmt.Fprintf(buf, "[%d]", t.len)
123 writeType(buf, t.elem, qf, visited)
126 buf.WriteString("[]")
127 writeType(buf, t.elem, qf, visited)
130 buf.WriteString("struct{")
131 for i, f := range t.fields {
133 buf.WriteString("; ")
135 // This doesn't do the right thing for embedded type
136 // aliases where we should print the alias name, not
137 // the aliased type (see issue #44410).
139 buf.WriteString(f.name)
142 writeType(buf, f.typ, qf, visited)
143 if tag := t.Tag(i); tag != "" {
144 fmt.Fprintf(buf, " %q", tag)
151 writeType(buf, t.base, qf, visited)
154 writeTuple(buf, t, false, qf, visited)
157 buf.WriteString("func")
158 writeSignature(buf, t, qf, visited)
165 for i, e := range t.types {
172 writeType(buf, e, qf, visited)
176 // We write the source-level methods and embedded types rather
177 // than the actual method set since resolved method signatures
178 // may have non-printable cycles if parameters have embedded
179 // interface types that (directly or indirectly) embed the
180 // current interface. For instance, consider the result type
184 // m() interface{ T }
187 buf.WriteString("interface{")
189 if gcCompatibilityMode {
190 // print flattened interface
191 // (useful to compare against gc-generated interfaces)
193 for i, m := range tset.methods {
195 buf.WriteString("; ")
197 buf.WriteString(m.name)
198 writeSignature(buf, m.typ.(*Signature), qf, visited)
201 if !empty && tset.types != nil {
202 buf.WriteString("; ")
204 if tset.types != nil {
205 buf.WriteString("type ")
206 writeType(buf, tset.types, qf, visited)
209 // print explicit interface methods and embedded types
210 for i, m := range t.methods {
212 buf.WriteString("; ")
214 buf.WriteString(m.name)
215 writeSignature(buf, m.typ.(*Signature), qf, visited)
218 if !empty && len(t.embeddeds) > 0 {
219 buf.WriteString("; ")
221 for i, typ := range t.embeddeds {
223 buf.WriteString("; ")
225 writeType(buf, typ, qf, visited)
229 // print /* incomplete */ if needed to satisfy existing tests
230 // TODO(gri) get rid of this eventually
231 if debug && t.tset == nil {
235 buf.WriteString("/* incomplete */")
240 buf.WriteString("map[")
241 writeType(buf, t.key, qf, visited)
243 writeType(buf, t.elem, qf, visited)
251 // chan (<-chan T) requires parentheses
252 if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
266 writeType(buf, t.elem, qf, visited)
272 writeTypeName(buf, t.obj, qf)
276 writeTypeList(buf, t.targs, qf, visited)
278 } else if t.TParams() != nil {
279 // parameterized type
280 writeTParamList(buf, t.TParams(), qf, visited)
286 // Optionally write out package for typeparams (like Named).
287 // TODO(danscales): this is required for import/export, so
288 // we maybe need a separate function that won't be changed
289 // for debugging purposes.
290 if t.obj.pkg != nil {
291 writePackage(buf, t.obj.pkg, qf)
295 buf.WriteString(s + subscript(t.id))
298 buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
299 writeTypeName(buf, t.base.obj, qf)
301 writeTypeList(buf, t.targs, qf, visited)
308 // For externally defined implementations of Type.
309 // Note: In this case cycles won't be caught.
310 buf.WriteString(t.String())
314 func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
315 for i, typ := range list {
317 buf.WriteString(", ")
319 writeType(buf, typ, qf, visited)
323 func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
326 for i, p := range list {
327 // TODO(gri) support 'any' sugar here.
328 var b Type = &emptyInterface
329 if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil {
334 // type bound changed - write previous one before advancing
336 writeType(buf, prev, qf, visited)
338 buf.WriteString(", ")
342 if t, _ := p.typ.(*TypeParam); t != nil {
343 writeType(buf, t, qf, visited)
345 buf.WriteString(p.name)
350 writeType(buf, prev, qf, visited)
355 func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
357 buf.WriteString("<Named w/o object>")
361 writePackage(buf, obj.pkg, qf)
363 buf.WriteString(obj.name)
365 if instanceHashing != 0 {
366 // For local defined types, use the (original!) TypeName's position
367 // to disambiguate. This is overkill, and could probably instead
368 // just be the pointer value (if we assume a non-moving GC) or
369 // a unique ID (like cmd/compile uses). But this works for now,
370 // and is convenient for debugging.
372 // TODO(mdempsky): I still don't fully understand why typ.orig.orig
373 // can differ from typ.orig, or whether looping more than twice is
375 typ := obj.typ.(*Named)
376 for typ.orig != typ {
379 if orig := typ.obj; orig.pkg != nil && orig.parent != orig.pkg.scope {
380 fmt.Fprintf(buf, "@%q", orig.pos)
385 func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
388 for i, v := range tup.vars {
390 buf.WriteString(", ")
393 buf.WriteString(v.name)
397 if variadic && i == len(tup.vars)-1 {
398 if s, ok := typ.(*Slice); ok {
399 buf.WriteString("...")
403 // append(s, "foo"...) leads to signature func([]byte, string...)
404 if t := asBasic(typ); t == nil || t.kind != String {
405 panic("internal error: string type expected")
407 writeType(buf, typ, qf, visited)
408 buf.WriteString("...")
412 writeType(buf, typ, qf, visited)
418 // WriteSignature writes the representation of the signature sig to buf,
419 // without a leading "func" keyword.
420 // The Qualifier controls the printing of
421 // package-level objects, and may be nil.
422 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
423 writeSignature(buf, sig, qf, make([]Type, 0, 8))
426 func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
427 if sig.tparams != nil {
428 writeTParamList(buf, sig.tparams, qf, visited)
431 writeTuple(buf, sig.params, sig.variadic, qf, visited)
433 n := sig.results.Len()
440 if n == 1 && sig.results.vars[0].name == "" {
441 // single unnamed result
442 writeType(buf, sig.results.vars[0].typ, qf, visited)
446 // multiple or named result(s)
447 writeTuple(buf, sig.results, false, qf, visited)
450 // subscript returns the decimal (utf8) representation of x using subscript digits.
451 func subscript(x uint64) string {
452 const w = len("₀") // all digits 0...9 have the same utf8 width
457 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
463 return string(buf[i:])