GoPLS Viewer

Home|gopls/cmd/guru/pointsto.go
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
5package main
6
7import (
8    "fmt"
9    "go/ast"
10    "go/token"
11    "go/types"
12    "sort"
13
14    "golang.org/x/tools/cmd/guru/serial"
15    "golang.org/x/tools/go/ast/astutil"
16    "golang.org/x/tools/go/loader"
17    "golang.org/x/tools/go/pointer"
18    "golang.org/x/tools/go/ssa"
19    "golang.org/x/tools/go/ssa/ssautil"
20)
21
22// pointsto runs the pointer analysis on the selected expression,
23// and reports its points-to set (for a pointer-like expression)
24// or its dynamic types (for an interface, reflect.Value, or
25// reflect.Type expression) and their points-to sets.
26//
27// All printed sets are sorted to ensure determinism.
28func pointsto(q *Queryerror {
29    lconf := loader.Config{Buildq.Build}
30
31    if err := setPTAScope(&lconfq.Scope); err != nil {
32        return err
33    }
34
35    // Load/parse/type-check the program.
36    lprogerr := loadWithSoftErrors(&lconf)
37    if err != nil {
38        return err
39    }
40
41    qposerr := parseQueryPos(lprogq.Postrue// needs exact pos
42    if err != nil {
43        return err
44    }
45
46    prog := ssautil.CreateProgram(lprogssa.GlobalDebug)
47
48    ptaConfigerr := setupPTA(proglprogq.PTALogq.Reflection)
49    if err != nil {
50        return err
51    }
52
53    pathaction := findInterestingNode(qpos.infoqpos.path)
54    if action != actionExpr {
55        return fmt.Errorf("pointer analysis wants an expression; got %s",
56            astutil.NodeDescription(qpos.path[0]))
57    }
58
59    var expr ast.Expr
60    var obj types.Object
61    switch n := path[0].(type) {
62    case *ast.ValueSpec:
63        // ambiguous ValueSpec containing multiple names
64        return fmt.Errorf("multiple value specification")
65    case *ast.Ident:
66        obj = qpos.info.ObjectOf(n)
67        expr = n
68    case ast.Expr:
69        expr = n
70    default:
71        // TODO(adonovan): is this reachable?
72        return fmt.Errorf("unexpected AST for expr: %T"n)
73    }
74
75    // Reject non-pointerlike types (includes all constants---except nil).
76    // TODO(adonovan): reject nil too.
77    typ := qpos.info.TypeOf(expr)
78    if !pointer.CanPoint(typ) {
79        return fmt.Errorf("pointer analysis wants an expression of reference type; got %s"typ)
80    }
81
82    // Determine the ssa.Value for the expression.
83    var value ssa.Value
84    var isAddr bool
85    if obj != nil {
86        // def/ref of func/var object
87        valueisAddrerr = ssaValueForIdent(progqpos.infoobjpath)
88    } else {
89        valueisAddrerr = ssaValueForExpr(progqpos.infopath)
90    }
91    if err != nil {
92        return err // e.g. trivially dead code
93    }
94
95    // Defer SSA construction till after errors are reported.
96    prog.Build()
97
98    // Run the pointer analysis.
99    ptrserr := runPTA(ptaConfigvalueisAddr)
100    if err != nil {
101        return err // e.g. analytically unreachable
102    }
103
104    q.Output(lprog.Fset, &pointstoResult{
105        qposqpos,
106        typ:  typ,
107        ptrsptrs,
108    })
109    return nil
110}
111
112// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
113// to the root of the AST is path.  isAddr reports whether the
114// ssa.Value is the address denoted by the ast.Ident, not its value.
115func ssaValueForIdent(prog *ssa.Programqinfo *loader.PackageInfoobj types.Objectpath []ast.Node) (value ssa.ValueisAddr boolerr error) {
116    switch obj := obj.(type) {
117    case *types.Var:
118        pkg := prog.Package(qinfo.Pkg)
119        pkg.Build()
120        if vaddr := prog.VarValue(objpkgpath); v != nil {
121            return vaddrnil
122        }
123        return nilfalsefmt.Errorf("can't locate SSA Value for var %s"obj.Name())
124
125    case *types.Func:
126        fn := prog.FuncValue(obj)
127        if fn == nil {
128            return nilfalsefmt.Errorf("%s is an interface method"obj)
129        }
130        // TODO(adonovan): there's no point running PTA on a *Func ident.
131        // Eliminate this feature.
132        return fnfalsenil
133    }
134    panic(obj)
135}
136
137// ssaValueForExpr returns the ssa.Value of the non-ast.Ident
138// expression whose path to the root of the AST is path.
139func ssaValueForExpr(prog *ssa.Programqinfo *loader.PackageInfopath []ast.Node) (value ssa.ValueisAddr boolerr error) {
140    pkg := prog.Package(qinfo.Pkg)
141    pkg.SetDebugMode(true)
142    pkg.Build()
143
144    fn := ssa.EnclosingFunction(pkgpath)
145    if fn == nil {
146        return nilfalsefmt.Errorf("no SSA function built for this location (dead code?)")
147    }
148
149    if vaddr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil {
150        return vaddrnil
151    }
152
153    return nilfalsefmt.Errorf("can't locate SSA Value for expression in %s"fn)
154}
155
156// runPTA runs the pointer analysis of the selected SSA value or address.
157func runPTA(conf *pointer.Configv ssa.ValueisAddr bool) (ptrs []pointerResulterr error) {
158    T := v.Type()
159    if isAddr {
160        conf.AddIndirectQuery(v)
161        T = deref(T)
162    } else {
163        conf.AddQuery(v)
164    }
165    ptares := ptrAnalysis(conf)
166
167    var ptr pointer.Pointer
168    if isAddr {
169        ptr = ptares.IndirectQueries[v]
170    } else {
171        ptr = ptares.Queries[v]
172    }
173    if ptr == (pointer.Pointer{}) {
174        return nilfmt.Errorf("pointer analysis did not find expression (dead code?)")
175    }
176    pts := ptr.PointsTo()
177
178    if pointer.CanHaveDynamicTypes(T) {
179        // Show concrete types for interface/reflect.Value expression.
180        if concs := pts.DynamicTypes(); concs.Len() > 0 {
181            concs.Iterate(func(conc types.Typepta interface{}) {
182                labels := pta.(pointer.PointsToSet).Labels()
183                sort.Sort(byPosAndString(labels)) // to ensure determinism
184                ptrs = append(ptrspointerResult{conclabels})
185            })
186        }
187    } else {
188        // Show labels for other expressions.
189        labels := pts.Labels()
190        sort.Sort(byPosAndString(labels)) // to ensure determinism
191        ptrs = append(ptrspointerResult{Tlabels})
192    }
193    sort.Sort(byTypeString(ptrs)) // to ensure determinism
194    return ptrsnil
195}
196
197type pointerResult struct {
198    typ    types.Type       // type of the pointer (always concrete)
199    labels []*pointer.Label // set of labels
200}
201
202type pointstoResult struct {
203    qpos *queryPos
204    typ  types.Type      // type of expression
205    ptrs []pointerResult // pointer info (typ is concrete => len==1)
206}
207
208func (r *pointstoResultPrintPlain(printf printfFunc) {
209    if pointer.CanHaveDynamicTypes(r.typ) {
210        // Show concrete types for interface, reflect.Type or
211        // reflect.Value expression.
212
213        if len(r.ptrs) > 0 {
214            printf(r.qpos"this %s may contain these dynamic types:"r.qpos.typeString(r.typ))
215            for _ptr := range r.ptrs {
216                var obj types.Object
217                if ntok := deref(ptr.typ).(*types.Named); ok {
218                    obj = nt.Obj()
219                }
220                if len(ptr.labels) > 0 {
221                    printf(obj"\t%s, may point to:"r.qpos.typeString(ptr.typ))
222                    printLabels(printfptr.labels"\t\t")
223                } else {
224                    printf(obj"\t%s"r.qpos.typeString(ptr.typ))
225                }
226            }
227        } else {
228            printf(r.qpos"this %s cannot contain any dynamic types."r.typ)
229        }
230    } else {
231        // Show labels for other expressions.
232        if ptr := r.ptrs[0]; len(ptr.labels) > 0 {
233            printf(r.qpos"this %s may point to these objects:",
234                r.qpos.typeString(r.typ))
235            printLabels(printfptr.labels"\t")
236        } else {
237            printf(r.qpos"this %s may not point to anything.",
238                r.qpos.typeString(r.typ))
239        }
240    }
241}
242
243func (r *pointstoResultJSON(fset *token.FileSet) []byte {
244    var pts []serial.PointsTo
245    for _ptr := range r.ptrs {
246        var namePos string
247        if ntok := deref(ptr.typ).(*types.Named); ok {
248            namePos = fset.Position(nt.Obj().Pos()).String()
249        }
250        var labels []serial.PointsToLabel
251        for _l := range ptr.labels {
252            labels = append(labelsserial.PointsToLabel{
253                Pos:  fset.Position(l.Pos()).String(),
254                Descl.String(),
255            })
256        }
257        pts = append(ptsserial.PointsTo{
258            Type:    r.qpos.typeString(ptr.typ),
259            NamePosnamePos,
260            Labels:  labels,
261        })
262    }
263    return toJSON(pts)
264}
265
266type byTypeString []pointerResult
267
268func (a byTypeStringLen() int           { return len(a) }
269func (a byTypeStringLess(ij intbool { return a[i].typ.String() < a[j].typ.String() }
270func (a byTypeStringSwap(ij int)      { a[i], a[j] = a[j], a[i] }
271
272type byPosAndString []*pointer.Label
273
274func (a byPosAndStringLen() int { return len(a) }
275func (a byPosAndStringLess(ij intbool {
276    cmp := a[i].Pos() - a[j].Pos()
277    return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String())
278}
279func (a byPosAndStringSwap(ij int) { a[i], a[j] = a[j], a[i] }
280
281func printLabels(printf printfFunclabels []*pointer.Labelprefix string) {
282    // TODO(adonovan): due to context-sensitivity, many of these
283    // labels may differ only by context, which isn't apparent.
284    for _label := range labels {
285        printf(label"%s%s"prefixlabel)
286    }
287}
288
MembersX
pointerResult
pointstoResult.JSON.RangeStmt_6952.BlockStmt.RangeStmt_7147.l
byTypeString.Less.a
pointsto.qpos
pointsto.ptaConfig
pointsto.typ
pointsto.value
runPTA
pointerResult.labels
pointstoResult.typ
pointstoResult.ptrs
pointsto.lconf
pointsto.expr
ssaValueForExpr.isAddr
ssaValueForExpr.addr
byPosAndString.Swap.i
pointstoResult.JSON.pts
byTypeString.Swap.a
byTypeString.Swap.j
byPosAndString
ssaValueForIdent.value
byPosAndString.Less.i
byPosAndString.Swap.a
byTypeString.Swap.i
printLabels.RangeStmt_8211.label
pointsto.err
pointsto.obj
pointstoResult.JSON
pointstoResult.JSON.fset
pointsto
ssaValueForIdent.BlockStmt.fn
ssaValueForExpr.fn
runPTA.BlockStmt.concs
pointstoResult.PrintPlain.r
pointstoResult.PrintPlain.BlockStmt.BlockStmt.RangeStmt_6097.BlockStmt.obj
pointstoResult.JSON.RangeStmt_6952.BlockStmt.namePos
byTypeString.Len.a
pointsto.action
ssaValueForIdent.BlockStmt.v
ssaValueForExpr.err
runPTA.conf
byTypeString.Less.i
ssaValueForIdent.BlockStmt.addr
ssaValueForExpr.value
pointstoResult
pointstoResult.qpos
pointstoResult.JSON.r
pointstoResult.JSON.RangeStmt_6952.BlockStmt.labels
byPosAndString.Len
byPosAndString.Swap.j
ssaValueForIdent
ssaValueForExpr.pkg
runPTA.ptares
pointstoResult.PrintPlain
byTypeString
byPosAndString.Less.a
pointsto.path
ssaValueForIdent.qinfo
runPTA.BlockStmt.BlockStmt.BlockStmt.labels
pointstoResult.PrintPlain.printf
ssaValueForExpr.path
pointsto.isAddr
ssaValueForIdent.path
ssaValueForIdent.BlockStmt.pkg
ssaValueForExpr.prog
runPTA.pts
pointsto.q
pointsto.lprog
pointsto.prog
runPTA.err
printLabels.labels
ssaValueForIdent.prog
ssaValueForIdent.isAddr
pointerResult.typ
byPosAndString.Less
byTypeString.Len
printLabels.printf
printLabels.prefix
pointsto.ptrs
runPTA.isAddr
runPTA.BlockStmt.labels
pointstoResult.JSON.RangeStmt_6952.ptr
runPTA.ptrs
runPTA.ptr
byTypeString.Less
byTypeString.Less.j
ssaValueForIdent.obj
ssaValueForIdent.err
ssaValueForExpr
runPTA.v
byPosAndString.Swap
ssaValueForExpr.qinfo
runPTA.T
pointstoResult.PrintPlain.BlockStmt.BlockStmt.RangeStmt_6097.ptr
byPosAndString.Less.j
ssaValueForExpr.v
byTypeString.Swap
byPosAndString.Len.a
printLabels
Members
X