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