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
7package types2
8
9import (
10	"bytes"
11	"fmt"
12	"sort"
13	"strconv"
14	"strings"
15	"unicode/utf8"
16)
17
18// A Qualifier controls how named package-level objects are printed in
19// calls to [TypeString], [ObjectString], and [SelectionString].
20//
21// These three formatting routines call the Qualifier for each
22// package-level object O, and if the Qualifier returns a non-empty
23// string p, the object is printed in the form p.O.
24// If it returns an empty string, only the object name O is printed.
25//
26// Using a nil Qualifier is equivalent to using (*[Package]).Path: the
27// object is qualified by the import path, e.g., "encoding/json.Marshal".
28type Qualifier func(*Package) string
29
30// RelativeTo returns a [Qualifier] that fully qualifies members of
31// all packages other than pkg.
32func RelativeTo(pkg *Package) Qualifier {
33	if pkg == nil {
34		return nil
35	}
36	return func(other *Package) string {
37		if pkg == other {
38			return "" // same package; unqualified
39		}
40		return other.Path()
41	}
42}
43
44// TypeString returns the string representation of typ.
45// The [Qualifier] controls the printing of
46// package-level objects, and may be nil.
47func TypeString(typ Type, qf Qualifier) string {
48	var buf bytes.Buffer
49	WriteType(&buf, typ, qf)
50	return buf.String()
51}
52
53// WriteType writes the string representation of typ to buf.
54// The [Qualifier] controls the printing of
55// package-level objects, and may be nil.
56func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
57	newTypeWriter(buf, qf).typ(typ)
58}
59
60// WriteSignature writes the representation of the signature sig to buf,
61// without a leading "func" keyword. The [Qualifier] controls the printing
62// of package-level objects, and may be nil.
63func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
64	newTypeWriter(buf, qf).signature(sig)
65}
66
67type typeWriter struct {
68	buf          *bytes.Buffer
69	seen         map[Type]bool
70	qf           Qualifier
71	ctxt         *Context       // if non-nil, we are type hashing
72	tparams      *TypeParamList // local type parameters
73	paramNames   bool           // if set, write function parameter names, otherwise, write types only
74	tpSubscripts bool           // if set, write type parameter indices as subscripts
75	pkgInfo      bool           // package-annotate first unexported-type field to avoid confusing type description
76}
77
78func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
79	return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false}
80}
81
82func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
83	assert(ctxt != nil)
84	return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false}
85}
86
87func (w *typeWriter) byte(b byte) {
88	if w.ctxt != nil {
89		if b == ' ' {
90			b = '#'
91		}
92		w.buf.WriteByte(b)
93		return
94	}
95	w.buf.WriteByte(b)
96	if b == ',' || b == ';' {
97		w.buf.WriteByte(' ')
98	}
99}
100
101func (w *typeWriter) string(s string) {
102	w.buf.WriteString(s)
103}
104
105func (w *typeWriter) error(msg string) {
106	if w.ctxt != nil {
107		panic(msg)
108	}
109	w.buf.WriteString("<" + msg + ">")
110}
111
112func (w *typeWriter) typ(typ Type) {
113	if w.seen[typ] {
114		w.error("cycle to " + goTypeName(typ))
115		return
116	}
117	w.seen[typ] = true
118	defer delete(w.seen, typ)
119
120	switch t := typ.(type) {
121	case nil:
122		w.error("nil")
123
124	case *Basic:
125		// exported basic types go into package unsafe
126		// (currently this is just unsafe.Pointer)
127		if isExported(t.name) {
128			if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
129				w.typeName(obj)
130				break
131			}
132		}
133		w.string(t.name)
134
135	case *Array:
136		w.byte('[')
137		w.string(strconv.FormatInt(t.len, 10))
138		w.byte(']')
139		w.typ(t.elem)
140
141	case *Slice:
142		w.string("[]")
143		w.typ(t.elem)
144
145	case *Struct:
146		w.string("struct{")
147		for i, f := range t.fields {
148			if i > 0 {
149				w.byte(';')
150			}
151
152			// If disambiguating one struct for another, look for the first unexported field.
153			// Do this first in case of nested structs; tag the first-outermost field.
154			pkgAnnotate := false
155			if w.qf == nil && w.pkgInfo && !isExported(f.name) {
156				// note for embedded types, type name is field name, and "string" etc are lower case hence unexported.
157				pkgAnnotate = true
158				w.pkgInfo = false // only tag once
159			}
160
161			// This doesn't do the right thing for embedded type
162			// aliases where we should print the alias name, not
163			// the aliased type (see go.dev/issue/44410).
164			if !f.embedded {
165				w.string(f.name)
166				w.byte(' ')
167			}
168			w.typ(f.typ)
169			if pkgAnnotate {
170				w.string(" /* package ")
171				w.string(f.pkg.Path())
172				w.string(" */ ")
173			}
174			if tag := t.Tag(i); tag != "" {
175				w.byte(' ')
176				// TODO(gri) If tag contains blanks, replacing them with '#'
177				//           in Context.TypeHash may produce another tag
178				//           accidentally.
179				w.string(strconv.Quote(tag))
180			}
181		}
182		w.byte('}')
183
184	case *Pointer:
185		w.byte('*')
186		w.typ(t.base)
187
188	case *Tuple:
189		w.tuple(t, false)
190
191	case *Signature:
192		w.string("func")
193		w.signature(t)
194
195	case *Union:
196		// Unions only appear as (syntactic) embedded elements
197		// in interfaces and syntactically cannot be empty.
198		if t.Len() == 0 {
199			w.error("empty union")
200			break
201		}
202		for i, t := range t.terms {
203			if i > 0 {
204				w.string(termSep)
205			}
206			if t.tilde {
207				w.byte('~')
208			}
209			w.typ(t.typ)
210		}
211
212	case *Interface:
213		if w.ctxt == nil {
214			if t == universeAnyAlias.Type().Underlying() {
215				// When not hashing, we can try to improve type strings by writing "any"
216				// for a type that is pointer-identical to universeAny.
217				// TODO(rfindley): this logic should not be necessary with
218				// gotypesalias=1. Remove once that is always the case.
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			if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil {
326				if isTypes2 {
327					w.string(fmt.Sprintf(" /* with %s declared at %v */", t.obj.name, t.obj.Pos()))
328				} else {
329					// Can't print position information because
330					// we don't have a token.FileSet accessible.
331					w.string("/* type parameter */")
332				}
333			}
334		}
335
336	case *Alias:
337		w.typeName(t.obj)
338		if list := t.targs.list(); len(list) != 0 {
339			// instantiated type
340			w.typeList(list)
341		}
342		if w.ctxt != nil {
343			// TODO(gri) do we need to print the alias type name, too?
344			w.typ(Unalias(t.obj.typ))
345		}
346
347	default:
348		// For externally defined implementations of Type.
349		// Note: In this case cycles won't be caught.
350		w.string(t.String())
351	}
352}
353
354// typeSet writes a canonical hash for an interface type set.
355func (w *typeWriter) typeSet(s *_TypeSet) {
356	assert(w.ctxt != nil)
357	first := true
358	for _, m := range s.methods {
359		if !first {
360			w.byte(';')
361		}
362		first = false
363		w.string(m.name)
364		w.signature(m.typ.(*Signature))
365	}
366	switch {
367	case s.terms.isAll():
368		// nothing to do
369	case s.terms.isEmpty():
370		w.string(s.terms.String())
371	default:
372		var termHashes []string
373		for _, term := range s.terms {
374			// terms are not canonically sorted, so we sort their hashes instead.
375			var buf bytes.Buffer
376			if term.tilde {
377				buf.WriteByte('~')
378			}
379			newTypeHasher(&buf, w.ctxt).typ(term.typ)
380			termHashes = append(termHashes, buf.String())
381		}
382		sort.Strings(termHashes)
383		if !first {
384			w.byte(';')
385		}
386		w.string(strings.Join(termHashes, "|"))
387	}
388}
389
390func (w *typeWriter) typeList(list []Type) {
391	w.byte('[')
392	for i, typ := range list {
393		if i > 0 {
394			w.byte(',')
395		}
396		w.typ(typ)
397	}
398	w.byte(']')
399}
400
401func (w *typeWriter) tParamList(list []*TypeParam) {
402	w.byte('[')
403	var prev Type
404	for i, tpar := range list {
405		// Determine the type parameter and its constraint.
406		// list is expected to hold type parameter names,
407		// but don't crash if that's not the case.
408		if tpar == nil {
409			w.error("nil type parameter")
410			continue
411		}
412		if i > 0 {
413			if tpar.bound != prev {
414				// bound changed - write previous one before advancing
415				w.byte(' ')
416				w.typ(prev)
417			}
418			w.byte(',')
419		}
420		prev = tpar.bound
421		w.typ(tpar)
422	}
423	if prev != nil {
424		w.byte(' ')
425		w.typ(prev)
426	}
427	w.byte(']')
428}
429
430func (w *typeWriter) typeName(obj *TypeName) {
431	w.string(packagePrefix(obj.pkg, w.qf))
432	w.string(obj.name)
433}
434
435func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
436	w.byte('(')
437	if tup != nil {
438		for i, v := range tup.vars {
439			if i > 0 {
440				w.byte(',')
441			}
442			// parameter names are ignored for type identity and thus type hashes
443			if w.ctxt == nil && v.name != "" && w.paramNames {
444				w.string(v.name)
445				w.byte(' ')
446			}
447			typ := v.typ
448			if variadic && i == len(tup.vars)-1 {
449				if s, ok := typ.(*Slice); ok {
450					w.string("...")
451					typ = s.elem
452				} else {
453					// special case:
454					// append(s, "foo"...) leads to signature func([]byte, string...)
455					if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
456						w.error("expected string type")
457						continue
458					}
459					w.typ(typ)
460					w.string("...")
461					continue
462				}
463			}
464			w.typ(typ)
465		}
466	}
467	w.byte(')')
468}
469
470func (w *typeWriter) signature(sig *Signature) {
471	if sig.TypeParams().Len() != 0 {
472		if w.ctxt != nil {
473			assert(w.tparams == nil)
474			w.tparams = sig.TypeParams()
475			defer func() {
476				w.tparams = nil
477			}()
478		}
479		w.tParamList(sig.TypeParams().list())
480	}
481
482	w.tuple(sig.params, sig.variadic)
483
484	n := sig.results.Len()
485	if n == 0 {
486		// no result
487		return
488	}
489
490	w.byte(' ')
491	if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
492		// single unnamed result (if type hashing, name must be ignored)
493		w.typ(sig.results.vars[0].typ)
494		return
495	}
496
497	// multiple or named result(s)
498	w.tuple(sig.results, false)
499}
500
501// subscript returns the decimal (utf8) representation of x using subscript digits.
502func subscript(x uint64) string {
503	const w = len("₀") // all digits 0...9 have the same utf8 width
504	var buf [32 * w]byte
505	i := len(buf)
506	for {
507		i -= w
508		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
509		x /= 10
510		if x == 0 {
511			break
512		}
513	}
514	return string(buf[i:])
515}
516