package types
import (
)
type Qualifier func(*Package) string
func ( *Package) Qualifier {
if == nil {
return nil
}
return func( *Package) string {
if == {
return ""
}
return .Path()
}
}
func ( Type, Qualifier) string {
return typeString(, , false)
}
func ( Type, Qualifier, bool) string {
var bytes.Buffer
:= newTypeWriter(&, )
.debug =
.typ()
return .String()
}
func ( *bytes.Buffer, Type, Qualifier) {
newTypeWriter(, ).typ()
}
func ( *bytes.Buffer, *Signature, Qualifier) {
newTypeWriter(, ).signature()
}
type typeWriter struct {
buf *bytes.Buffer
seen map[Type]bool
qf Qualifier
ctxt *Context
tparams *TypeParamList
debug bool
}
func ( *bytes.Buffer, Qualifier) *typeWriter {
return &typeWriter{, make(map[Type]bool), , nil, nil, false}
}
func ( *bytes.Buffer, *Context) *typeWriter {
assert( != nil)
return &typeWriter{, make(map[Type]bool), nil, , nil, false}
}
func ( *typeWriter) ( byte) {
if .ctxt != nil {
if == ' ' {
= '#'
}
.buf.WriteByte()
return
}
.buf.WriteByte()
if == ',' || == ';' {
.buf.WriteByte(' ')
}
}
func ( *typeWriter) ( string) {
.buf.WriteString()
}
func ( *typeWriter) ( string) {
if .ctxt != nil {
panic()
}
.buf.WriteString("<" + + ">")
}
func ( *typeWriter) ( Type) {
if .seen[] {
.error("cycle to " + goTypeName())
return
}
.seen[] = true
defer delete(.seen, )
switch t := .(type) {
case nil:
.error("nil")
case *Basic:
if token.IsExported(.name) {
if , := Unsafe.scope.Lookup(.name).(*TypeName); != nil {
.typeName()
break
}
}
.string(.name)
case *Array:
.byte('[')
.string(strconv.FormatInt(.len, 10))
.byte(']')
.(.elem)
case *Slice:
.string("[]")
.(.elem)
case *Struct:
.string("struct{")
for , := range .fields {
if > 0 {
.byte(';')
}
if !.embedded {
.string(.name)
.byte(' ')
}
.(.typ)
if := .Tag(); != "" {
.byte(' ')
.string(strconv.Quote())
}
}
.byte('}')
case *Pointer:
.byte('*')
.(.base)
case *Tuple:
.tuple(, false)
case *Signature:
.string("func")
.signature()
case *Union:
if .Len() == 0 {
.error("empty union")
break
}
for , := range .terms {
if > 0 {
.byte('|')
}
if .tilde {
.byte('~')
}
.(.typ)
}
case *Interface:
if .ctxt == nil {
if == universeAny.Type() {
.string("any")
break
}
if == universeComparable.Type().(*Named).underlying {
.string("interface{comparable}")
break
}
}
if .implicit {
if len(.methods) == 0 && len(.embeddeds) == 1 {
.(.embeddeds[0])
break
}
.string("/* implicit */ ")
}
.string("interface{")
:= true
if .ctxt != nil {
.typeSet(.typeSet())
} else {
for , := range .methods {
if ! {
.byte(';')
}
= false
.string(.name)
.signature(.typ.(*Signature))
}
for , := range .embeddeds {
if ! {
.byte(';')
}
= false
.()
}
}
.byte('}')
case *Map:
.string("map[")
.(.key)
.byte(']')
.(.elem)
case *Chan:
var string
var bool
switch .dir {
case SendRecv:
= "chan "
if , := .elem.(*Chan); != nil && .dir == RecvOnly {
= true
}
case SendOnly:
= "chan<- "
case RecvOnly:
= "<-chan "
default:
.error("unknown channel direction")
}
.string()
if {
.byte('(')
}
.(.elem)
if {
.byte(')')
}
case *Named:
if .ctxt != nil {
.string(strconv.Itoa(.ctxt.getID()))
}
.typeName(.obj)
if .targs != nil {
.typeList(.targs.list())
} else if .ctxt == nil && .TypeParams().Len() != 0 {
.tParamList(.TypeParams().list())
}
case *TypeParam:
if .obj == nil {
.error("unnamed type parameter")
break
}
if := tparamIndex(.tparams.list(), ); >= 0 {
.string(fmt.Sprintf("$%d", ))
} else {
.string(.obj.name)
if .debug || .ctxt != nil {
.string(subscript(.id))
}
}
default:
.string(.String())
}
}
func ( *typeWriter) ( *_TypeSet) {
assert(.ctxt != nil)
:= true
for , := range .methods {
if ! {
.byte(';')
}
= false
.string(.name)
.signature(.typ.(*Signature))
}
switch {
case .terms.isAll():
case .terms.isEmpty():
.string(.terms.String())
default:
var []string
for , := range .terms {
var bytes.Buffer
if .tilde {
.WriteByte('~')
}
newTypeHasher(&, .ctxt).typ(.typ)
= append(, .String())
}
sort.Strings()
if ! {
.byte(';')
}
.string(strings.Join(, "|"))
}
}
func ( *typeWriter) ( []Type) {
.byte('[')
for , := range {
if > 0 {
.byte(',')
}
.typ()
}
.byte(']')
}
func ( *typeWriter) ( []*TypeParam) {
.byte('[')
var Type
for , := range {
if == nil {
.error("nil type parameter")
continue
}
if > 0 {
if .bound != {
.byte(' ')
.typ()
}
.byte(',')
}
= .bound
.typ()
}
if != nil {
.byte(' ')
.typ()
}
.byte(']')
}
func ( *typeWriter) ( *TypeName) {
if .pkg != nil {
writePackage(.buf, .pkg, .qf)
}
.string(.name)
}
func ( *typeWriter) ( *Tuple, bool) {
.byte('(')
if != nil {
for , := range .vars {
if > 0 {
.byte(',')
}
if .ctxt == nil && .name != "" {
.string(.name)
.byte(' ')
}
:= .typ
if && == len(.vars)-1 {
if , := .(*Slice); {
.string("...")
= .elem
} else {
if , := under().(*Basic); == nil || .kind != String {
.error("expected string type")
continue
}
.typ()
.string("...")
continue
}
}
.typ()
}
}
.byte(')')
}
func ( *typeWriter) ( *Signature) {
if .TypeParams().Len() != 0 {
if .ctxt != nil {
assert(.tparams == nil)
.tparams = .TypeParams()
defer func() {
.tparams = nil
}()
}
.tParamList(.TypeParams().list())
}
.tuple(.params, .variadic)
:= .results.Len()
if == 0 {
return
}
.byte(' ')
if == 1 && (.ctxt != nil || .results.vars[0].name == "") {
.typ(.results.vars[0].typ)
return
}
.tuple(.results, false)
}
func ( uint64) string {
const = len("₀")
var [32 * ]byte
:= len()
for {
-=
utf8.EncodeRune([:], '₀'+rune(%10))
/= 10
if == 0 {
break
}
}
return string([:])
}