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