...

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	)
    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 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.embedded {
   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 embedded
   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