GoPLS Viewer

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