github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/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 // 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(pkg) 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 var gcCompatibilityMode bool 60 61 // TypeString returns the string representation of typ. 62 // The Qualifier controls the printing of 63 // package-level objects, and may be nil. 64 func TypeString(typ Type, qf Qualifier) string { 65 var buf bytes.Buffer 66 WriteType(&buf, typ, qf) 67 return buf.String() 68 } 69 70 // WriteType writes the string representation of typ to buf. 71 // The Qualifier controls the printing of 72 // package-level objects, and may be nil. 73 func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) { 74 writeType(buf, typ, qf, make([]Type, 0, 8)) 75 } 76 77 func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { 78 // Theoretically, this is a quadratic lookup algorithm, but in 79 // practice deeply nested composite types with unnamed component 80 // types are uncommon. This code is likely more efficient than 81 // using a map. 82 for _, t := range visited { 83 if t == typ { 84 fmt.Fprintf(buf, "○%T", typ) // cycle to typ 85 return 86 } 87 } 88 visited = append(visited, typ) 89 90 switch t := typ.(type) { 91 case nil: 92 buf.WriteString("<nil>") 93 94 case *Basic: 95 if t.kind == UnsafePointer { 96 buf.WriteString("unsafe.") 97 } 98 if gcCompatibilityMode { 99 // forget the alias names 100 switch t.kind { 101 case Byte: 102 t = Typ[Uint8] 103 case Rune: 104 t = Typ[Int32] 105 } 106 } 107 buf.WriteString(t.name) 108 109 case *Array: 110 fmt.Fprintf(buf, "[%d]", t.len) 111 writeType(buf, t.elem, qf, visited) 112 113 case *Slice: 114 buf.WriteString("[]") 115 writeType(buf, t.elem, qf, visited) 116 117 case *Struct: 118 buf.WriteString("struct{") 119 for i, f := range t.fields { 120 if i > 0 { 121 buf.WriteString("; ") 122 } 123 if !f.anonymous { 124 buf.WriteString(f.name) 125 buf.WriteByte(' ') 126 } 127 writeType(buf, f.typ, qf, visited) 128 if tag := t.Tag(i); tag != "" { 129 fmt.Fprintf(buf, " %q", tag) 130 } 131 } 132 buf.WriteByte('}') 133 134 case *Pointer: 135 buf.WriteByte('*') 136 writeType(buf, t.base, qf, visited) 137 138 case *Tuple: 139 writeTuple(buf, t, false, qf, visited) 140 141 case *Signature: 142 buf.WriteString("func") 143 writeSignature(buf, t, qf, visited) 144 145 case *Interface: 146 // We write the source-level methods and embedded types rather 147 // than the actual method set since resolved method signatures 148 // may have non-printable cycles if parameters have anonymous 149 // interface types that (directly or indirectly) embed the 150 // current interface. For instance, consider the result type 151 // of m: 152 // 153 // type T interface{ 154 // m() interface{ T } 155 // } 156 // 157 buf.WriteString("interface{") 158 if gcCompatibilityMode { 159 // print flattened interface 160 // (useful to compare against gc-generated interfaces) 161 for i, m := range t.allMethods { 162 if i > 0 { 163 buf.WriteString("; ") 164 } 165 buf.WriteString(m.name) 166 writeSignature(buf, m.typ.(*Signature), qf, visited) 167 } 168 } else { 169 // print explicit interface methods and embedded types 170 for i, m := range t.methods { 171 if i > 0 { 172 buf.WriteString("; ") 173 } 174 buf.WriteString(m.name) 175 writeSignature(buf, m.typ.(*Signature), qf, visited) 176 } 177 for i, typ := range t.embeddeds { 178 if i > 0 || len(t.methods) > 0 { 179 buf.WriteString("; ") 180 } 181 writeType(buf, typ, qf, visited) 182 } 183 } 184 buf.WriteByte('}') 185 186 case *Map: 187 buf.WriteString("map[") 188 writeType(buf, t.key, qf, visited) 189 buf.WriteByte(']') 190 writeType(buf, t.elem, qf, visited) 191 192 case *Chan: 193 var s string 194 var parens bool 195 switch t.dir { 196 case SendRecv: 197 s = "chan " 198 // chan (<-chan T) requires parentheses 199 if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly { 200 parens = true 201 } 202 case SendOnly: 203 s = "chan<- " 204 case RecvOnly: 205 s = "<-chan " 206 default: 207 panic("unreachable") 208 } 209 buf.WriteString(s) 210 if parens { 211 buf.WriteByte('(') 212 } 213 writeType(buf, t.elem, qf, visited) 214 if parens { 215 buf.WriteByte(')') 216 } 217 218 case *Named: 219 s := "<Named w/o object>" 220 if obj := t.obj; obj != nil { 221 if obj.pkg != nil { 222 writePackage(buf, obj.pkg, qf) 223 } 224 // TODO(gri): function-local named types should be displayed 225 // differently from named types at package level to avoid 226 // ambiguity. 227 s = obj.name 228 } 229 buf.WriteString(s) 230 231 default: 232 // For externally defined implementations of Type. 233 buf.WriteString(t.String()) 234 } 235 } 236 237 func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) { 238 buf.WriteByte('(') 239 if tup != nil { 240 for i, v := range tup.vars { 241 if i > 0 { 242 buf.WriteString(", ") 243 } 244 if v.name != "" { 245 buf.WriteString(v.name) 246 buf.WriteByte(' ') 247 } 248 typ := v.typ 249 if variadic && i == len(tup.vars)-1 { 250 if s, ok := typ.(*Slice); ok { 251 buf.WriteString("...") 252 typ = s.elem 253 } else { 254 // special case: 255 // append(s, "foo"...) leads to signature func([]byte, string...) 256 if t, ok := typ.Underlying().(*Basic); !ok || t.kind != String { 257 panic("internal error: string type expected") 258 } 259 writeType(buf, typ, qf, visited) 260 buf.WriteString("...") 261 continue 262 } 263 } 264 writeType(buf, typ, qf, visited) 265 } 266 } 267 buf.WriteByte(')') 268 } 269 270 // WriteSignature writes the representation of the signature sig to buf, 271 // without a leading "func" keyword. 272 // The Qualifier controls the printing of 273 // package-level objects, and may be nil. 274 func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { 275 writeSignature(buf, sig, qf, make([]Type, 0, 8)) 276 } 277 278 func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) { 279 writeTuple(buf, sig.params, sig.variadic, qf, visited) 280 281 n := sig.results.Len() 282 if n == 0 { 283 // no result 284 return 285 } 286 287 buf.WriteByte(' ') 288 if n == 1 && sig.results.vars[0].name == "" { 289 // single unnamed result 290 writeType(buf, sig.results.vars[0].typ, qf, visited) 291 return 292 } 293 294 // multiple or named result(s) 295 writeTuple(buf, sig.results, false, qf, visited) 296 }