Source file
src/go/types/typestring.go
1
2
3
4
5
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
20
21
22
23
24
25
26
27
28
29 type Qualifier func(*Package) string
30
31
32
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 ""
40 }
41 return other.Path()
42 }
43 }
44
45
46
47
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
55
56
57 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
58 newTypeWriter(buf, qf).typ(typ)
59 }
60
61
62
63
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
73 tparams *TypeParamList
74 paramNames bool
75 tpSubscripts bool
76 pkgInfo bool
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
127
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
154
155 pkgAnnotate := false
156 if w.qf == nil && w.pkgInfo && !token.IsExported(f.name) {
157
158 pkgAnnotate = true
159 w.pkgInfo = false
160 }
161
162
163
164
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
178
179
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
198
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
217
218
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
233
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
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
293
294 if w.ctxt != nil {
295 w.string(strconv.Itoa(w.ctxt.getID(t)))
296 }
297 w.typeName(t.obj)
298 if t.inst != nil {
299
300 w.typeList(t.inst.targs.list())
301 } else if w.ctxt == nil && t.TypeParams().Len() != 0 {
302
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
313
314
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
322
323
324
325
326
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
336 w.typ(Unalias(t.obj.typ))
337 }
338
339 default:
340
341
342 w.string(t.String())
343 }
344 }
345
346
347 func (w *typeWriter) typeSet(s *_TypeSet) {
348 assert(w.ctxt != nil)
349 first := true
350 for _, m := range s.methods {
351 if !first {
352 w.byte(';')
353 }
354 first = false
355 w.string(m.name)
356 w.signature(m.typ.(*Signature))
357 }
358 switch {
359 case s.terms.isAll():
360
361 case s.terms.isEmpty():
362 w.string(s.terms.String())
363 default:
364 var termHashes []string
365 for _, term := range s.terms {
366
367 var buf bytes.Buffer
368 if term.tilde {
369 buf.WriteByte('~')
370 }
371 newTypeHasher(&buf, w.ctxt).typ(term.typ)
372 termHashes = append(termHashes, buf.String())
373 }
374 sort.Strings(termHashes)
375 if !first {
376 w.byte(';')
377 }
378 w.string(strings.Join(termHashes, "|"))
379 }
380 }
381
382 func (w *typeWriter) typeList(list []Type) {
383 w.byte('[')
384 for i, typ := range list {
385 if i > 0 {
386 w.byte(',')
387 }
388 w.typ(typ)
389 }
390 w.byte(']')
391 }
392
393 func (w *typeWriter) tParamList(list []*TypeParam) {
394 w.byte('[')
395 var prev Type
396 for i, tpar := range list {
397
398
399
400 if tpar == nil {
401 w.error("nil type parameter")
402 continue
403 }
404 if i > 0 {
405 if tpar.bound != prev {
406
407 w.byte(' ')
408 w.typ(prev)
409 }
410 w.byte(',')
411 }
412 prev = tpar.bound
413 w.typ(tpar)
414 }
415 if prev != nil {
416 w.byte(' ')
417 w.typ(prev)
418 }
419 w.byte(']')
420 }
421
422 func (w *typeWriter) typeName(obj *TypeName) {
423 w.string(packagePrefix(obj.pkg, w.qf))
424 w.string(obj.name)
425 }
426
427 func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
428 w.byte('(')
429 if tup != nil {
430 for i, v := range tup.vars {
431 if i > 0 {
432 w.byte(',')
433 }
434
435 if w.ctxt == nil && v.name != "" && w.paramNames {
436 w.string(v.name)
437 w.byte(' ')
438 }
439 typ := v.typ
440 if variadic && i == len(tup.vars)-1 {
441 if s, ok := typ.(*Slice); ok {
442 w.string("...")
443 typ = s.elem
444 } else {
445
446
447 if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
448 w.error("expected string type")
449 continue
450 }
451 w.typ(typ)
452 w.string("...")
453 continue
454 }
455 }
456 w.typ(typ)
457 }
458 }
459 w.byte(')')
460 }
461
462 func (w *typeWriter) signature(sig *Signature) {
463 if sig.TypeParams().Len() != 0 {
464 if w.ctxt != nil {
465 assert(w.tparams == nil)
466 w.tparams = sig.TypeParams()
467 defer func() {
468 w.tparams = nil
469 }()
470 }
471 w.tParamList(sig.TypeParams().list())
472 }
473
474 w.tuple(sig.params, sig.variadic)
475
476 n := sig.results.Len()
477 if n == 0 {
478
479 return
480 }
481
482 w.byte(' ')
483 if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
484
485 w.typ(sig.results.vars[0].typ)
486 return
487 }
488
489
490 w.tuple(sig.results, false)
491 }
492
493
494 func subscript(x uint64) string {
495 const w = len("₀")
496 var buf [32 * w]byte
497 i := len(buf)
498 for {
499 i -= w
500 utf8.EncodeRune(buf[i:], '₀'+rune(x%10))
501 x /= 10
502 if x == 0 {
503 break
504 }
505 }
506 return string(buf[i:])
507 }
508
View as plain text