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.
19 // A Qualifier controls how named package-level objects are printed in
20 // calls to [TypeString], [ObjectString], and [SelectionString].
22 // These three formatting routines call the Qualifier for each
23 // package-level object O, and if the Qualifier returns a non-empty
24 // string p, the object is printed in the form p.O.
25 // If it returns an empty string, only the object name O is printed.
27 // Using a nil Qualifier is equivalent to using (*[Package]).Path: the
28 // object is qualified by the import path, e.g., "encoding/json.Marshal".
29 type Qualifier func(*Package) string
31 // RelativeTo returns a [Qualifier] that fully qualifies members of
32 // all packages other than pkg.
33 func RelativeTo(pkg *Package) Qualifier {
37 return func(other *Package) string {
39 return "" // same package; unqualified
45 // TypeString returns the string representation of typ.
46 // The [Qualifier] controls the printing of
47 // package-level objects, and may be nil.
48 func TypeString(typ Type, qf Qualifier) string {
50 WriteType(&buf, typ, qf)
54 // WriteType writes the string representation of typ to buf.
55 // The [Qualifier] controls the printing of
56 // package-level objects, and may be nil.
57 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
58 newTypeWriter(buf, qf).typ(typ)
61 // WriteSignature writes the representation of the signature sig to buf,
62 // without a leading "func" keyword. The [Qualifier] controls the printing
63 // of package-level objects, and may be nil.
64 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
65 newTypeWriter(buf, qf).signature(sig)
68 type typeWriter struct {
72 ctxt *Context // if non-nil, we are type hashing
73 tparams *TypeParamList // local type parameters
74 paramNames bool // if set, write function parameter names, otherwise, write types only
75 tpSubscripts bool // if set, write type parameter indices as subscripts
76 pkgInfo bool // package-annotate first unexported-type field to avoid confusing type description
79 func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
80 return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false}
83 func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
85 return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false}
88 func (w *typeWriter) byte(b byte) {
97 if b == ',' || b == ';' {
102 func (w *typeWriter) string(s string) {
106 func (w *typeWriter) error(msg string) {
110 w.buf.WriteString("<" + msg + ">")
113 func (w *typeWriter) typ(typ Type) {
115 w.error("cycle to " + goTypeName(typ))
119 defer delete(w.seen, typ)
121 switch t := typ.(type) {
126 // exported basic types go into package unsafe
127 // (currently this is just unsafe.Pointer)
128 if token.IsExported(t.name) {
129 if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
138 w.string(strconv.FormatInt(t.len, 10))
148 for i, f := range t.fields {
153 // If disambiguating one struct for another, look for the first unexported field.
154 // Do this first in case of nested structs; tag the first-outermost field.
156 if w.qf == nil && w.pkgInfo && !token.IsExported(f.name) {
157 // note for embedded types, type name is field name, and "string" etc are lower case hence unexported.
159 w.pkgInfo = false // only tag once
162 // This doesn't do the right thing for embedded type
163 // aliases where we should print the alias name, not
164 // the aliased type (see go.dev/issue/44410).
171 w.string(" /* package ")
172 w.string(f.pkg.Path())
175 if tag := t.Tag(i); tag != "" {
177 // TODO(rfindley) If tag contains blanks, replacing them with '#'
178 // in Context.TypeHash may produce another tag
180 w.string(strconv.Quote(tag))
197 // Unions only appear as (syntactic) embedded elements
198 // in interfaces and syntactically cannot be empty.
200 w.error("empty union")
203 for i, t := range t.terms {
215 if t == universeAny.Type() {
216 // When not hashing, we can try to improve type strings by writing "any"
217 // for a type that is pointer-identical to universeAny. This logic should
218 // be deprecated by more robust handling for aliases.
222 if t == asNamed(universeComparable.Type()).underlying {
223 w.string("interface{comparable}")
228 if len(t.methods) == 0 && len(t.embeddeds) == 1 {
229 w.typ(t.embeddeds[0])
232 // Something's wrong with the implicit interface.
233 // Print it as such and continue.
234 w.string("/* implicit */ ")
236 w.string("interface{")
239 w.typeSet(t.typeSet())
241 for _, m := range t.methods {
247 w.signature(m.typ.(*Signature))
249 for _, typ := range t.embeddeds {
271 // chan (<-chan T) requires parentheses
272 if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
280 w.error("unknown channel direction")
292 // If hashing, write a unique prefix for t to represent its identity, since
293 // named type identity is pointer identity.
295 w.string(strconv.Itoa(w.ctxt.getID(t)))
297 w.typeName(t.obj) // when hashing written for readability of the hash only
300 w.typeList(t.inst.targs.list())
301 } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
302 // parameterized type
303 w.tParamList(t.TypeParams().list())
308 w.error("unnamed type parameter")
311 if i := tparamIndex(w.tparams.list(), t); i >= 0 {
312 // The names of type parameters that are declared by the type being
313 // hashed are not part of the type identity. Replace them with a
314 // placeholder indicating their index.
315 w.string(fmt.Sprintf("$%d", i))
318 if w.tpSubscripts || w.ctxt != nil {
319 w.string(subscript(t.id))
321 // If the type parameter name is the same as a predeclared object
322 // (say int), point out where it is declared to avoid confusing
323 // error messages. This doesn't need to be super-elegant; we just
324 // need a clear indication that this is not a predeclared name.
325 // Note: types2 prints position information here - we can't do
326 // that because we don't have a token.FileSet accessible.
327 if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil {
328 w.string("/* type parameter */")
335 // TODO(gri) do we need to print the alias type name, too?
336 w.typ(_Unalias(t.obj.typ))
338 w.string(fmt.Sprintf(" /* = %s */", _Unalias(t.obj.typ)))
342 // For externally defined implementations of Type.
343 // Note: In this case cycles won't be caught.
348 // typeSet writes a canonical hash for an interface type set.
349 func (w *typeWriter) typeSet(s *_TypeSet) {
350 assert(w.ctxt != nil)
352 for _, m := range s.methods {
358 w.signature(m.typ.(*Signature))
361 case s.terms.isAll():
363 case s.terms.isEmpty():
364 w.string(s.terms.String())
366 var termHashes []string
367 for _, term := range s.terms {
368 // terms are not canonically sorted, so we sort their hashes instead.
373 newTypeHasher(&buf, w.ctxt).typ(term.typ)
374 termHashes = append(termHashes, buf.String())
376 sort.Strings(termHashes)
380 w.string(strings.Join(termHashes, "|"))
384 func (w *typeWriter) typeList(list []Type) {
386 for i, typ := range list {
395 func (w *typeWriter) tParamList(list []*TypeParam) {
398 for i, tpar := range list {
399 // Determine the type parameter and its constraint.
400 // list is expected to hold type parameter names,
401 // but don't crash if that's not the case.
403 w.error("nil type parameter")
407 if tpar.bound != prev {
408 // bound changed - write previous one before advancing
424 func (w *typeWriter) typeName(obj *TypeName) {
425 w.string(packagePrefix(obj.pkg, w.qf))
429 func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
432 for i, v := range tup.vars {
436 // parameter names are ignored for type identity and thus type hashes
437 if w.ctxt == nil && v.name != "" && w.paramNames {
442 if variadic && i == len(tup.vars)-1 {
443 if s, ok := typ.(*Slice); ok {
448 // append(s, "foo"...) leads to signature func([]byte, string...)
449 if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
450 w.error("expected string type")
464 func (w *typeWriter) signature(sig *Signature) {
465 if sig.TypeParams().Len() != 0 {
467 assert(w.tparams == nil)
468 w.tparams = sig.TypeParams()
473 w.tParamList(sig.TypeParams().list())
476 w.tuple(sig.params, sig.variadic)
478 n := sig.results.Len()
485 if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
486 // single unnamed result (if type hashing, name must be ignored)
487 w.typ(sig.results.vars[0].typ)
491 // multiple or named result(s)
492 w.tuple(sig.results, false)
495 // subscript returns the decimal (utf8) representation of x using subscript digits.
496 func subscript(x uint64) string {
497 const w = len("₀") // all digits 0...9 have the same utf8 width
502 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
508 return string(buf[i:])