...
Run Format

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

View as plain text