Source file
src/go/types/typestring.go
1
2
3
4
5
6
7
8
9
10 package types
11
12 import (
13 "bytes"
14 "fmt"
15 "slices"
16 "strconv"
17 "strings"
18 "unicode/utf8"
19 )
20
21
22
23
24
25
26
27
28
29
30
31 type Qualifier func(*Package) string
32
33
34
35 func RelativeTo(pkg *Package) Qualifier {
36 if pkg == nil {
37 return nil
38 }
39 return func(other *Package) string {
40 if pkg == other {
41 return ""
42 }
43 return other.Path()
44 }
45 }
46
47
48
49
50 func TypeString(typ Type, qf Qualifier) string {
51 var buf bytes.Buffer
52 WriteType(&buf, typ, qf)
53 return buf.String()
54 }
55
56
57
58
59 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
60 newTypeWriter(buf, qf).typ(typ)
61 }
62
63
64
65
66 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
67 newTypeWriter(buf, qf).signature(sig)
68 }
69
70 type typeWriter struct {
71 buf *bytes.Buffer
72 seen map[Type]bool
73 qf Qualifier
74 ctxt *Context
75 tparams *TypeParamList
76 paramNames bool
77 tpSubscripts bool
78 pkgInfo bool
79 }
80
81 func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
82 return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false}
83 }
84
85 func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
86 assert(ctxt != nil)
87 return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false}
88 }
89
90 func (w *typeWriter) byte(b byte) {
91 if w.ctxt != nil {
92 if b == ' ' {
93 b = '#'
94 }
95 w.buf.WriteByte(b)
96 return
97 }
98 w.buf.WriteByte(b)
99 if b == ',' || b == ';' {
100 w.buf.WriteByte(' ')
101 }
102 }
103
104 func (w *typeWriter) string(s string) {
105 w.buf.WriteString(s)
106 }
107
108 func (w *typeWriter) error(msg string) {
109 if w.ctxt != nil {
110 panic(msg)
111 }
112 w.buf.WriteString("<" + msg + ">")
113 }
114
115 func (w *typeWriter) typ(typ Type) {
116 if w.seen[typ] {
117 w.error("cycle to " + goTypeName(typ))
118 return
119 }
120 w.seen[typ] = true
121 defer delete(w.seen, typ)
122
123 switch t := typ.(type) {
124 case nil:
125 w.error("nil")
126
127 case *Basic:
128
129
130 if isExported(t.name) {
131 if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
132 w.typeName(obj)
133 break
134 }
135 }
136 w.string(t.name)
137
138 case *Array:
139 w.byte('[')
140 w.string(strconv.FormatInt(t.len, 10))
141 w.byte(']')
142 w.typ(t.elem)
143
144 case *Slice:
145 w.string("[]")
146 w.typ(t.elem)
147
148 case *Struct:
149 w.string("struct{")
150 for i, f := range t.fields {
151 if i > 0 {
152 w.byte(';')
153 }
154
155
156
157 pkgAnnotate := false
158 if w.qf == nil && w.pkgInfo && !isExported(f.name) {
159
160 pkgAnnotate = true
161 w.pkgInfo = false
162 }
163
164
165
166
167 if !f.embedded {
168 w.string(f.name)
169 w.byte(' ')
170 }
171 w.typ(f.typ)
172 if pkgAnnotate {
173 w.string(" /* package ")
174 w.string(f.pkg.Path())
175 w.string(" */ ")
176 }
177 if tag := t.Tag(i); tag != "" {
178 w.byte(' ')
179
180
181
182 w.string(strconv.Quote(tag))
183 }
184 }
185 w.byte('}')
186
187 case *Pointer:
188 w.byte('*')
189 w.typ(t.base)
190
191 case *Tuple:
192 w.tuple(t, false)
193
194 case *Signature:
195 w.string("func")
196 w.signature(t)
197
198 case *Union:
199
200
201 if t.Len() == 0 {
202 w.error("empty union")
203 break
204 }
205 for i, t := range t.terms {
206 if i > 0 {
207 w.string(termSep)
208 }
209 if t.tilde {
210 w.byte('~')
211 }
212 w.typ(t.typ)
213 }
214
215 case *Interface:
216 if w.ctxt == nil {
217 if t == asNamed(universeComparable.Type()).underlying {
218 w.string("interface{comparable}")
219 break
220 }
221 }
222 if t.implicit {
223 if len(t.methods) == 0 && len(t.embeddeds) == 1 {
224 w.typ(t.embeddeds[0])
225 break
226 }
227
228
229 w.string("/* implicit */ ")
230 }
231 w.string("interface{")
232 first := true
233 if w.ctxt != nil {
234 w.typeSet(t.typeSet())
235 } else {
236 for _, m := range t.methods {
237 if !first {
238 w.byte(';')
239 }
240 first = false
241 w.string(m.name)
242 w.signature(m.typ.(*Signature))
243 }
244 for _, typ := range t.embeddeds {
245 if !first {
246 w.byte(';')
247 }
248 first = false
249 w.typ(typ)
250 }
251 }
252 w.byte('}')
253
254 case *Map:
255 w.string("map[")
256 w.typ(t.key)
257 w.byte(']')
258 w.typ(t.elem)
259
260 case *Chan:
261 var s string
262 var parens bool
263 switch t.dir {
264 case SendRecv:
265 s = "chan "
266
267 if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
268 parens = true
269 }
270 case SendOnly:
271 s = "chan<- "
272 case RecvOnly:
273 s = "<-chan "
274 default:
275 w.error("unknown channel direction")
276 }
277 w.string(s)
278 if parens {
279 w.byte('(')
280 }
281 w.typ(t.elem)
282 if parens {
283 w.byte(')')
284 }
285
286 case *Named:
287
288
289 if w.ctxt != nil {
290 w.string(strconv.Itoa(w.ctxt.getID(t)))
291 }
292 w.typeName(t.obj)
293 if t.inst != nil {
294
295 w.typeList(t.inst.targs.list())
296 } else if w.ctxt == nil && t.TypeParams().Len() != 0 {
297
298 w.tParamList(t.TypeParams().list())
299 }
300
301 case *TypeParam:
302 if t.obj == nil {
303 w.error("unnamed type parameter")
304 break
305 }
306 if i := slices.Index(w.tparams.list(), t); i >= 0 {
307
308
309
310 w.string(fmt.Sprintf("$%d", i))
311 } else {
312 w.string(t.obj.name)
313 if w.tpSubscripts || w.ctxt != nil {
314 w.string(subscript(t.id))
315 }
316
317
318
319
320 if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil {
321 if isTypes2 {
322 w.string(fmt.Sprintf(" /* with %s declared at %v */", t.obj.name, t.obj.Pos()))
323 } else {
324
325
326 w.string("/* type parameter */")
327 }
328 }
329 }
330
331 case *Alias:
332 w.typeName(t.obj)
333 if list := t.targs.list(); len(list) != 0 {
334
335 w.typeList(list)
336 } else if w.ctxt == nil && t.TypeParams().Len() != 0 {
337
338 w.tParamList(t.TypeParams().list())
339 }
340 if w.ctxt != nil {
341
342 typ := Unalias(t.obj.typ)
343 if typ == nil {
344 panic("known implementation limitation: encountered an incomplete alias (see go.dev/issue/78296)")
345 }
346 w.typ(typ)
347 }
348
349 default:
350
351
352 w.string(t.String())
353 }
354 }
355
356
357 func (w *typeWriter) typeSet(s *_TypeSet) {
358 assert(w.ctxt != nil)
359 first := true
360 for _, m := range s.methods {
361 if !first {
362 w.byte(';')
363 }
364 first = false
365 w.string(m.name)
366 w.signature(m.typ.(*Signature))
367 }
368 switch {
369 case s.terms.isAll():
370
371 case s.terms.isEmpty():
372 w.string(s.terms.String())
373 default:
374 var termHashes []string
375 for _, term := range s.terms {
376
377 var buf bytes.Buffer
378 if term.tilde {
379 buf.WriteByte('~')
380 }
381 newTypeHasher(&buf, w.ctxt).typ(term.typ)
382 termHashes = append(termHashes, buf.String())
383 }
384 slices.Sort(termHashes)
385 if !first {
386 w.byte(';')
387 }
388 w.string(strings.Join(termHashes, "|"))
389 }
390 }
391
392 func (w *typeWriter) typeList(list []Type) {
393 w.byte('[')
394 for i, typ := range list {
395 if i > 0 {
396 w.byte(',')
397 }
398 w.typ(typ)
399 }
400 w.byte(']')
401 }
402
403 func (w *typeWriter) tParamList(list []*TypeParam) {
404 w.byte('[')
405 var prev Type
406 for i, tpar := range list {
407
408
409
410 if tpar == nil {
411 w.error("nil type parameter")
412 continue
413 }
414 if i > 0 {
415 if tpar.bound != prev {
416
417 w.byte(' ')
418 w.typ(prev)
419 }
420 w.byte(',')
421 }
422 prev = tpar.bound
423 w.typ(tpar)
424 }
425 if prev != nil {
426 w.byte(' ')
427 w.typ(prev)
428 }
429 w.byte(']')
430 }
431
432 func (w *typeWriter) typeName(obj *TypeName) {
433 w.string(packagePrefix(obj.pkg, w.qf))
434 w.string(obj.name)
435 }
436
437 func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
438 w.byte('(')
439 if tup != nil {
440 for i, v := range tup.vars {
441 if i > 0 {
442 w.byte(',')
443 }
444
445 if w.ctxt == nil && v.name != "" && w.paramNames {
446 w.string(v.name)
447 w.byte(' ')
448 }
449 typ := v.typ
450 if variadic && i == len(tup.vars)-1 {
451 if slice, ok := typ.(*Slice); ok {
452 w.string("...")
453 w.typ(slice.elem)
454 } else {
455
456
457
458
459
460
461
462
463
464 w.typ(typ)
465 w.string("...")
466 }
467 } else {
468 w.typ(typ)
469 }
470 }
471 }
472 w.byte(')')
473 }
474
475 func (w *typeWriter) signature(sig *Signature) {
476 if sig.TypeParams().Len() != 0 {
477 if w.ctxt != nil {
478 assert(w.tparams == nil)
479 w.tparams = sig.TypeParams()
480 defer func() {
481 w.tparams = nil
482 }()
483 }
484 w.tParamList(sig.TypeParams().list())
485 }
486
487 w.tuple(sig.params, sig.variadic)
488
489 n := sig.results.Len()
490 if n == 0 {
491
492 return
493 }
494
495 w.byte(' ')
496 if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
497
498 w.typ(sig.results.vars[0].typ)
499 return
500 }
501
502
503 w.tuple(sig.results, false)
504 }
505
506
507 func subscript(x uint64) string {
508 const w = len("₀")
509 var buf [32 * w]byte
510 i := len(buf)
511 for {
512 i -= w
513 utf8.EncodeRune(buf[i:], '₀'+rune(x%10))
514 x /= 10
515 if x == 0 {
516 break
517 }
518 }
519 return string(buf[i:])
520 }
521
View as plain text