github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/go/types/typestring.go (about)

     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  )
    13  
    14  // If GcCompatibilityMode is set, printing of types is modified
    15  // to match the representation of some types in the gc compiler:
    16  //
    17  //	- byte and rune lose their alias name and simply stand for
    18  //	  uint8 and int32 respectively
    19  //	- embedded interfaces get flattened (the embedding info is lost,
    20  //	  and certain recursive interface types cannot be printed anymore)
    21  //
    22  // This makes it easier to compare packages computed with the type-
    23  // checker vs packages imported from gc export data.
    24  //
    25  // Caution: This flag affects all uses of WriteType, globally.
    26  // It is only provided for testing in conjunction with
    27  // gc-generated data. It may be removed at any time.
    28  var GcCompatibilityMode bool
    29  
    30  // TypeString returns the string representation of typ.
    31  // Named types are printed package-qualified if they
    32  // do not belong to this package.
    33  func TypeString(this *Package, typ Type) string {
    34  	var buf bytes.Buffer
    35  	WriteType(&buf, this, typ)
    36  	return buf.String()
    37  }
    38  
    39  // WriteType writes the string representation of typ to buf.
    40  // Named types are printed package-qualified if they
    41  // do not belong to this package.
    42  func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
    43  	writeType(buf, this, typ, make([]Type, 8))
    44  }
    45  
    46  func writeType(buf *bytes.Buffer, this *Package, typ Type, visited []Type) {
    47  	// Theoretically, this is a quadratic lookup algorithm, but in
    48  	// practice deeply nested composite types with unnamed component
    49  	// types are uncommon. This code is likely more efficient than
    50  	// using a map.
    51  	for _, t := range visited {
    52  		if t == typ {
    53  			fmt.Fprintf(buf, "○%T", typ) // cycle to typ
    54  			return
    55  		}
    56  	}
    57  	visited = append(visited, typ)
    58  
    59  	switch t := typ.(type) {
    60  	case nil:
    61  		buf.WriteString("<nil>")
    62  
    63  	case *Basic:
    64  		if t.kind == UnsafePointer {
    65  			buf.WriteString("unsafe.")
    66  		}
    67  		if GcCompatibilityMode {
    68  			// forget the alias names
    69  			switch t.kind {
    70  			case Byte:
    71  				t = Typ[Uint8]
    72  			case Rune:
    73  				t = Typ[Int32]
    74  			}
    75  		}
    76  		buf.WriteString(t.name)
    77  
    78  	case *Array:
    79  		fmt.Fprintf(buf, "[%d]", t.len)
    80  		writeType(buf, this, t.elem, visited)
    81  
    82  	case *Slice:
    83  		buf.WriteString("[]")
    84  		writeType(buf, this, t.elem, visited)
    85  
    86  	case *Struct:
    87  		buf.WriteString("struct{")
    88  		for i, f := range t.fields {
    89  			if i > 0 {
    90  				buf.WriteString("; ")
    91  			}
    92  			if !f.anonymous {
    93  				buf.WriteString(f.name)
    94  				buf.WriteByte(' ')
    95  			}
    96  			writeType(buf, this, f.typ, visited)
    97  			if tag := t.Tag(i); tag != "" {
    98  				fmt.Fprintf(buf, " %q", tag)
    99  			}
   100  		}
   101  		buf.WriteByte('}')
   102  
   103  	case *Pointer:
   104  		buf.WriteByte('*')
   105  		writeType(buf, this, t.base, visited)
   106  
   107  	case *Tuple:
   108  		writeTuple(buf, this, t, false, visited)
   109  
   110  	case *Signature:
   111  		buf.WriteString("func")
   112  		writeSignature(buf, this, t, visited)
   113  
   114  	case *Interface:
   115  		// We write the source-level methods and embedded types rather
   116  		// than the actual method set since resolved method signatures
   117  		// may have non-printable cycles if parameters have anonymous
   118  		// interface types that (directly or indirectly) embed the
   119  		// current interface. For instance, consider the result type
   120  		// of m:
   121  		//
   122  		//     type T interface{
   123  		//         m() interface{ T }
   124  		//     }
   125  		//
   126  		buf.WriteString("interface{")
   127  		if GcCompatibilityMode {
   128  			// print flattened interface
   129  			// (useful to compare against gc-generated interfaces)
   130  			for i, m := range t.allMethods {
   131  				if i > 0 {
   132  					buf.WriteString("; ")
   133  				}
   134  				buf.WriteString(m.name)
   135  				writeSignature(buf, this, m.typ.(*Signature), visited)
   136  			}
   137  		} else {
   138  			// print explicit interface methods and embedded types
   139  			for i, m := range t.methods {
   140  				if i > 0 {
   141  					buf.WriteString("; ")
   142  				}
   143  				buf.WriteString(m.name)
   144  				writeSignature(buf, this, m.typ.(*Signature), visited)
   145  			}
   146  			for i, typ := range t.embeddeds {
   147  				if i > 0 || len(t.methods) > 0 {
   148  					buf.WriteString("; ")
   149  				}
   150  				writeType(buf, this, typ, visited)
   151  			}
   152  		}
   153  		buf.WriteByte('}')
   154  
   155  	case *Map:
   156  		buf.WriteString("map[")
   157  		writeType(buf, this, t.key, visited)
   158  		buf.WriteByte(']')
   159  		writeType(buf, this, t.elem, visited)
   160  
   161  	case *Chan:
   162  		var s string
   163  		var parens bool
   164  		switch t.dir {
   165  		case SendRecv:
   166  			s = "chan "
   167  			// chan (<-chan T) requires parentheses
   168  			if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
   169  				parens = true
   170  			}
   171  		case SendOnly:
   172  			s = "chan<- "
   173  		case RecvOnly:
   174  			s = "<-chan "
   175  		default:
   176  			panic("unreachable")
   177  		}
   178  		buf.WriteString(s)
   179  		if parens {
   180  			buf.WriteByte('(')
   181  		}
   182  		writeType(buf, this, t.elem, visited)
   183  		if parens {
   184  			buf.WriteByte(')')
   185  		}
   186  
   187  	case *Named:
   188  		s := "<Named w/o object>"
   189  		if obj := t.obj; obj != nil {
   190  			if pkg := obj.pkg; pkg != nil && pkg != this {
   191  				buf.WriteString(pkg.path)
   192  				buf.WriteByte('.')
   193  			}
   194  			// TODO(gri): function-local named types should be displayed
   195  			// differently from named types at package level to avoid
   196  			// ambiguity.
   197  			s = obj.name
   198  		}
   199  		buf.WriteString(s)
   200  
   201  	default:
   202  		// For externally defined implementations of Type.
   203  		buf.WriteString(t.String())
   204  	}
   205  }
   206  
   207  func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool, visited []Type) {
   208  	buf.WriteByte('(')
   209  	if tup != nil {
   210  		for i, v := range tup.vars {
   211  			if i > 0 {
   212  				buf.WriteString(", ")
   213  			}
   214  			if v.name != "" {
   215  				buf.WriteString(v.name)
   216  				buf.WriteByte(' ')
   217  			}
   218  			typ := v.typ
   219  			if variadic && i == len(tup.vars)-1 {
   220  				if s, ok := typ.(*Slice); ok {
   221  					buf.WriteString("...")
   222  					typ = s.elem
   223  				} else {
   224  					// special case:
   225  					// append(s, "foo"...) leads to signature func([]byte, string...)
   226  					if t, ok := typ.Underlying().(*Basic); !ok || t.kind != String {
   227  						panic("internal error: string type expected")
   228  					}
   229  					writeType(buf, this, typ, visited)
   230  					buf.WriteString("...")
   231  					continue
   232  				}
   233  			}
   234  			writeType(buf, this, typ, visited)
   235  		}
   236  	}
   237  	buf.WriteByte(')')
   238  }
   239  
   240  // WriteSignature writes the representation of the signature sig to buf,
   241  // without a leading "func" keyword.
   242  // Named types are printed package-qualified if they
   243  // do not belong to this package.
   244  func WriteSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
   245  	writeSignature(buf, this, sig, make([]Type, 8))
   246  }
   247  
   248  func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature, visited []Type) {
   249  	writeTuple(buf, this, sig.params, sig.variadic, visited)
   250  
   251  	n := sig.results.Len()
   252  	if n == 0 {
   253  		// no result
   254  		return
   255  	}
   256  
   257  	buf.WriteByte(' ')
   258  	if n == 1 && sig.results.vars[0].name == "" {
   259  		// single unnamed result
   260  		writeType(buf, this, sig.results.vars[0].typ, visited)
   261  		return
   262  	}
   263  
   264  	// multiple or named result(s)
   265  	writeTuple(buf, this, sig.results, false, visited)
   266  }