1// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
2// Source: ../../cmd/compile/internal/types2/typestring.go
3
4// Copyright 2013 The Go Authors. All rights reserved.
5// Use of this source code is governed by a BSD-style
6// license that can be found in the LICENSE file.
7
8// This file implements printing of types.
9
10package types
11
12import (
13	"bytes"
14	"fmt"
15	"sort"
16	"strconv"
17	"strings"
18	"unicode/utf8"
19)
20
21// A Qualifier controls how named package-level objects are printed in
22// calls to [TypeString], [ObjectString], and [SelectionString].
23//
24// These three formatting routines call the Qualifier for each
25// package-level object O, and if the Qualifier returns a non-empty
26// string p, the object is printed in the form p.O.
27// If it returns an empty string, only the object name O is printed.
28//
29// Using a nil Qualifier is equivalent to using (*[Package]).Path: the
30// object is qualified by the import path, e.g., "encoding/json.Marshal".
31type Qualifier func(*Package) string
32
33// RelativeTo returns a [Qualifier] that fully qualifies members of
34// all packages other than pkg.
35func RelativeTo(pkg *Package) Qualifier {
36	if pkg == nil {
37		return nil
38	}
39	return func(other *Package) string {
40		if pkg == other {
41			return "" // same package; unqualified
42		}
43		return other.Path()
44	}
45}
46
47// TypeString returns the string representation of typ.
48// The [Qualifier] controls the printing of
49// package-level objects, and may be nil.
50func TypeString(typ Type, qf Qualifier) string {
51	var buf bytes.Buffer
52	WriteType(&buf, typ, qf)
53	return buf.String()
54}
55
56// WriteType writes the string representation of typ to buf.
57// The [Qualifier] controls the printing of
58// package-level objects, and may be nil.
59func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
60	newTypeWriter(buf, qf).typ(typ)
61}
62
63// WriteSignature writes the representation of the signature sig to buf,
64// without a leading "func" keyword. The [Qualifier] controls the printing
65// of package-level objects, and may be nil.
66func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
67	newTypeWriter(buf, qf).signature(sig)
68}
69
70type typeWriter struct {
71	buf          *bytes.Buffer
72	seen         map[Type]bool
73	qf           Qualifier
74	ctxt         *Context       // if non-nil, we are type hashing
75	tparams      *TypeParamList // local type parameters
76	paramNames   bool           // if set, write function parameter names, otherwise, write types only
77	tpSubscripts bool           // if set, write type parameter indices as subscripts
78	pkgInfo      bool           // package-annotate first unexported-type field to avoid confusing type description
79}
80
81func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
82	return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false}
83}
84
85func 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
90func (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
104func (w *typeWriter) string(s string) {
105	w.buf.WriteString(s)
106}
107
108func (w *typeWriter) error(msg string) {
109	if w.ctxt != nil {
110		panic(msg)
111	}
112	w.buf.WriteString("<" + msg + ">")
113}
114
115func (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		// exported basic types go into package unsafe
129		// (currently this is just unsafe.Pointer)
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			// If disambiguating one struct for another, look for the first unexported field.
156			// Do this first in case of nested structs; tag the first-outermost field.
157			pkgAnnotate := false
158			if w.qf == nil && w.pkgInfo && !isExported(f.name) {
159				// note for embedded types, type name is field name, and "string" etc are lower case hence unexported.
160				pkgAnnotate = true
161				w.pkgInfo = false // only tag once
162			}
163
164			// This doesn't do the right thing for embedded type
165			// aliases where we should print the alias name, not
166			// the aliased type (see go.dev/issue/44410).
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				// TODO(gri) If tag contains blanks, replacing them with '#'
180				//           in Context.TypeHash may produce another tag
181				//           accidentally.
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		// Unions only appear as (syntactic) embedded elements
200		// in interfaces and syntactically cannot be empty.
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 == universeAnyAlias.Type().Underlying() {
218				// When not hashing, we can try to improve type strings by writing "any"
219				// for a type that is pointer-identical to universeAny.
220				// TODO(rfindley): this logic should not be necessary with
221				// gotypesalias=1. Remove once that is always the case.
222				w.string("any")
223				break
224			}
225			if t == asNamed(universeComparable.Type()).underlying {
226				w.string("interface{comparable}")
227				break
228			}
229		}
230		if t.implicit {
231			if len(t.methods) == 0 && len(t.embeddeds) == 1 {
232				w.typ(t.embeddeds[0])
233				break
234			}
235			// Something's wrong with the implicit interface.
236			// Print it as such and continue.
237			w.string("/* implicit */ ")
238		}
239		w.string("interface{")
240		first := true
241		if w.ctxt != nil {
242			w.typeSet(t.typeSet())
243		} else {
244			for _, m := range t.methods {
245				if !first {
246					w.byte(';')
247				}
248				first = false
249				w.string(m.name)
250				w.signature(m.typ.(*Signature))
251			}
252			for _, typ := range t.embeddeds {
253				if !first {
254					w.byte(';')
255				}
256				first = false
257				w.typ(typ)
258			}
259		}
260		w.byte('}')
261
262	case *Map:
263		w.string("map[")
264		w.typ(t.key)
265		w.byte(']')
266		w.typ(t.elem)
267
268	case *Chan:
269		var s string
270		var parens bool
271		switch t.dir {
272		case SendRecv:
273			s = "chan "
274			// chan (<-chan T) requires parentheses
275			if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
276				parens = true
277			}
278		case SendOnly:
279			s = "chan<- "
280		case RecvOnly:
281			s = "<-chan "
282		default:
283			w.error("unknown channel direction")
284		}
285		w.string(s)
286		if parens {
287			w.byte('(')
288		}
289		w.typ(t.elem)
290		if parens {
291			w.byte(')')
292		}
293
294	case *Named:
295		// If hashing, write a unique prefix for t to represent its identity, since
296		// named type identity is pointer identity.
297		if w.ctxt != nil {
298			w.string(strconv.Itoa(w.ctxt.getID(t)))
299		}
300		w.typeName(t.obj) // when hashing written for readability of the hash only
301		if t.inst != nil {
302			// instantiated type
303			w.typeList(t.inst.targs.list())
304		} else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
305			// parameterized type
306			w.tParamList(t.TypeParams().list())
307		}
308
309	case *TypeParam:
310		if t.obj == nil {
311			w.error("unnamed type parameter")
312			break
313		}
314		if i := tparamIndex(w.tparams.list(), t); i >= 0 {
315			// The names of type parameters that are declared by the type being
316			// hashed are not part of the type identity. Replace them with a
317			// placeholder indicating their index.
318			w.string(fmt.Sprintf("$%d", i))
319		} else {
320			w.string(t.obj.name)
321			if w.tpSubscripts || w.ctxt != nil {
322				w.string(subscript(t.id))
323			}
324			// If the type parameter name is the same as a predeclared object
325			// (say int), point out where it is declared to avoid confusing
326			// error messages. This doesn't need to be super-elegant; we just
327			// need a clear indication that this is not a predeclared name.
328			if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil {
329				if isTypes2 {
330					w.string(fmt.Sprintf(" /* with %s declared at %v */", t.obj.name, t.obj.Pos()))
331				} else {
332					// Can't print position information because
333					// we don't have a token.FileSet accessible.
334					w.string("/* type parameter */")
335				}
336			}
337		}
338
339	case *Alias:
340		w.typeName(t.obj)
341		if list := t.targs.list(); len(list) != 0 {
342			// instantiated type
343			w.typeList(list)
344		}
345		if w.ctxt != nil {
346			// TODO(gri) do we need to print the alias type name, too?
347			w.typ(Unalias(t.obj.typ))
348		}
349
350	default:
351		// For externally defined implementations of Type.
352		// Note: In this case cycles won't be caught.
353		w.string(t.String())
354	}
355}
356
357// typeSet writes a canonical hash for an interface type set.
358func (w *typeWriter) typeSet(s *_TypeSet) {
359	assert(w.ctxt != nil)
360	first := true
361	for _, m := range s.methods {
362		if !first {
363			w.byte(';')
364		}
365		first = false
366		w.string(m.name)
367		w.signature(m.typ.(*Signature))
368	}
369	switch {
370	case s.terms.isAll():
371		// nothing to do
372	case s.terms.isEmpty():
373		w.string(s.terms.String())
374	default:
375		var termHashes []string
376		for _, term := range s.terms {
377			// terms are not canonically sorted, so we sort their hashes instead.
378			var buf bytes.Buffer
379			if term.tilde {
380				buf.WriteByte('~')
381			}
382			newTypeHasher(&buf, w.ctxt).typ(term.typ)
383			termHashes = append(termHashes, buf.String())
384		}
385		sort.Strings(termHashes)
386		if !first {
387			w.byte(';')
388		}
389		w.string(strings.Join(termHashes, "|"))
390	}
391}
392
393func (w *typeWriter) typeList(list []Type) {
394	w.byte('[')
395	for i, typ := range list {
396		if i > 0 {
397			w.byte(',')
398		}
399		w.typ(typ)
400	}
401	w.byte(']')
402}
403
404func (w *typeWriter) tParamList(list []*TypeParam) {
405	w.byte('[')
406	var prev Type
407	for i, tpar := range list {
408		// Determine the type parameter and its constraint.
409		// list is expected to hold type parameter names,
410		// but don't crash if that's not the case.
411		if tpar == nil {
412			w.error("nil type parameter")
413			continue
414		}
415		if i > 0 {
416			if tpar.bound != prev {
417				// bound changed - write previous one before advancing
418				w.byte(' ')
419				w.typ(prev)
420			}
421			w.byte(',')
422		}
423		prev = tpar.bound
424		w.typ(tpar)
425	}
426	if prev != nil {
427		w.byte(' ')
428		w.typ(prev)
429	}
430	w.byte(']')
431}
432
433func (w *typeWriter) typeName(obj *TypeName) {
434	w.string(packagePrefix(obj.pkg, w.qf))
435	w.string(obj.name)
436}
437
438func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
439	w.byte('(')
440	if tup != nil {
441		for i, v := range tup.vars {
442			if i > 0 {
443				w.byte(',')
444			}
445			// parameter names are ignored for type identity and thus type hashes
446			if w.ctxt == nil && v.name != "" && w.paramNames {
447				w.string(v.name)
448				w.byte(' ')
449			}
450			typ := v.typ
451			if variadic && i == len(tup.vars)-1 {
452				if s, ok := typ.(*Slice); ok {
453					w.string("...")
454					typ = s.elem
455				} else {
456					// special case:
457					// append(s, "foo"...) leads to signature func([]byte, string...)
458					if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
459						w.error("expected string type")
460						continue
461					}
462					w.typ(typ)
463					w.string("...")
464					continue
465				}
466			}
467			w.typ(typ)
468		}
469	}
470	w.byte(')')
471}
472
473func (w *typeWriter) signature(sig *Signature) {
474	if sig.TypeParams().Len() != 0 {
475		if w.ctxt != nil {
476			assert(w.tparams == nil)
477			w.tparams = sig.TypeParams()
478			defer func() {
479				w.tparams = nil
480			}()
481		}
482		w.tParamList(sig.TypeParams().list())
483	}
484
485	w.tuple(sig.params, sig.variadic)
486
487	n := sig.results.Len()
488	if n == 0 {
489		// no result
490		return
491	}
492
493	w.byte(' ')
494	if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
495		// single unnamed result (if type hashing, name must be ignored)
496		w.typ(sig.results.vars[0].typ)
497		return
498	}
499
500	// multiple or named result(s)
501	w.tuple(sig.results, false)
502}
503
504// subscript returns the decimal (utf8) representation of x using subscript digits.
505func subscript(x uint64) string {
506	const w = len("₀") // all digits 0...9 have the same utf8 width
507	var buf [32 * w]byte
508	i := len(buf)
509	for {
510		i -= w
511		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
512		x /= 10
513		if x == 0 {
514			break
515		}
516	}
517	return string(buf[i:])
518}
519