Source file src/go/types/typestring.go

     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  
     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  // A Qualifier controls how named package-level objects are printed in
    20  // calls to [TypeString], [ObjectString], and [SelectionString].
    21  //
    22  // These three formatting routines call the Qualifier for each
    23  // package-level object O, and if the Qualifier returns a non-empty
    24  // string p, the object is printed in the form p.O.
    25  // If it returns an empty string, only the object name O is printed.
    26  //
    27  // Using a nil Qualifier is equivalent to using (*[Package]).Path: the
    28  // object is qualified by the import path, e.g., "encoding/json.Marshal".
    29  type Qualifier func(*Package) string
    30  
    31  // RelativeTo returns a [Qualifier] that fully qualifies members of
    32  // all packages other than pkg.
    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 "" // same package; unqualified
    40  		}
    41  		return other.Path()
    42  	}
    43  }
    44  
    45  // TypeString returns the string representation of typ.
    46  // The [Qualifier] controls the printing of
    47  // package-level objects, and may be nil.
    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  // WriteType writes the string representation of typ to buf.
    55  // The [Qualifier] controls the printing of
    56  // package-level objects, and may be nil.
    57  func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
    58  	newTypeWriter(buf, qf).typ(typ)
    59  }
    60  
    61  // WriteSignature writes the representation of the signature sig to buf,
    62  // without a leading "func" keyword. The [Qualifier] controls the printing
    63  // of package-level objects, and may be nil.
    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       // if non-nil, we are type hashing
    73  	tparams      *TypeParamList // local type parameters
    74  	paramNames   bool           // if set, write function parameter names, otherwise, write types only
    75  	tpSubscripts bool           // if set, write type parameter indices as subscripts
    76  	pkgInfo      bool           // package-annotate first unexported-type field to avoid confusing type description
    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  		// exported basic types go into package unsafe
   127  		// (currently this is just unsafe.Pointer)
   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  			// If disambiguating one struct for another, look for the first unexported field.
   154  			// Do this first in case of nested structs; tag the first-outermost field.
   155  			pkgAnnotate := false
   156  			if w.qf == nil && w.pkgInfo && !token.IsExported(f.name) {
   157  				// note for embedded types, type name is field name, and "string" etc are lower case hence unexported.
   158  				pkgAnnotate = true
   159  				w.pkgInfo = false // only tag once
   160  			}
   161  
   162  			// This doesn't do the right thing for embedded type
   163  			// aliases where we should print the alias name, not
   164  			// the aliased type (see go.dev/issue/44410).
   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  				// TODO(rfindley) If tag contains blanks, replacing them with '#'
   178  				//                in Context.TypeHash may produce another tag
   179  				//                accidentally.
   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  		// Unions only appear as (syntactic) embedded elements
   198  		// in interfaces and syntactically cannot be empty.
   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  				// When not hashing, we can try to improve type strings by writing "any"
   217  				// for a type that is pointer-identical to universeAny. This logic should
   218  				// be deprecated by more robust handling for aliases.
   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  			// Note: types2 prints position information here - we can't do
   326  			//       that because we don't have a token.FileSet accessible.
   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  			// TODO(gri) do we need to print the alias type name, too?
   336  			w.typ(Unalias(t.obj.typ))
   337  		}
   338  
   339  	default:
   340  		// For externally defined implementations of Type.
   341  		// Note: In this case cycles won't be caught.
   342  		w.string(t.String())
   343  	}
   344  }
   345  
   346  // typeSet writes a canonical hash for an interface type set.
   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  		// nothing to do
   361  	case s.terms.isEmpty():
   362  		w.string(s.terms.String())
   363  	default:
   364  		var termHashes []string
   365  		for _, term := range s.terms {
   366  			// terms are not canonically sorted, so we sort their hashes instead.
   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  		// Determine the type parameter and its constraint.
   398  		// list is expected to hold type parameter names,
   399  		// but don't crash if that's not the case.
   400  		if tpar == nil {
   401  			w.error("nil type parameter")
   402  			continue
   403  		}
   404  		if i > 0 {
   405  			if tpar.bound != prev {
   406  				// bound changed - write previous one before advancing
   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  			// parameter names are ignored for type identity and thus type hashes
   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  					// special case:
   446  					// append(s, "foo"...) leads to signature func([]byte, string...)
   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  		// no result
   479  		return
   480  	}
   481  
   482  	w.byte(' ')
   483  	if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
   484  		// single unnamed result (if type hashing, name must be ignored)
   485  		w.typ(sig.results.vars[0].typ)
   486  		return
   487  	}
   488  
   489  	// multiple or named result(s)
   490  	w.tuple(sig.results, false)
   491  }
   492  
   493  // subscript returns the decimal (utf8) representation of x using subscript digits.
   494  func subscript(x uint64) string {
   495  	const w = len("₀") // all digits 0...9 have the same utf8 width
   496  	var buf [32 * w]byte
   497  	i := len(buf)
   498  	for {
   499  		i -= w
   500  		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
   501  		x /= 10
   502  		if x == 0 {
   503  			break
   504  		}
   505  	}
   506  	return string(buf[i:])
   507  }
   508  

View as plain text