golang.org/x/tools@v0.21.0/cmd/godex/print.go (about) 1 // Copyright 2014 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 package main 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/constant" 11 "go/token" 12 "go/types" 13 "io" 14 "math/big" 15 16 "golang.org/x/tools/internal/aliases" 17 ) 18 19 // TODO(gri) use tabwriter for alignment? 20 21 func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) { 22 var p printer 23 p.pkg = pkg 24 p.printPackage(pkg, filter) 25 p.printGccgoExtra(pkg) 26 io.Copy(w, &p.buf) 27 } 28 29 type printer struct { 30 pkg *types.Package 31 buf bytes.Buffer 32 indent int // current indentation level 33 last byte // last byte written 34 } 35 36 func (p *printer) print(s string) { 37 // Write the string one byte at a time. We care about the presence of 38 // newlines for indentation which we will see even in the presence of 39 // (non-corrupted) Unicode; no need to read one rune at a time. 40 for i := 0; i < len(s); i++ { 41 ch := s[i] 42 if ch != '\n' && p.last == '\n' { 43 // Note: This could lead to a range overflow for very large 44 // indentations, but it's extremely unlikely to happen for 45 // non-pathological code. 46 p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent]) 47 } 48 p.buf.WriteByte(ch) 49 p.last = ch 50 } 51 } 52 53 func (p *printer) printf(format string, args ...interface{}) { 54 p.print(fmt.Sprintf(format, args...)) 55 } 56 57 // methodsFor returns the named type and corresponding methods if the type 58 // denoted by obj is not an interface and has methods. Otherwise it returns 59 // the zero value. 60 func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) { 61 named, _ := aliases.Unalias(obj.Type()).(*types.Named) 62 if named == nil { 63 // A type name's type can also be the 64 // exported basic type unsafe.Pointer. 65 return nil, nil 66 } 67 if _, ok := named.Underlying().(*types.Interface); ok { 68 // ignore interfaces 69 return nil, nil 70 } 71 methods := combinedMethodSet(named) 72 if len(methods) == 0 { 73 return nil, nil 74 } 75 return named, methods 76 } 77 78 func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) { 79 // collect objects by kind 80 var ( 81 consts []*types.Const 82 typem []*types.Named // non-interface types with methods 83 typez []*types.TypeName // interfaces or types without methods 84 vars []*types.Var 85 funcs []*types.Func 86 builtins []*types.Builtin 87 methods = make(map[*types.Named][]*types.Selection) // method sets for named types 88 ) 89 scope := pkg.Scope() 90 for _, name := range scope.Names() { 91 obj := scope.Lookup(name) 92 if obj.Exported() { 93 // collect top-level exported and possibly filtered objects 94 if filter == nil || filter(obj) { 95 switch obj := obj.(type) { 96 case *types.Const: 97 consts = append(consts, obj) 98 case *types.TypeName: 99 // group into types with methods and types without 100 if named, m := methodsFor(obj); named != nil { 101 typem = append(typem, named) 102 methods[named] = m 103 } else { 104 typez = append(typez, obj) 105 } 106 case *types.Var: 107 vars = append(vars, obj) 108 case *types.Func: 109 funcs = append(funcs, obj) 110 case *types.Builtin: 111 // for unsafe.Sizeof, etc. 112 builtins = append(builtins, obj) 113 } 114 } 115 } else if filter == nil { 116 // no filtering: collect top-level unexported types with methods 117 if obj, _ := obj.(*types.TypeName); obj != nil { 118 // see case *types.TypeName above 119 if named, m := methodsFor(obj); named != nil { 120 typem = append(typem, named) 121 methods[named] = m 122 } 123 } 124 } 125 } 126 127 p.printf("package %s // %q\n", pkg.Name(), pkg.Path()) 128 129 p.printDecl("const", len(consts), func() { 130 for _, obj := range consts { 131 p.printObj(obj) 132 p.print("\n") 133 } 134 }) 135 136 p.printDecl("var", len(vars), func() { 137 for _, obj := range vars { 138 p.printObj(obj) 139 p.print("\n") 140 } 141 }) 142 143 p.printDecl("type", len(typez), func() { 144 for _, obj := range typez { 145 p.printf("%s ", obj.Name()) 146 typ := obj.Type() 147 if isAlias(obj) { 148 p.print("= ") 149 p.writeType(p.pkg, typ) 150 } else { 151 p.writeType(p.pkg, typ.Underlying()) 152 } 153 p.print("\n") 154 } 155 }) 156 157 // non-interface types with methods 158 for _, named := range typem { 159 first := true 160 if obj := named.Obj(); obj.Exported() { 161 if first { 162 p.print("\n") 163 first = false 164 } 165 p.printf("type %s ", obj.Name()) 166 p.writeType(p.pkg, named.Underlying()) 167 p.print("\n") 168 } 169 for _, m := range methods[named] { 170 if obj := m.Obj(); obj.Exported() { 171 if first { 172 p.print("\n") 173 first = false 174 } 175 p.printFunc(m.Recv(), obj.(*types.Func)) 176 p.print("\n") 177 } 178 } 179 } 180 181 if len(funcs) > 0 { 182 p.print("\n") 183 for _, obj := range funcs { 184 p.printFunc(nil, obj) 185 p.print("\n") 186 } 187 } 188 189 // TODO(gri) better handling of builtins (package unsafe only) 190 if len(builtins) > 0 { 191 p.print("\n") 192 for _, obj := range builtins { 193 p.printf("func %s() // builtin\n", obj.Name()) 194 } 195 } 196 197 p.print("\n") 198 } 199 200 func (p *printer) printDecl(keyword string, n int, printGroup func()) { 201 switch n { 202 case 0: 203 // nothing to do 204 case 1: 205 p.printf("\n%s ", keyword) 206 printGroup() 207 default: 208 p.printf("\n%s (\n", keyword) 209 p.indent++ 210 printGroup() 211 p.indent-- 212 p.print(")\n") 213 } 214 } 215 216 // absInt returns the absolute value of v as a *big.Int. 217 // v must be a numeric value. 218 func absInt(v constant.Value) *big.Int { 219 // compute big-endian representation of v 220 b := constant.Bytes(v) // little-endian 221 for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { 222 b[i], b[j] = b[j], b[i] 223 } 224 return new(big.Int).SetBytes(b) 225 } 226 227 var ( 228 one = big.NewRat(1, 1) 229 ten = big.NewRat(10, 1) 230 ) 231 232 // floatString returns the string representation for a 233 // numeric value v in normalized floating-point format. 234 func floatString(v constant.Value) string { 235 if constant.Sign(v) == 0 { 236 return "0.0" 237 } 238 // x != 0 239 240 // convert |v| into a big.Rat x 241 x := new(big.Rat).SetFrac(absInt(constant.Num(v)), absInt(constant.Denom(v))) 242 243 // normalize x and determine exponent e 244 // (This is not very efficient, but also not speed-critical.) 245 var e int 246 for x.Cmp(ten) >= 0 { 247 x.Quo(x, ten) 248 e++ 249 } 250 for x.Cmp(one) < 0 { 251 x.Mul(x, ten) 252 e-- 253 } 254 255 // TODO(gri) Values such as 1/2 are easier to read in form 0.5 256 // rather than 5.0e-1. Similarly, 1.0e1 is easier to read as 257 // 10.0. Fine-tune best exponent range for readability. 258 259 s := x.FloatString(100) // good-enough precision 260 261 // trim trailing 0's 262 i := len(s) 263 for i > 0 && s[i-1] == '0' { 264 i-- 265 } 266 s = s[:i] 267 268 // add a 0 if the number ends in decimal point 269 if len(s) > 0 && s[len(s)-1] == '.' { 270 s += "0" 271 } 272 273 // add exponent and sign 274 if e != 0 { 275 s += fmt.Sprintf("e%+d", e) 276 } 277 if constant.Sign(v) < 0 { 278 s = "-" + s 279 } 280 281 // TODO(gri) If v is a "small" fraction (i.e., numerator and denominator 282 // are just a small number of decimal digits), add the exact fraction as 283 // a comment. For instance: 3.3333...e-1 /* = 1/3 */ 284 285 return s 286 } 287 288 // valString returns the string representation for the value v. 289 // Setting floatFmt forces an integer value to be formatted in 290 // normalized floating-point format. 291 // TODO(gri) Move this code into package constant. 292 func valString(v constant.Value, floatFmt bool) string { 293 switch v.Kind() { 294 case constant.Int: 295 if floatFmt { 296 return floatString(v) 297 } 298 case constant.Float: 299 return floatString(v) 300 case constant.Complex: 301 re := constant.Real(v) 302 im := constant.Imag(v) 303 var s string 304 if constant.Sign(re) != 0 { 305 s = floatString(re) 306 if constant.Sign(im) >= 0 { 307 s += " + " 308 } else { 309 s += " - " 310 im = constant.UnaryOp(token.SUB, im, 0) // negate im 311 } 312 } 313 // im != 0, otherwise v would be constant.Int or constant.Float 314 return s + floatString(im) + "i" 315 } 316 return v.String() 317 } 318 319 func (p *printer) printObj(obj types.Object) { 320 p.print(obj.Name()) 321 322 typ, basic := obj.Type().Underlying().(*types.Basic) 323 if basic && typ.Info()&types.IsUntyped != 0 { 324 // don't write untyped types 325 } else { 326 p.print(" ") 327 p.writeType(p.pkg, obj.Type()) 328 } 329 330 if obj, ok := obj.(*types.Const); ok { 331 floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0 332 p.print(" = ") 333 p.print(valString(obj.Val(), floatFmt)) 334 } 335 } 336 337 func (p *printer) printFunc(recvType types.Type, obj *types.Func) { 338 p.print("func ") 339 sig := obj.Type().(*types.Signature) 340 if recvType != nil { 341 p.print("(") 342 p.writeType(p.pkg, recvType) 343 p.print(") ") 344 } 345 p.print(obj.Name()) 346 p.writeSignature(p.pkg, sig) 347 } 348 349 // combinedMethodSet returns the method set for a named type T 350 // merged with all the methods of *T that have different names than 351 // the methods of T. 352 // 353 // combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet 354 // but doesn't require a MethodSetCache. 355 // TODO(gri) If this functionality doesn't change over time, consider 356 // just calling IntuitiveMethodSet eventually. 357 func combinedMethodSet(T *types.Named) []*types.Selection { 358 // method set for T 359 mset := types.NewMethodSet(T) 360 var res []*types.Selection 361 for i, n := 0, mset.Len(); i < n; i++ { 362 res = append(res, mset.At(i)) 363 } 364 365 // add all *T methods with names different from T methods 366 pmset := types.NewMethodSet(types.NewPointer(T)) 367 for i, n := 0, pmset.Len(); i < n; i++ { 368 pm := pmset.At(i) 369 if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil { 370 res = append(res, pm) 371 } 372 } 373 374 return res 375 }