Black Lives Matter. Support the Equal Justice Initiative.

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  	"unicode/utf8"
    14  )
    15  
    16  // A Qualifier controls how named package-level objects are printed in
    17  // calls to TypeString, ObjectString, and SelectionString.
    18  //
    19  // These three formatting routines call the Qualifier for each
    20  // package-level object O, and if the Qualifier returns a non-empty
    21  // string p, the object is printed in the form p.O.
    22  // If it returns an empty string, only the object name O is printed.
    23  //
    24  // Using a nil Qualifier is equivalent to using (*Package).Path: the
    25  // object is qualified by the import path, e.g., "encoding/json.Marshal".
    26  //
    27  type Qualifier func(*Package) string
    28  
    29  // RelativeTo returns a Qualifier that fully qualifies members of
    30  // all packages other than pkg.
    31  func RelativeTo(pkg *Package) Qualifier {
    32  	if pkg == nil {
    33  		return nil
    34  	}
    35  	return func(other *Package) string {
    36  		if pkg == other {
    37  			return "" // same package; unqualified
    38  		}
    39  		return other.Path()
    40  	}
    41  }
    42  
    43  // If gcCompatibilityMode is set, printing of types is modified
    44  // to match the representation of some types in the gc compiler:
    45  //
    46  //	- byte and rune lose their alias name and simply stand for
    47  //	  uint8 and int32 respectively
    48  //	- embedded interfaces get flattened (the embedding info is lost,
    49  //	  and certain recursive interface types cannot be printed anymore)
    50  //
    51  // This makes it easier to compare packages computed with the type-
    52  // checker vs packages imported from gc export data.
    53  //
    54  // Caution: This flag affects all uses of WriteType, globally.
    55  // It is only provided for testing in conjunction with
    56  // gc-generated data.
    57  //
    58  // This flag is exported in the x/tools/go/types package. We don't
    59  // need it at the moment in the std repo and so we don't export it
    60  // anymore. We should eventually try to remove it altogether.
    61  // TODO(gri) remove this
    62  var gcCompatibilityMode bool
    63  
    64  // TypeString returns the string representation of typ.
    65  // The Qualifier controls the printing of
    66  // package-level objects, and may be nil.
    67  func TypeString(typ Type, qf Qualifier) string {
    68  	var buf bytes.Buffer
    69  	WriteType(&buf, typ, qf)
    70  	return buf.String()
    71  }
    72  
    73  // WriteType writes the string representation of typ to buf.
    74  // The Qualifier controls the printing of
    75  // package-level objects, and may be nil.
    76  func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
    77  	writeType(buf, typ, qf, make([]Type, 0, 8))
    78  }
    79  
    80  // instanceMarker is the prefix for an instantiated type
    81  // in "non-evaluated" instance form.
    82  const instanceMarker = '#'
    83  
    84  func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
    85  	// Theoretically, this is a quadratic lookup algorithm, but in
    86  	// practice deeply nested composite types with unnamed component
    87  	// types are uncommon. This code is likely more efficient than
    88  	// using a map.
    89  	for _, t := range visited {
    90  		if t == typ {
    91  			fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
    92  			return
    93  		}
    94  	}
    95  	visited = append(visited, typ)
    96  
    97  	switch t := typ.(type) {
    98  	case nil:
    99  		buf.WriteString("<nil>")
   100  
   101  	case *Basic:
   102  		// exported basic types go into package unsafe
   103  		// (currently this is just unsafe.Pointer)
   104  		if token.IsExported(t.name) {
   105  			if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
   106  				writeTypeName(buf, obj, qf)
   107  				break
   108  			}
   109  		}
   110  
   111  		if gcCompatibilityMode {
   112  			// forget the alias names
   113  			switch t.kind {
   114  			case Byte:
   115  				t = Typ[Uint8]
   116  			case Rune:
   117  				t = Typ[Int32]
   118  			}
   119  		}
   120  		buf.WriteString(t.name)
   121  
   122  	case *Array:
   123  		fmt.Fprintf(buf, "[%d]", t.len)
   124  		writeType(buf, t.elem, qf, visited)
   125  
   126  	case *Slice:
   127  		buf.WriteString("[]")
   128  		writeType(buf, t.elem, qf, visited)
   129  
   130  	case *Struct:
   131  		buf.WriteString("struct{")
   132  		for i, f := range t.fields {
   133  			if i > 0 {
   134  				buf.WriteString("; ")
   135  			}
   136  			// This doesn't do the right thing for embedded type
   137  			// aliases where we should print the alias name, not
   138  			// the aliased type (see issue #44410).
   139  			if !f.embedded {
   140  				buf.WriteString(f.name)
   141  				buf.WriteByte(' ')
   142  			}
   143  			writeType(buf, f.typ, qf, visited)
   144  			if tag := t.Tag(i); tag != "" {
   145  				fmt.Fprintf(buf, " %q", tag)
   146  			}
   147  		}
   148  		buf.WriteByte('}')
   149  
   150  	case *Pointer:
   151  		buf.WriteByte('*')
   152  		writeType(buf, t.base, qf, visited)
   153  
   154  	case *Tuple:
   155  		writeTuple(buf, t, false, qf, visited)
   156  
   157  	case *Signature:
   158  		buf.WriteString("func")
   159  		writeSignature(buf, t, qf, visited)
   160  
   161  	case *_Sum:
   162  		for i, t := range t.types {
   163  			if i > 0 {
   164  				buf.WriteString(", ")
   165  			}
   166  			writeType(buf, t, qf, visited)
   167  		}
   168  
   169  	case *Interface:
   170  		// We write the source-level methods and embedded types rather
   171  		// than the actual method set since resolved method signatures
   172  		// may have non-printable cycles if parameters have embedded
   173  		// interface types that (directly or indirectly) embed the
   174  		// current interface. For instance, consider the result type
   175  		// of m:
   176  		//
   177  		//     type T interface{
   178  		//         m() interface{ T }
   179  		//     }
   180  		//
   181  		buf.WriteString("interface{")
   182  		empty := true
   183  		if gcCompatibilityMode {
   184  			// print flattened interface
   185  			// (useful to compare against gc-generated interfaces)
   186  			for i, m := range t.allMethods {
   187  				if i > 0 {
   188  					buf.WriteString("; ")
   189  				}
   190  				buf.WriteString(m.name)
   191  				writeSignature(buf, m.typ.(*Signature), qf, visited)
   192  				empty = false
   193  			}
   194  			if !empty && t.allTypes != nil {
   195  				buf.WriteString("; ")
   196  			}
   197  			if t.allTypes != nil {
   198  				buf.WriteString("type ")
   199  				writeType(buf, t.allTypes, qf, visited)
   200  			}
   201  		} else {
   202  			// print explicit interface methods and embedded types
   203  			for i, m := range t.methods {
   204  				if i > 0 {
   205  					buf.WriteString("; ")
   206  				}
   207  				buf.WriteString(m.name)
   208  				writeSignature(buf, m.typ.(*Signature), qf, visited)
   209  				empty = false
   210  			}
   211  			if !empty && t.types != nil {
   212  				buf.WriteString("; ")
   213  			}
   214  			if t.types != nil {
   215  				buf.WriteString("type ")
   216  				writeType(buf, t.types, qf, visited)
   217  				empty = false
   218  			}
   219  			if !empty && len(t.embeddeds) > 0 {
   220  				buf.WriteString("; ")
   221  			}
   222  			for i, typ := range t.embeddeds {
   223  				if i > 0 {
   224  					buf.WriteString("; ")
   225  				}
   226  				writeType(buf, typ, qf, visited)
   227  				empty = false
   228  			}
   229  		}
   230  		if t.allMethods == nil || len(t.methods) > len(t.allMethods) {
   231  			if !empty {
   232  				buf.WriteByte(' ')
   233  			}
   234  			buf.WriteString("/* incomplete */")
   235  		}
   236  		buf.WriteByte('}')
   237  
   238  	case *Map:
   239  		buf.WriteString("map[")
   240  		writeType(buf, t.key, qf, visited)
   241  		buf.WriteByte(']')
   242  		writeType(buf, t.elem, qf, visited)
   243  
   244  	case *Chan:
   245  		var s string
   246  		var parens bool
   247  		switch t.dir {
   248  		case SendRecv:
   249  			s = "chan "
   250  			// chan (<-chan T) requires parentheses
   251  			if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
   252  				parens = true
   253  			}
   254  		case SendOnly:
   255  			s = "chan<- "
   256  		case RecvOnly:
   257  			s = "<-chan "
   258  		default:
   259  			panic("unreachable")
   260  		}
   261  		buf.WriteString(s)
   262  		if parens {
   263  			buf.WriteByte('(')
   264  		}
   265  		writeType(buf, t.elem, qf, visited)
   266  		if parens {
   267  			buf.WriteByte(')')
   268  		}
   269  
   270  	case *Named:
   271  		writeTypeName(buf, t.obj, qf)
   272  		if t.targs != nil {
   273  			// instantiated type
   274  			buf.WriteByte('[')
   275  			writeTypeList(buf, t.targs, qf, visited)
   276  			buf.WriteByte(']')
   277  		} else if t.tparams != nil {
   278  			// parameterized type
   279  			writeTParamList(buf, t.tparams, qf, visited)
   280  		}
   281  
   282  	case *_TypeParam:
   283  		s := "?"
   284  		if t.obj != nil {
   285  			s = t.obj.name
   286  		}
   287  		buf.WriteString(s + subscript(t.id))
   288  
   289  	case *instance:
   290  		buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
   291  		writeTypeName(buf, t.base.obj, qf)
   292  		buf.WriteByte('[')
   293  		writeTypeList(buf, t.targs, qf, visited)
   294  		buf.WriteByte(']')
   295  
   296  	case *bottom:
   297  		buf.WriteString("⊥")
   298  
   299  	case *top:
   300  		buf.WriteString("⊤")
   301  
   302  	default:
   303  		// For externally defined implementations of Type.
   304  		buf.WriteString(t.String())
   305  	}
   306  }
   307  
   308  func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
   309  	for i, typ := range list {
   310  		if i > 0 {
   311  			buf.WriteString(", ")
   312  		}
   313  		writeType(buf, typ, qf, visited)
   314  	}
   315  }
   316  
   317  func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
   318  	// TODO(rFindley) compare this with the corresponding implementation in types2
   319  	buf.WriteString("[")
   320  	var prev Type
   321  	for i, p := range list {
   322  		// TODO(rFindley) support 'any' sugar here.
   323  		var b Type = &emptyInterface
   324  		if t, _ := p.typ.(*_TypeParam); t != nil && t.bound != nil {
   325  			b = t.bound
   326  		}
   327  		if i > 0 {
   328  			if b != prev {
   329  				// type bound changed - write previous one before advancing
   330  				buf.WriteByte(' ')
   331  				writeType(buf, prev, qf, visited)
   332  			}
   333  			buf.WriteString(", ")
   334  		}
   335  		prev = b
   336  
   337  		if t, _ := p.typ.(*_TypeParam); t != nil {
   338  			writeType(buf, t, qf, visited)
   339  		} else {
   340  			buf.WriteString(p.name)
   341  		}
   342  	}
   343  	if prev != nil {
   344  		buf.WriteByte(' ')
   345  		writeType(buf, prev, qf, visited)
   346  	}
   347  	buf.WriteByte(']')
   348  }
   349  
   350  func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
   351  	s := "<Named w/o object>"
   352  	if obj != nil {
   353  		if obj.pkg != nil {
   354  			writePackage(buf, obj.pkg, qf)
   355  		}
   356  		// TODO(gri): function-local named types should be displayed
   357  		// differently from named types at package level to avoid
   358  		// ambiguity.
   359  		s = obj.name
   360  	}
   361  	buf.WriteString(s)
   362  }
   363  
   364  func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
   365  	buf.WriteByte('(')
   366  	if tup != nil {
   367  		for i, v := range tup.vars {
   368  			if i > 0 {
   369  				buf.WriteString(", ")
   370  			}
   371  			if v.name != "" {
   372  				buf.WriteString(v.name)
   373  				buf.WriteByte(' ')
   374  			}
   375  			typ := v.typ
   376  			if variadic && i == len(tup.vars)-1 {
   377  				if s, ok := typ.(*Slice); ok {
   378  					buf.WriteString("...")
   379  					typ = s.elem
   380  				} else {
   381  					// special case:
   382  					// append(s, "foo"...) leads to signature func([]byte, string...)
   383  					if t := asBasic(typ); t == nil || t.kind != String {
   384  						panic("internal error: string type expected")
   385  					}
   386  					writeType(buf, typ, qf, visited)
   387  					buf.WriteString("...")
   388  					continue
   389  				}
   390  			}
   391  			writeType(buf, typ, qf, visited)
   392  		}
   393  	}
   394  	buf.WriteByte(')')
   395  }
   396  
   397  // WriteSignature writes the representation of the signature sig to buf,
   398  // without a leading "func" keyword.
   399  // The Qualifier controls the printing of
   400  // package-level objects, and may be nil.
   401  func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
   402  	writeSignature(buf, sig, qf, make([]Type, 0, 8))
   403  }
   404  
   405  func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
   406  	if sig.tparams != nil {
   407  		writeTParamList(buf, sig.tparams, qf, visited)
   408  	}
   409  
   410  	writeTuple(buf, sig.params, sig.variadic, qf, visited)
   411  
   412  	n := sig.results.Len()
   413  	if n == 0 {
   414  		// no result
   415  		return
   416  	}
   417  
   418  	buf.WriteByte(' ')
   419  	if n == 1 && sig.results.vars[0].name == "" {
   420  		// single unnamed result
   421  		writeType(buf, sig.results.vars[0].typ, qf, visited)
   422  		return
   423  	}
   424  
   425  	// multiple or named result(s)
   426  	writeTuple(buf, sig.results, false, qf, visited)
   427  }
   428  
   429  // subscript returns the decimal (utf8) representation of x using subscript digits.
   430  func subscript(x uint64) string {
   431  	const w = len("₀") // all digits 0...9 have the same utf8 width
   432  	var buf [32 * w]byte
   433  	i := len(buf)
   434  	for {
   435  		i -= w
   436  		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
   437  		x /= 10
   438  		if x == 0 {
   439  			break
   440  		}
   441  	}
   442  	return string(buf[i:])
   443  }
   444  

View as plain text