...

Source file src/go/types/typestring.go

Documentation: go/types

     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  	return typeString(typ, qf, false)
    50  }
    51  
    52  func typeString(typ Type, qf Qualifier, debug bool) string {
    53  	var buf bytes.Buffer
    54  	w := newTypeWriter(&buf, qf)
    55  	w.debug = debug
    56  	w.typ(typ)
    57  	return buf.String()
    58  }
    59  
    60  // WriteType writes the string representation of typ to buf.
    61  // The Qualifier controls the printing of
    62  // package-level objects, and may be nil.
    63  func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
    64  	newTypeWriter(buf, qf).typ(typ)
    65  }
    66  
    67  // WriteSignature writes the representation of the signature sig to buf,
    68  // without a leading "func" keyword.
    69  // The Qualifier controls the printing of
    70  // package-level objects, and may be nil.
    71  func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
    72  	newTypeWriter(buf, qf).signature(sig)
    73  }
    74  
    75  type typeWriter struct {
    76  	buf     *bytes.Buffer
    77  	seen    map[Type]bool
    78  	qf      Qualifier
    79  	ctxt    *Context       // if non-nil, we are type hashing
    80  	tparams *TypeParamList // local type parameters
    81  	debug   bool           // if true, write debug annotations
    82  }
    83  
    84  func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
    85  	return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, false}
    86  }
    87  
    88  func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
    89  	assert(ctxt != nil)
    90  	return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false}
    91  }
    92  
    93  func (w *typeWriter) byte(b byte) {
    94  	if w.ctxt != nil {
    95  		if b == ' ' {
    96  			b = '#'
    97  		}
    98  		w.buf.WriteByte(b)
    99  		return
   100  	}
   101  	w.buf.WriteByte(b)
   102  	if b == ',' || b == ';' {
   103  		w.buf.WriteByte(' ')
   104  	}
   105  }
   106  
   107  func (w *typeWriter) string(s string) {
   108  	w.buf.WriteString(s)
   109  }
   110  
   111  func (w *typeWriter) error(msg string) {
   112  	if w.ctxt != nil {
   113  		panic(msg)
   114  	}
   115  	w.buf.WriteString("<" + msg + ">")
   116  }
   117  
   118  func (w *typeWriter) typ(typ Type) {
   119  	if w.seen[typ] {
   120  		w.error("cycle to " + goTypeName(typ))
   121  		return
   122  	}
   123  	w.seen[typ] = true
   124  	defer delete(w.seen, typ)
   125  
   126  	switch t := typ.(type) {
   127  	case nil:
   128  		w.error("nil")
   129  
   130  	case *Basic:
   131  		// exported basic types go into package unsafe
   132  		// (currently this is just unsafe.Pointer)
   133  		if token.IsExported(t.name) {
   134  			if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
   135  				w.typeName(obj)
   136  				break
   137  			}
   138  		}
   139  		w.string(t.name)
   140  
   141  	case *Array:
   142  		w.byte('[')
   143  		w.string(strconv.FormatInt(t.len, 10))
   144  		w.byte(']')
   145  		w.typ(t.elem)
   146  
   147  	case *Slice:
   148  		w.string("[]")
   149  		w.typ(t.elem)
   150  
   151  	case *Struct:
   152  		w.string("struct{")
   153  		for i, f := range t.fields {
   154  			if i > 0 {
   155  				w.byte(';')
   156  			}
   157  			// This doesn't do the right thing for embedded type
   158  			// aliases where we should print the alias name, not
   159  			// the aliased type (see issue #44410).
   160  			if !f.embedded {
   161  				w.string(f.name)
   162  				w.byte(' ')
   163  			}
   164  			w.typ(f.typ)
   165  			if tag := t.Tag(i); tag != "" {
   166  				w.byte(' ')
   167  				// TODO(rfindley) If tag contains blanks, replacing them with '#'
   168  				//                in Context.TypeHash may produce another tag
   169  				//                accidentally.
   170  				w.string(strconv.Quote(tag))
   171  			}
   172  		}
   173  		w.byte('}')
   174  
   175  	case *Pointer:
   176  		w.byte('*')
   177  		w.typ(t.base)
   178  
   179  	case *Tuple:
   180  		w.tuple(t, false)
   181  
   182  	case *Signature:
   183  		w.string("func")
   184  		w.signature(t)
   185  
   186  	case *Union:
   187  		// Unions only appear as (syntactic) embedded elements
   188  		// in interfaces and syntactically cannot be empty.
   189  		if t.Len() == 0 {
   190  			w.error("empty union")
   191  			break
   192  		}
   193  		for i, t := range t.terms {
   194  			if i > 0 {
   195  				w.byte('|')
   196  			}
   197  			if t.tilde {
   198  				w.byte('~')
   199  			}
   200  			w.typ(t.typ)
   201  		}
   202  
   203  	case *Interface:
   204  		if w.ctxt == nil {
   205  			if t == universeAny.Type() {
   206  				// When not hashing, we can try to improve type strings by writing "any"
   207  				// for a type that is pointer-identical to universeAny. This logic should
   208  				// be deprecated by more robust handling for aliases.
   209  				w.string("any")
   210  				break
   211  			}
   212  			if t == universeComparable.Type().(*Named).underlying {
   213  				w.string("interface{comparable}")
   214  				break
   215  			}
   216  		}
   217  		if t.implicit {
   218  			if len(t.methods) == 0 && len(t.embeddeds) == 1 {
   219  				w.typ(t.embeddeds[0])
   220  				break
   221  			}
   222  			// Something's wrong with the implicit interface.
   223  			// Print it as such and continue.
   224  			w.string("/* implicit */ ")
   225  		}
   226  		w.string("interface{")
   227  		first := true
   228  		if w.ctxt != nil {
   229  			w.typeSet(t.typeSet())
   230  		} else {
   231  			for _, m := range t.methods {
   232  				if !first {
   233  					w.byte(';')
   234  				}
   235  				first = false
   236  				w.string(m.name)
   237  				w.signature(m.typ.(*Signature))
   238  			}
   239  			for _, typ := range t.embeddeds {
   240  				if !first {
   241  					w.byte(';')
   242  				}
   243  				first = false
   244  				w.typ(typ)
   245  			}
   246  		}
   247  		w.byte('}')
   248  
   249  	case *Map:
   250  		w.string("map[")
   251  		w.typ(t.key)
   252  		w.byte(']')
   253  		w.typ(t.elem)
   254  
   255  	case *Chan:
   256  		var s string
   257  		var parens bool
   258  		switch t.dir {
   259  		case SendRecv:
   260  			s = "chan "
   261  			// chan (<-chan T) requires parentheses
   262  			if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
   263  				parens = true
   264  			}
   265  		case SendOnly:
   266  			s = "chan<- "
   267  		case RecvOnly:
   268  			s = "<-chan "
   269  		default:
   270  			w.error("unknown channel direction")
   271  		}
   272  		w.string(s)
   273  		if parens {
   274  			w.byte('(')
   275  		}
   276  		w.typ(t.elem)
   277  		if parens {
   278  			w.byte(')')
   279  		}
   280  
   281  	case *Named:
   282  		// If hashing, write a unique prefix for t to represent its identity, since
   283  		// named type identity is pointer identity.
   284  		if w.ctxt != nil {
   285  			w.string(strconv.Itoa(w.ctxt.getID(t)))
   286  		}
   287  		w.typeName(t.obj) // when hashing written for readability of the hash only
   288  		if t.inst != nil {
   289  			// instantiated type
   290  			w.typeList(t.inst.targs.list())
   291  		} else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
   292  			// parameterized type
   293  			w.tParamList(t.TypeParams().list())
   294  		}
   295  
   296  	case *TypeParam:
   297  		if t.obj == nil {
   298  			w.error("unnamed type parameter")
   299  			break
   300  		}
   301  		if i := tparamIndex(w.tparams.list(), t); i >= 0 {
   302  			// The names of type parameters that are declared by the type being
   303  			// hashed are not part of the type identity. Replace them with a
   304  			// placeholder indicating their index.
   305  			w.string(fmt.Sprintf("$%d", i))
   306  		} else {
   307  			w.string(t.obj.name)
   308  			if w.debug || w.ctxt != nil {
   309  				w.string(subscript(t.id))
   310  			}
   311  		}
   312  
   313  	default:
   314  		// For externally defined implementations of Type.
   315  		// Note: In this case cycles won't be caught.
   316  		w.string(t.String())
   317  	}
   318  }
   319  
   320  // typeSet writes a canonical hash for an interface type set.
   321  func (w *typeWriter) typeSet(s *_TypeSet) {
   322  	assert(w.ctxt != nil)
   323  	first := true
   324  	for _, m := range s.methods {
   325  		if !first {
   326  			w.byte(';')
   327  		}
   328  		first = false
   329  		w.string(m.name)
   330  		w.signature(m.typ.(*Signature))
   331  	}
   332  	switch {
   333  	case s.terms.isAll():
   334  		// nothing to do
   335  	case s.terms.isEmpty():
   336  		w.string(s.terms.String())
   337  	default:
   338  		var termHashes []string
   339  		for _, term := range s.terms {
   340  			// terms are not canonically sorted, so we sort their hashes instead.
   341  			var buf bytes.Buffer
   342  			if term.tilde {
   343  				buf.WriteByte('~')
   344  			}
   345  			newTypeHasher(&buf, w.ctxt).typ(term.typ)
   346  			termHashes = append(termHashes, buf.String())
   347  		}
   348  		sort.Strings(termHashes)
   349  		if !first {
   350  			w.byte(';')
   351  		}
   352  		w.string(strings.Join(termHashes, "|"))
   353  	}
   354  }
   355  
   356  func (w *typeWriter) typeList(list []Type) {
   357  	w.byte('[')
   358  	for i, typ := range list {
   359  		if i > 0 {
   360  			w.byte(',')
   361  		}
   362  		w.typ(typ)
   363  	}
   364  	w.byte(']')
   365  }
   366  
   367  func (w *typeWriter) tParamList(list []*TypeParam) {
   368  	w.byte('[')
   369  	var prev Type
   370  	for i, tpar := range list {
   371  		// Determine the type parameter and its constraint.
   372  		// list is expected to hold type parameter names,
   373  		// but don't crash if that's not the case.
   374  		if tpar == nil {
   375  			w.error("nil type parameter")
   376  			continue
   377  		}
   378  		if i > 0 {
   379  			if tpar.bound != prev {
   380  				// bound changed - write previous one before advancing
   381  				w.byte(' ')
   382  				w.typ(prev)
   383  			}
   384  			w.byte(',')
   385  		}
   386  		prev = tpar.bound
   387  		w.typ(tpar)
   388  	}
   389  	if prev != nil {
   390  		w.byte(' ')
   391  		w.typ(prev)
   392  	}
   393  	w.byte(']')
   394  }
   395  
   396  func (w *typeWriter) typeName(obj *TypeName) {
   397  	if obj.pkg != nil {
   398  		writePackage(w.buf, obj.pkg, w.qf)
   399  	}
   400  	w.string(obj.name)
   401  }
   402  
   403  func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
   404  	w.byte('(')
   405  	if tup != nil {
   406  		for i, v := range tup.vars {
   407  			if i > 0 {
   408  				w.byte(',')
   409  			}
   410  			// parameter names are ignored for type identity and thus type hashes
   411  			if w.ctxt == nil && v.name != "" {
   412  				w.string(v.name)
   413  				w.byte(' ')
   414  			}
   415  			typ := v.typ
   416  			if variadic && i == len(tup.vars)-1 {
   417  				if s, ok := typ.(*Slice); ok {
   418  					w.string("...")
   419  					typ = s.elem
   420  				} else {
   421  					// special case:
   422  					// append(s, "foo"...) leads to signature func([]byte, string...)
   423  					if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
   424  						w.error("expected string type")
   425  						continue
   426  					}
   427  					w.typ(typ)
   428  					w.string("...")
   429  					continue
   430  				}
   431  			}
   432  			w.typ(typ)
   433  		}
   434  	}
   435  	w.byte(')')
   436  }
   437  
   438  func (w *typeWriter) signature(sig *Signature) {
   439  	if sig.TypeParams().Len() != 0 {
   440  		if w.ctxt != nil {
   441  			assert(w.tparams == nil)
   442  			w.tparams = sig.TypeParams()
   443  			defer func() {
   444  				w.tparams = nil
   445  			}()
   446  		}
   447  		w.tParamList(sig.TypeParams().list())
   448  	}
   449  
   450  	w.tuple(sig.params, sig.variadic)
   451  
   452  	n := sig.results.Len()
   453  	if n == 0 {
   454  		// no result
   455  		return
   456  	}
   457  
   458  	w.byte(' ')
   459  	if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
   460  		// single unnamed result (if type hashing, name must be ignored)
   461  		w.typ(sig.results.vars[0].typ)
   462  		return
   463  	}
   464  
   465  	// multiple or named result(s)
   466  	w.tuple(sig.results, false)
   467  }
   468  
   469  // subscript returns the decimal (utf8) representation of x using subscript digits.
   470  func subscript(x uint64) string {
   471  	const w = len("₀") // all digits 0...9 have the same utf8 width
   472  	var buf [32 * w]byte
   473  	i := len(buf)
   474  	for {
   475  		i -= w
   476  		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
   477  		x /= 10
   478  		if x == 0 {
   479  			break
   480  		}
   481  	}
   482  	return string(buf[i:])
   483  }
   484  

View as plain text