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