]> Cypherpunks.ru repositories - gostls13.git/blob - src/go/types/typestring.go
6eee1af2a81c00c333bd722416035c8123d13ec3
[gostls13.git] / src / go / types / typestring.go
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.
4
5 // This file implements printing of types.
6
7 package types
8
9 import (
10         "bytes"
11         "fmt"
12         "go/token"
13         "sort"
14         "strconv"
15         "strings"
16         "unicode/utf8"
17 )
18
19 // A Qualifier controls how named package-level objects are printed in
20 // calls to [TypeString], [ObjectString], and [SelectionString].
21 //
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.
26 //
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
30
31 // RelativeTo returns a [Qualifier] that fully qualifies members of
32 // all packages other than pkg.
33 func RelativeTo(pkg *Package) Qualifier {
34         if pkg == nil {
35                 return nil
36         }
37         return func(other *Package) string {
38                 if pkg == other {
39                         return "" // same package; unqualified
40                 }
41                 return other.Path()
42         }
43 }
44
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 {
49         var buf bytes.Buffer
50         WriteType(&buf, typ, qf)
51         return buf.String()
52 }
53
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)
59 }
60
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)
66 }
67
68 type typeWriter struct {
69         buf          *bytes.Buffer
70         seen         map[Type]bool
71         qf           Qualifier
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
77 }
78
79 func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
80         return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false}
81 }
82
83 func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
84         assert(ctxt != nil)
85         return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false}
86 }
87
88 func (w *typeWriter) byte(b byte) {
89         if w.ctxt != nil {
90                 if b == ' ' {
91                         b = '#'
92                 }
93                 w.buf.WriteByte(b)
94                 return
95         }
96         w.buf.WriteByte(b)
97         if b == ',' || b == ';' {
98                 w.buf.WriteByte(' ')
99         }
100 }
101
102 func (w *typeWriter) string(s string) {
103         w.buf.WriteString(s)
104 }
105
106 func (w *typeWriter) error(msg string) {
107         if w.ctxt != nil {
108                 panic(msg)
109         }
110         w.buf.WriteString("<" + msg + ">")
111 }
112
113 func (w *typeWriter) typ(typ Type) {
114         if w.seen[typ] {
115                 w.error("cycle to " + goTypeName(typ))
116                 return
117         }
118         w.seen[typ] = true
119         defer delete(w.seen, typ)
120
121         switch t := typ.(type) {
122         case nil:
123                 w.error("nil")
124
125         case *Basic:
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 {
130                                 w.typeName(obj)
131                                 break
132                         }
133                 }
134                 w.string(t.name)
135
136         case *Array:
137                 w.byte('[')
138                 w.string(strconv.FormatInt(t.len, 10))
139                 w.byte(']')
140                 w.typ(t.elem)
141
142         case *Slice:
143                 w.string("[]")
144                 w.typ(t.elem)
145
146         case *Struct:
147                 w.string("struct{")
148                 for i, f := range t.fields {
149                         if i > 0 {
150                                 w.byte(';')
151                         }
152
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.
155                         pkgAnnotate := false
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.
158                                 pkgAnnotate = true
159                                 w.pkgInfo = false // only tag once
160                         }
161
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).
165                         if !f.embedded {
166                                 w.string(f.name)
167                                 w.byte(' ')
168                         }
169                         w.typ(f.typ)
170                         if pkgAnnotate {
171                                 w.string(" /* package ")
172                                 w.string(f.pkg.Path())
173                                 w.string(" */ ")
174                         }
175                         if tag := t.Tag(i); tag != "" {
176                                 w.byte(' ')
177                                 // TODO(rfindley) If tag contains blanks, replacing them with '#'
178                                 //                in Context.TypeHash may produce another tag
179                                 //                accidentally.
180                                 w.string(strconv.Quote(tag))
181                         }
182                 }
183                 w.byte('}')
184
185         case *Pointer:
186                 w.byte('*')
187                 w.typ(t.base)
188
189         case *Tuple:
190                 w.tuple(t, false)
191
192         case *Signature:
193                 w.string("func")
194                 w.signature(t)
195
196         case *Union:
197                 // Unions only appear as (syntactic) embedded elements
198                 // in interfaces and syntactically cannot be empty.
199                 if t.Len() == 0 {
200                         w.error("empty union")
201                         break
202                 }
203                 for i, t := range t.terms {
204                         if i > 0 {
205                                 w.string(termSep)
206                         }
207                         if t.tilde {
208                                 w.byte('~')
209                         }
210                         w.typ(t.typ)
211                 }
212
213         case *Interface:
214                 if w.ctxt == nil {
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.
219                                 w.string("any")
220                                 break
221                         }
222                         if t == asNamed(universeComparable.Type()).underlying {
223                                 w.string("interface{comparable}")
224                                 break
225                         }
226                 }
227                 if t.implicit {
228                         if len(t.methods) == 0 && len(t.embeddeds) == 1 {
229                                 w.typ(t.embeddeds[0])
230                                 break
231                         }
232                         // Something's wrong with the implicit interface.
233                         // Print it as such and continue.
234                         w.string("/* implicit */ ")
235                 }
236                 w.string("interface{")
237                 first := true
238                 if w.ctxt != nil {
239                         w.typeSet(t.typeSet())
240                 } else {
241                         for _, m := range t.methods {
242                                 if !first {
243                                         w.byte(';')
244                                 }
245                                 first = false
246                                 w.string(m.name)
247                                 w.signature(m.typ.(*Signature))
248                         }
249                         for _, typ := range t.embeddeds {
250                                 if !first {
251                                         w.byte(';')
252                                 }
253                                 first = false
254                                 w.typ(typ)
255                         }
256                 }
257                 w.byte('}')
258
259         case *Map:
260                 w.string("map[")
261                 w.typ(t.key)
262                 w.byte(']')
263                 w.typ(t.elem)
264
265         case *Chan:
266                 var s string
267                 var parens bool
268                 switch t.dir {
269                 case SendRecv:
270                         s = "chan "
271                         // chan (<-chan T) requires parentheses
272                         if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
273                                 parens = true
274                         }
275                 case SendOnly:
276                         s = "chan<- "
277                 case RecvOnly:
278                         s = "<-chan "
279                 default:
280                         w.error("unknown channel direction")
281                 }
282                 w.string(s)
283                 if parens {
284                         w.byte('(')
285                 }
286                 w.typ(t.elem)
287                 if parens {
288                         w.byte(')')
289                 }
290
291         case *Named:
292                 // If hashing, write a unique prefix for t to represent its identity, since
293                 // named type identity is pointer identity.
294                 if w.ctxt != nil {
295                         w.string(strconv.Itoa(w.ctxt.getID(t)))
296                 }
297                 w.typeName(t.obj) // when hashing written for readability of the hash only
298                 if t.inst != nil {
299                         // instantiated type
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())
304                 }
305
306         case *TypeParam:
307                 if t.obj == nil {
308                         w.error("unnamed type parameter")
309                         break
310                 }
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))
316                 } else {
317                         w.string(t.obj.name)
318                         if w.tpSubscripts || w.ctxt != nil {
319                                 w.string(subscript(t.id))
320                         }
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 */")
329                         }
330                 }
331
332         case *_Alias:
333                 w.typeName(t.obj)
334                 if w.ctxt != nil {
335                         // TODO(gri) do we need to print the alias type name, too?
336                         w.typ(_Unalias(t.obj.typ))
337                 } else {
338                         w.string(fmt.Sprintf(" /* = %s */", _Unalias(t.obj.typ)))
339                 }
340
341         default:
342                 // For externally defined implementations of Type.
343                 // Note: In this case cycles won't be caught.
344                 w.string(t.String())
345         }
346 }
347
348 // typeSet writes a canonical hash for an interface type set.
349 func (w *typeWriter) typeSet(s *_TypeSet) {
350         assert(w.ctxt != nil)
351         first := true
352         for _, m := range s.methods {
353                 if !first {
354                         w.byte(';')
355                 }
356                 first = false
357                 w.string(m.name)
358                 w.signature(m.typ.(*Signature))
359         }
360         switch {
361         case s.terms.isAll():
362                 // nothing to do
363         case s.terms.isEmpty():
364                 w.string(s.terms.String())
365         default:
366                 var termHashes []string
367                 for _, term := range s.terms {
368                         // terms are not canonically sorted, so we sort their hashes instead.
369                         var buf bytes.Buffer
370                         if term.tilde {
371                                 buf.WriteByte('~')
372                         }
373                         newTypeHasher(&buf, w.ctxt).typ(term.typ)
374                         termHashes = append(termHashes, buf.String())
375                 }
376                 sort.Strings(termHashes)
377                 if !first {
378                         w.byte(';')
379                 }
380                 w.string(strings.Join(termHashes, "|"))
381         }
382 }
383
384 func (w *typeWriter) typeList(list []Type) {
385         w.byte('[')
386         for i, typ := range list {
387                 if i > 0 {
388                         w.byte(',')
389                 }
390                 w.typ(typ)
391         }
392         w.byte(']')
393 }
394
395 func (w *typeWriter) tParamList(list []*TypeParam) {
396         w.byte('[')
397         var prev Type
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.
402                 if tpar == nil {
403                         w.error("nil type parameter")
404                         continue
405                 }
406                 if i > 0 {
407                         if tpar.bound != prev {
408                                 // bound changed - write previous one before advancing
409                                 w.byte(' ')
410                                 w.typ(prev)
411                         }
412                         w.byte(',')
413                 }
414                 prev = tpar.bound
415                 w.typ(tpar)
416         }
417         if prev != nil {
418                 w.byte(' ')
419                 w.typ(prev)
420         }
421         w.byte(']')
422 }
423
424 func (w *typeWriter) typeName(obj *TypeName) {
425         w.string(packagePrefix(obj.pkg, w.qf))
426         w.string(obj.name)
427 }
428
429 func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
430         w.byte('(')
431         if tup != nil {
432                 for i, v := range tup.vars {
433                         if i > 0 {
434                                 w.byte(',')
435                         }
436                         // parameter names are ignored for type identity and thus type hashes
437                         if w.ctxt == nil && v.name != "" && w.paramNames {
438                                 w.string(v.name)
439                                 w.byte(' ')
440                         }
441                         typ := v.typ
442                         if variadic && i == len(tup.vars)-1 {
443                                 if s, ok := typ.(*Slice); ok {
444                                         w.string("...")
445                                         typ = s.elem
446                                 } else {
447                                         // special case:
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")
451                                                 continue
452                                         }
453                                         w.typ(typ)
454                                         w.string("...")
455                                         continue
456                                 }
457                         }
458                         w.typ(typ)
459                 }
460         }
461         w.byte(')')
462 }
463
464 func (w *typeWriter) signature(sig *Signature) {
465         if sig.TypeParams().Len() != 0 {
466                 if w.ctxt != nil {
467                         assert(w.tparams == nil)
468                         w.tparams = sig.TypeParams()
469                         defer func() {
470                                 w.tparams = nil
471                         }()
472                 }
473                 w.tParamList(sig.TypeParams().list())
474         }
475
476         w.tuple(sig.params, sig.variadic)
477
478         n := sig.results.Len()
479         if n == 0 {
480                 // no result
481                 return
482         }
483
484         w.byte(' ')
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)
488                 return
489         }
490
491         // multiple or named result(s)
492         w.tuple(sig.results, false)
493 }
494
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
498         var buf [32 * w]byte
499         i := len(buf)
500         for {
501                 i -= w
502                 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
503                 x /= 10
504                 if x == 0 {
505                         break
506                 }
507         }
508         return string(buf[i:])
509 }