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 7package types2 8 9import ( 10 "bytes" 11 "fmt" 12 "sort" 13 "strconv" 14 "strings" 15 "unicode/utf8" 16) 17 18// A Qualifier controls how named package-level objects are printed in 19// calls to [TypeString], [ObjectString], and [SelectionString]. 20// 21// These three formatting routines call the Qualifier for each 22// package-level object O, and if the Qualifier returns a non-empty 23// string p, the object is printed in the form p.O. 24// If it returns an empty string, only the object name O is printed. 25// 26// Using a nil Qualifier is equivalent to using (*[Package]).Path: the 27// object is qualified by the import path, e.g., "encoding/json.Marshal". 28type Qualifier func(*Package) string 29 30// RelativeTo returns a [Qualifier] that fully qualifies members of 31// all packages other than pkg. 32func RelativeTo(pkg *Package) Qualifier { 33 if pkg == nil { 34 return nil 35 } 36 return func(other *Package) string { 37 if pkg == other { 38 return "" // same package; unqualified 39 } 40 return other.Path() 41 } 42} 43 44// TypeString returns the string representation of typ. 45// The [Qualifier] controls the printing of 46// package-level objects, and may be nil. 47func TypeString(typ Type, qf Qualifier) string { 48 var buf bytes.Buffer 49 WriteType(&buf, typ, qf) 50 return buf.String() 51} 52 53// WriteType writes the string representation of typ to buf. 54// The [Qualifier] controls the printing of 55// package-level objects, and may be nil. 56func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) { 57 newTypeWriter(buf, qf).typ(typ) 58} 59 60// WriteSignature writes the representation of the signature sig to buf, 61// without a leading "func" keyword. The [Qualifier] controls the printing 62// of package-level objects, and may be nil. 63func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { 64 newTypeWriter(buf, qf).signature(sig) 65} 66 67type typeWriter struct { 68 buf *bytes.Buffer 69 seen map[Type]bool 70 qf Qualifier 71 ctxt *Context // if non-nil, we are type hashing 72 tparams *TypeParamList // local type parameters 73 paramNames bool // if set, write function parameter names, otherwise, write types only 74 tpSubscripts bool // if set, write type parameter indices as subscripts 75 pkgInfo bool // package-annotate first unexported-type field to avoid confusing type description 76} 77 78func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter { 79 return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, true, false, false} 80} 81 82func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter { 83 assert(ctxt != nil) 84 return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false, false, false} 85} 86 87func (w *typeWriter) byte(b byte) { 88 if w.ctxt != nil { 89 if b == ' ' { 90 b = '#' 91 } 92 w.buf.WriteByte(b) 93 return 94 } 95 w.buf.WriteByte(b) 96 if b == ',' || b == ';' { 97 w.buf.WriteByte(' ') 98 } 99} 100 101func (w *typeWriter) string(s string) { 102 w.buf.WriteString(s) 103} 104 105func (w *typeWriter) error(msg string) { 106 if w.ctxt != nil { 107 panic(msg) 108 } 109 w.buf.WriteString("<" + msg + ">") 110} 111 112func (w *typeWriter) typ(typ Type) { 113 if w.seen[typ] { 114 w.error("cycle to " + goTypeName(typ)) 115 return 116 } 117 w.seen[typ] = true 118 defer delete(w.seen, typ) 119 120 switch t := typ.(type) { 121 case nil: 122 w.error("nil") 123 124 case *Basic: 125 // exported basic types go into package unsafe 126 // (currently this is just unsafe.Pointer) 127 if isExported(t.name) { 128 if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil { 129 w.typeName(obj) 130 break 131 } 132 } 133 w.string(t.name) 134 135 case *Array: 136 w.byte('[') 137 w.string(strconv.FormatInt(t.len, 10)) 138 w.byte(']') 139 w.typ(t.elem) 140 141 case *Slice: 142 w.string("[]") 143 w.typ(t.elem) 144 145 case *Struct: 146 w.string("struct{") 147 for i, f := range t.fields { 148 if i > 0 { 149 w.byte(';') 150 } 151 152 // If disambiguating one struct for another, look for the first unexported field. 153 // Do this first in case of nested structs; tag the first-outermost field. 154 pkgAnnotate := false 155 if w.qf == nil && w.pkgInfo && !isExported(f.name) { 156 // note for embedded types, type name is field name, and "string" etc are lower case hence unexported. 157 pkgAnnotate = true 158 w.pkgInfo = false // only tag once 159 } 160 161 // This doesn't do the right thing for embedded type 162 // aliases where we should print the alias name, not 163 // the aliased type (see go.dev/issue/44410). 164 if !f.embedded { 165 w.string(f.name) 166 w.byte(' ') 167 } 168 w.typ(f.typ) 169 if pkgAnnotate { 170 w.string(" /* package ") 171 w.string(f.pkg.Path()) 172 w.string(" */ ") 173 } 174 if tag := t.Tag(i); tag != "" { 175 w.byte(' ') 176 // TODO(gri) If tag contains blanks, replacing them with '#' 177 // in Context.TypeHash may produce another tag 178 // accidentally. 179 w.string(strconv.Quote(tag)) 180 } 181 } 182 w.byte('}') 183 184 case *Pointer: 185 w.byte('*') 186 w.typ(t.base) 187 188 case *Tuple: 189 w.tuple(t, false) 190 191 case *Signature: 192 w.string("func") 193 w.signature(t) 194 195 case *Union: 196 // Unions only appear as (syntactic) embedded elements 197 // in interfaces and syntactically cannot be empty. 198 if t.Len() == 0 { 199 w.error("empty union") 200 break 201 } 202 for i, t := range t.terms { 203 if i > 0 { 204 w.string(termSep) 205 } 206 if t.tilde { 207 w.byte('~') 208 } 209 w.typ(t.typ) 210 } 211 212 case *Interface: 213 if w.ctxt == nil { 214 if t == universeAnyAlias.Type().Underlying() { 215 // When not hashing, we can try to improve type strings by writing "any" 216 // for a type that is pointer-identical to universeAny. 217 // TODO(rfindley): this logic should not be necessary with 218 // gotypesalias=1. Remove once that is always the case. 219 w.string("any") 220 break 221 } 222 if t == asNamed(universeComparable.Type()).underlying { 223 w.string("interface{comparable}") 224 break 225 } 226 } 227 if t.implicit { 228 if len(t.methods) == 0 && len(t.embeddeds) == 1 { 229 w.typ(t.embeddeds[0]) 230 break 231 } 232 // Something's wrong with the implicit interface. 233 // Print it as such and continue. 234 w.string("/* implicit */ ") 235 } 236 w.string("interface{") 237 first := true 238 if w.ctxt != nil { 239 w.typeSet(t.typeSet()) 240 } else { 241 for _, m := range t.methods { 242 if !first { 243 w.byte(';') 244 } 245 first = false 246 w.string(m.name) 247 w.signature(m.typ.(*Signature)) 248 } 249 for _, typ := range t.embeddeds { 250 if !first { 251 w.byte(';') 252 } 253 first = false 254 w.typ(typ) 255 } 256 } 257 w.byte('}') 258 259 case *Map: 260 w.string("map[") 261 w.typ(t.key) 262 w.byte(']') 263 w.typ(t.elem) 264 265 case *Chan: 266 var s string 267 var parens bool 268 switch t.dir { 269 case SendRecv: 270 s = "chan " 271 // chan (<-chan T) requires parentheses 272 if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly { 273 parens = true 274 } 275 case SendOnly: 276 s = "chan<- " 277 case RecvOnly: 278 s = "<-chan " 279 default: 280 w.error("unknown channel direction") 281 } 282 w.string(s) 283 if parens { 284 w.byte('(') 285 } 286 w.typ(t.elem) 287 if parens { 288 w.byte(')') 289 } 290 291 case *Named: 292 // If hashing, write a unique prefix for t to represent its identity, since 293 // named type identity is pointer identity. 294 if w.ctxt != nil { 295 w.string(strconv.Itoa(w.ctxt.getID(t))) 296 } 297 w.typeName(t.obj) // when hashing written for readability of the hash only 298 if t.inst != nil { 299 // instantiated type 300 w.typeList(t.inst.targs.list()) 301 } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams 302 // parameterized type 303 w.tParamList(t.TypeParams().list()) 304 } 305 306 case *TypeParam: 307 if t.obj == nil { 308 w.error("unnamed type parameter") 309 break 310 } 311 if i := tparamIndex(w.tparams.list(), t); i >= 0 { 312 // The names of type parameters that are declared by the type being 313 // hashed are not part of the type identity. Replace them with a 314 // placeholder indicating their index. 315 w.string(fmt.Sprintf("$%d", i)) 316 } else { 317 w.string(t.obj.name) 318 if w.tpSubscripts || w.ctxt != nil { 319 w.string(subscript(t.id)) 320 } 321 // If the type parameter name is the same as a predeclared object 322 // (say int), point out where it is declared to avoid confusing 323 // error messages. This doesn't need to be super-elegant; we just 324 // need a clear indication that this is not a predeclared name. 325 if w.ctxt == nil && Universe.Lookup(t.obj.name) != nil { 326 if isTypes2 { 327 w.string(fmt.Sprintf(" /* with %s declared at %v */", t.obj.name, t.obj.Pos())) 328 } else { 329 // Can't print position information because 330 // we don't have a token.FileSet accessible. 331 w.string("/* type parameter */") 332 } 333 } 334 } 335 336 case *Alias: 337 w.typeName(t.obj) 338 if list := t.targs.list(); len(list) != 0 { 339 // instantiated type 340 w.typeList(list) 341 } 342 if w.ctxt != nil { 343 // TODO(gri) do we need to print the alias type name, too? 344 w.typ(Unalias(t.obj.typ)) 345 } 346 347 default: 348 // For externally defined implementations of Type. 349 // Note: In this case cycles won't be caught. 350 w.string(t.String()) 351 } 352} 353 354// typeSet writes a canonical hash for an interface type set. 355func (w *typeWriter) typeSet(s *_TypeSet) { 356 assert(w.ctxt != nil) 357 first := true 358 for _, m := range s.methods { 359 if !first { 360 w.byte(';') 361 } 362 first = false 363 w.string(m.name) 364 w.signature(m.typ.(*Signature)) 365 } 366 switch { 367 case s.terms.isAll(): 368 // nothing to do 369 case s.terms.isEmpty(): 370 w.string(s.terms.String()) 371 default: 372 var termHashes []string 373 for _, term := range s.terms { 374 // terms are not canonically sorted, so we sort their hashes instead. 375 var buf bytes.Buffer 376 if term.tilde { 377 buf.WriteByte('~') 378 } 379 newTypeHasher(&buf, w.ctxt).typ(term.typ) 380 termHashes = append(termHashes, buf.String()) 381 } 382 sort.Strings(termHashes) 383 if !first { 384 w.byte(';') 385 } 386 w.string(strings.Join(termHashes, "|")) 387 } 388} 389 390func (w *typeWriter) typeList(list []Type) { 391 w.byte('[') 392 for i, typ := range list { 393 if i > 0 { 394 w.byte(',') 395 } 396 w.typ(typ) 397 } 398 w.byte(']') 399} 400 401func (w *typeWriter) tParamList(list []*TypeParam) { 402 w.byte('[') 403 var prev Type 404 for i, tpar := range list { 405 // Determine the type parameter and its constraint. 406 // list is expected to hold type parameter names, 407 // but don't crash if that's not the case. 408 if tpar == nil { 409 w.error("nil type parameter") 410 continue 411 } 412 if i > 0 { 413 if tpar.bound != prev { 414 // bound changed - write previous one before advancing 415 w.byte(' ') 416 w.typ(prev) 417 } 418 w.byte(',') 419 } 420 prev = tpar.bound 421 w.typ(tpar) 422 } 423 if prev != nil { 424 w.byte(' ') 425 w.typ(prev) 426 } 427 w.byte(']') 428} 429 430func (w *typeWriter) typeName(obj *TypeName) { 431 w.string(packagePrefix(obj.pkg, w.qf)) 432 w.string(obj.name) 433} 434 435func (w *typeWriter) tuple(tup *Tuple, variadic bool) { 436 w.byte('(') 437 if tup != nil { 438 for i, v := range tup.vars { 439 if i > 0 { 440 w.byte(',') 441 } 442 // parameter names are ignored for type identity and thus type hashes 443 if w.ctxt == nil && v.name != "" && w.paramNames { 444 w.string(v.name) 445 w.byte(' ') 446 } 447 typ := v.typ 448 if variadic && i == len(tup.vars)-1 { 449 if s, ok := typ.(*Slice); ok { 450 w.string("...") 451 typ = s.elem 452 } else { 453 // special case: 454 // append(s, "foo"...) leads to signature func([]byte, string...) 455 if t, _ := under(typ).(*Basic); t == nil || t.kind != String { 456 w.error("expected string type") 457 continue 458 } 459 w.typ(typ) 460 w.string("...") 461 continue 462 } 463 } 464 w.typ(typ) 465 } 466 } 467 w.byte(')') 468} 469 470func (w *typeWriter) signature(sig *Signature) { 471 if sig.TypeParams().Len() != 0 { 472 if w.ctxt != nil { 473 assert(w.tparams == nil) 474 w.tparams = sig.TypeParams() 475 defer func() { 476 w.tparams = nil 477 }() 478 } 479 w.tParamList(sig.TypeParams().list()) 480 } 481 482 w.tuple(sig.params, sig.variadic) 483 484 n := sig.results.Len() 485 if n == 0 { 486 // no result 487 return 488 } 489 490 w.byte(' ') 491 if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") { 492 // single unnamed result (if type hashing, name must be ignored) 493 w.typ(sig.results.vars[0].typ) 494 return 495 } 496 497 // multiple or named result(s) 498 w.tuple(sig.results, false) 499} 500 501// subscript returns the decimal (utf8) representation of x using subscript digits. 502func subscript(x uint64) string { 503 const w = len("₀") // all digits 0...9 have the same utf8 width 504 var buf [32 * w]byte 505 i := len(buf) 506 for { 507 i -= w 508 utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080 509 x /= 10 510 if x == 0 { 511 break 512 } 513 } 514 return string(buf[i:]) 515} 516