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  //
    30  type Qualifier func(*Package) string
    31  
    32  // RelativeTo returns a Qualifier that fully qualifies members of
    33  // all packages other than pkg.
    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 "" // same package; unqualified
    41  		}
    42  		return other.Path()
    43  	}
    44  }
    45  
    46  // TypeString returns the string representation of typ.
    47  // The Qualifier controls the printing of
    48  // package-level objects, and may be nil.
    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  // WriteType writes the string representation of typ to buf.
    62  // The Qualifier controls the printing of
    63  // package-level objects, and may be nil.
    64  func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
    65  	newTypeWriter(buf, qf).typ(typ)
    66  }
    67  
    68  // WriteSignature writes the representation of the signature sig to buf,
    69  // without a leading "func" keyword.
    70  // The Qualifier controls the printing of
    71  // package-level objects, and may be nil.
    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       // if non-nil, we are type hashing
    81  	tparams *TypeParamList // local type parameters
    82  	debug   bool           // if true, write debug annotations
    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  		// exported basic types go into package unsafe
   133  		// (currently this is just unsafe.Pointer)
   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  			// This doesn't do the right thing for embedded type
   159  			// aliases where we should print the alias name, not
   160  			// the aliased type (see issue #44410).
   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  				// TODO(rfindley) If tag contains blanks, replacing them with '#'
   169  				//                in Context.TypeHash may produce another tag
   170  				//                accidentally.
   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  		// Unions only appear as (syntactic) embedded elements
   189  		// in interfaces and syntactically cannot be empty.
   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  				// When not hashing, we can try to improve type strings by writing "any"
   208  				// for a type that is pointer-identical to universeAny. This logic should
   209  				// be deprecated by more robust handling for aliases.
   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  			// Something's wrong with the implicit interface.
   224  			// Print it as such and continue.
   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  			// chan (<-chan T) requires parentheses
   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  		// If hashing, write a unique prefix for t to represent its identity, since
   284  		// named type identity is pointer identity.
   285  		if w.ctxt != nil {
   286  			w.string(strconv.Itoa(w.ctxt.getID(t)))
   287  		}
   288  		w.typeName(t.obj) // when hashing written for readability of the hash only
   289  		if t.targs != nil {
   290  			// instantiated type
   291  			w.typeList(t.targs.list())
   292  		} else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
   293  			// parameterized type
   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  			// The names of type parameters that are declared by the type being
   304  			// hashed are not part of the type identity. Replace them with a
   305  			// placeholder indicating their index.
   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  		// For externally defined implementations of Type.
   316  		// Note: In this case cycles won't be caught.
   317  		w.string(t.String())
   318  	}
   319  }
   320  
   321  // typeSet writes a canonical hash for an interface type set.
   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  		// nothing to do
   336  	case s.terms.isEmpty():
   337  		w.string(s.terms.String())
   338  	default:
   339  		var termHashes []string
   340  		for _, term := range s.terms {
   341  			// terms are not canonically sorted, so we sort their hashes instead.
   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  		// Determine the type parameter and its constraint.
   373  		// list is expected to hold type parameter names,
   374  		// but don't crash if that's not the case.
   375  		if tpar == nil {
   376  			w.error("nil type parameter")
   377  			continue
   378  		}
   379  		if i > 0 {
   380  			if tpar.bound != prev {
   381  				// bound changed - write previous one before advancing
   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  			// parameter names are ignored for type identity and thus type hashes
   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  					// special case:
   423  					// append(s, "foo"...) leads to signature func([]byte, string...)
   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  		// no result
   456  		return
   457  	}
   458  
   459  	w.byte(' ')
   460  	if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
   461  		// single unnamed result (if type hashing, name must be ignored)
   462  		w.typ(sig.results.vars[0].typ)
   463  		return
   464  	}
   465  
   466  	// multiple or named result(s)
   467  	w.tuple(sig.results, false)
   468  }
   469  
   470  // subscript returns the decimal (utf8) representation of x using subscript digits.
   471  func subscript(x uint64) string {
   472  	const w = len("₀") // all digits 0...9 have the same utf8 width
   473  	var buf [32 * w]byte
   474  	i := len(buf)
   475  	for {
   476  		i -= w
   477  		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
   478  		x /= 10
   479  		if x == 0 {
   480  			break
   481  		}
   482  	}
   483  	return string(buf[i:])
   484  }
   485  

View as plain text