...

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