cmd/{guru,callgraph}: stop using go/pointer
This change removes the -algo=pta option from cmd/callgraph,
and all the subcommands of cmd/guru, that use pointer analysis.
These features have been poorly supported for a long time,
and the pointer analysis package is about to be tagged and
deleted.
Updates golang/go#59676
Change-Id: Id4ded651b8385c588991d01377b2f087d14ae191
Reviewed-on: https://go-review.googlesource.com/c/tools/+/499696
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/cmd/callgraph/main.go b/cmd/callgraph/main.go
index eb8c0d1..33f7dfa 100644
--- a/cmd/callgraph/main.go
+++ b/cmd/callgraph/main.go
@@ -20,14 +20,12 @@
// callee file/line/col
import (
- "bufio"
"bytes"
"flag"
"fmt"
"go/build"
"go/token"
"io"
- "log"
"os"
"runtime"
"text/template"
@@ -39,7 +37,6 @@
"golang.org/x/tools/go/callgraph/static"
"golang.org/x/tools/go/callgraph/vta"
"golang.org/x/tools/go/packages"
- "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
@@ -47,7 +44,7 @@
// flags
var (
algoFlag = flag.String("algo", "rta",
- `Call graph construction algorithm (static, cha, rta, vta, pta)`)
+ `Call graph construction algorithm (static, cha, rta, vta)`)
testFlag = flag.Bool("test", false,
"Loads test code (*_test.go) for imported packages")
@@ -55,9 +52,6 @@
formatFlag = flag.String("format",
"{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}",
"A template expression specifying how to format an edge")
-
- ptalogFlag = flag.String("ptalog", "",
- "Location of the points-to analysis log file, or empty to disable logging.")
)
func init() {
@@ -68,7 +62,7 @@
Usage:
- callgraph [-algo=static|cha|rta|vta|pta] [-test] [-format=...] package...
+ callgraph [-algo=static|cha|rta|vta] [-test] [-format=...] package...
Flags:
@@ -78,11 +72,10 @@
cha Class Hierarchy Analysis
rta Rapid Type Analysis
vta Variable Type Analysis
- pta inclusion-based Points-To Analysis
The algorithms are ordered by increasing precision in their
treatment of dynamic calls (and thus also computational cost).
- RTA and PTA require a whole program (main or test), and
+ RTA requires a whole program (main or test), and
include only functions reachable from main.
-test Include the package's tests in the analysis.
@@ -132,9 +125,9 @@
$GOROOT/src/net/http/triv.go | sort | uniq
Show functions that make dynamic calls into the 'fmt' test package,
- using the pointer analysis algorithm:
+ using the Rapid Type Analysis algorithm:
- callgraph -format='{{.Caller}} -{{.Dynamic}}-> {{.Callee}}' -test -algo=pta fmt |
+ callgraph -format='{{.Caller}} -{{.Dynamic}}-> {{.Callee}}' -test -algo=rta fmt |
sed -ne 's/-dynamic-/--/p' |
sed -ne 's/-->.*fmt_test.*$//p' | sort | uniq
@@ -205,39 +198,7 @@
cg = cha.CallGraph(prog)
case "pta":
- // Set up points-to analysis log file.
- var ptalog io.Writer
- if *ptalogFlag != "" {
- if f, err := os.Create(*ptalogFlag); err != nil {
- log.Fatalf("Failed to create PTA log file: %s", err)
- } else {
- buf := bufio.NewWriter(f)
- ptalog = buf
- defer func() {
- if err := buf.Flush(); err != nil {
- log.Printf("flush: %s", err)
- }
- if err := f.Close(); err != nil {
- log.Printf("close: %s", err)
- }
- }()
- }
- }
-
- mains, err := mainPackages(pkgs)
- if err != nil {
- return err
- }
- config := &pointer.Config{
- Mains: mains,
- BuildCallGraph: true,
- Log: ptalog,
- }
- ptares, err := pointer.Analyze(config)
- if err != nil {
- return err // internal error in pointer analysis
- }
- cg = ptares.CallGraph
+ return fmt.Errorf("pointer analysis is no longer supported (see Go issue #59676)")
case "rta":
mains, err := mainPackages(pkgs)
diff --git a/cmd/callgraph/main_test.go b/cmd/callgraph/main_test.go
index c8bee87..afcb7a9 100644
--- a/cmd/callgraph/main_test.go
+++ b/cmd/callgraph/main_test.go
@@ -65,14 +65,6 @@
"pkg.main --> pkg.main2",
"pkg.main2 --> (pkg.D).f",
}},
- {"pta", false, []string{
- // pta distinguishes main->C, main2->D. Also has a root node.
- `<root> --> pkg.init`,
- `<root> --> pkg.main`,
- `pkg.main --> (pkg.C).f`,
- `pkg.main --> pkg.main2`,
- `pkg.main2 --> (pkg.D).f`,
- }},
// tests: both the package's main and the test's main are called.
// The callgraph includes all the guts of the "testing" package.
{"rta", true, []string{
@@ -87,14 +79,6 @@
`pkg.Example --> (pkg.C).f`,
`pkg.main --> (pkg.C).f`,
}},
- {"pta", true, []string{
- `<root> --> pkg.test.main`,
- `<root> --> pkg.main`,
- `pkg.test.main --> testing.MainStart`,
- `testing.runExample --> pkg.Example`,
- `pkg.Example --> (pkg.C).f`,
- `pkg.main --> (pkg.C).f`,
- }},
} {
const format = "{{.Caller}} --> {{.Callee}}"
stdout = new(bytes.Buffer)
diff --git a/cmd/guru/callees.go b/cmd/guru/callees.go
deleted file mode 100644
index 5978957..0000000
--- a/cmd/guru/callees.go
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "go/types"
- "sort"
-
- "golang.org/x/tools/cmd/guru/serial"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-)
-
-// The callees function reports the possible callees of the function call site
-// identified by the specified source location.
-func callees(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
-
- // Load/parse/type-check the program.
- lprog, err := loadWithSoftErrors(&lconf)
- if err != nil {
- return err
- }
-
- qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
- if err != nil {
- return err
- }
-
- // Determine the enclosing call for the specified position.
- var e *ast.CallExpr
- for _, n := range qpos.path {
- if e, _ = n.(*ast.CallExpr); e != nil {
- break
- }
- }
- if e == nil {
- return fmt.Errorf("there is no function call here")
- }
- // TODO(adonovan): issue an error if the call is "too far
- // away" from the current selection, as this most likely is
- // not what the user intended.
-
- // Reject type conversions.
- if qpos.info.Types[e.Fun].IsType() {
- return fmt.Errorf("this is a type conversion, not a function call")
- }
-
- // Deal with obviously static calls before constructing SSA form.
- // Some static calls may yet require SSA construction,
- // e.g. f := func(){}; f().
- switch funexpr := unparen(e.Fun).(type) {
- case *ast.Ident:
- switch obj := qpos.info.Uses[funexpr].(type) {
- case *types.Builtin:
- // Reject calls to built-ins.
- return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
- case *types.Func:
- // This is a static function call
- q.Output(lprog.Fset, &calleesTypesResult{
- site: e,
- callee: obj,
- })
- return nil
- }
- case *ast.SelectorExpr:
- sel := qpos.info.Selections[funexpr]
- if sel == nil {
- // qualified identifier.
- // May refer to top level function variable
- // or to top level function.
- callee := qpos.info.Uses[funexpr.Sel]
- if obj, ok := callee.(*types.Func); ok {
- q.Output(lprog.Fset, &calleesTypesResult{
- site: e,
- callee: obj,
- })
- return nil
- }
- } else if sel.Kind() == types.MethodVal {
- // Inspect the receiver type of the selected method.
- // If it is concrete, the call is statically dispatched.
- // (Due to implicit field selections, it is not enough to look
- // at sel.Recv(), the type of the actual receiver expression.)
- method := sel.Obj().(*types.Func)
- recvtype := method.Type().(*types.Signature).Recv().Type()
- if !types.IsInterface(recvtype) {
- // static method call
- q.Output(lprog.Fset, &calleesTypesResult{
- site: e,
- callee: method,
- })
- return nil
- }
- }
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- pkg := prog.Package(qpos.info.Pkg)
- if pkg == nil {
- return fmt.Errorf("no SSA package")
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- // Ascertain calling function and call site.
- callerFn := ssa.EnclosingFunction(pkg, qpos.path)
- if callerFn == nil {
- return fmt.Errorf("no SSA function built for this location (dead code?)")
- }
-
- // Find the call site.
- site, err := findCallSite(callerFn, e)
- if err != nil {
- return err
- }
-
- funcs, err := findCallees(ptaConfig, site)
- if err != nil {
- return err
- }
-
- q.Output(lprog.Fset, &calleesSSAResult{
- site: site,
- funcs: funcs,
- })
- return nil
-}
-
-func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) {
- instr, _ := fn.ValueForExpr(call)
- callInstr, _ := instr.(ssa.CallInstruction)
- if instr == nil {
- return nil, fmt.Errorf("this call site is unreachable in this analysis")
- }
- return callInstr, nil
-}
-
-func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) {
- // Avoid running the pointer analysis for static calls.
- if callee := site.Common().StaticCallee(); callee != nil {
- switch callee.String() {
- case "runtime.SetFinalizer", "(reflect.Value).Call":
- // The PTA treats calls to these intrinsics as dynamic.
- // TODO(adonovan): avoid reliance on PTA internals.
-
- default:
- return []*ssa.Function{callee}, nil // singleton
- }
- }
-
- // Dynamic call: use pointer analysis.
- conf.BuildCallGraph = true
- cg := ptrAnalysis(conf).CallGraph
- cg.DeleteSyntheticNodes()
-
- // Find all call edges from the site.
- n := cg.Nodes[site.Parent()]
- if n == nil {
- return nil, fmt.Errorf("this call site is unreachable in this analysis")
- }
- calleesMap := make(map[*ssa.Function]bool)
- for _, edge := range n.Out {
- if edge.Site == site {
- calleesMap[edge.Callee.Func] = true
- }
- }
-
- // De-duplicate and sort.
- funcs := make([]*ssa.Function, 0, len(calleesMap))
- for f := range calleesMap {
- funcs = append(funcs, f)
- }
- sort.Sort(byFuncPos(funcs))
- return funcs, nil
-}
-
-type calleesSSAResult struct {
- site ssa.CallInstruction
- funcs []*ssa.Function
-}
-
-type calleesTypesResult struct {
- site *ast.CallExpr
- callee *types.Func
-}
-
-func (r *calleesSSAResult) PrintPlain(printf printfFunc) {
- if len(r.funcs) == 0 {
- // dynamic call on a provably nil func/interface
- printf(r.site, "%s on nil value", r.site.Common().Description())
- } else {
- printf(r.site, "this %s dispatches to:", r.site.Common().Description())
- for _, callee := range r.funcs {
- printf(callee, "\t%s", callee)
- }
- }
-}
-
-func (r *calleesSSAResult) JSON(fset *token.FileSet) []byte {
- j := &serial.Callees{
- Pos: fset.Position(r.site.Pos()).String(),
- Desc: r.site.Common().Description(),
- }
- for _, callee := range r.funcs {
- j.Callees = append(j.Callees, &serial.Callee{
- Name: callee.String(),
- Pos: fset.Position(callee.Pos()).String(),
- })
- }
- return toJSON(j)
-}
-
-func (r *calleesTypesResult) PrintPlain(printf printfFunc) {
- printf(r.site, "this static function call dispatches to:")
- printf(r.callee, "\t%s", r.callee.FullName())
-}
-
-func (r *calleesTypesResult) JSON(fset *token.FileSet) []byte {
- j := &serial.Callees{
- Pos: fset.Position(r.site.Pos()).String(),
- Desc: "static function call",
- }
- j.Callees = []*serial.Callee{
- {
- Name: r.callee.FullName(),
- Pos: fset.Position(r.callee.Pos()).String(),
- },
- }
- return toJSON(j)
-}
-
-// NB: byFuncPos is not deterministic across packages since it depends on load order.
-// Use lessPos if the tests need it.
-type byFuncPos []*ssa.Function
-
-func (a byFuncPos) Len() int { return len(a) }
-func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
-func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/cmd/guru/callers.go b/cmd/guru/callers.go
deleted file mode 100644
index 8afefba..0000000
--- a/cmd/guru/callers.go
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "go/token"
- "go/types"
-
- "golang.org/x/tools/cmd/guru/serial"
- "golang.org/x/tools/go/callgraph"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-)
-
-// The callers function reports the possible callers of the function
-// immediately enclosing the specified source location.
-func callers(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
-
- // Load/parse/type-check the program.
- lprog, err := loadWithSoftErrors(&lconf)
- if err != nil {
- return err
- }
-
- qpos, err := parseQueryPos(lprog, q.Pos, false)
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, 0)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- pkg := prog.Package(qpos.info.Pkg)
- if pkg == nil {
- return fmt.Errorf("no SSA package")
- }
- if !ssa.HasEnclosingFunction(pkg, qpos.path) {
- return fmt.Errorf("this position is not inside a function")
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- target := ssa.EnclosingFunction(pkg, qpos.path)
- if target == nil {
- return fmt.Errorf("no SSA function built for this location (dead code?)")
- }
-
- // If the function is never address-taken, all calls are direct
- // and can be found quickly by inspecting the whole SSA program.
- cg := directCallsTo(target, entryPoints(ptaConfig.Mains))
- if cg == nil {
- // Run the pointer analysis, recording each
- // call found to originate from target.
- // (Pointer analysis may return fewer results than
- // directCallsTo because it ignores dead code.)
- ptaConfig.BuildCallGraph = true
- cg = ptrAnalysis(ptaConfig).CallGraph
- }
- cg.DeleteSyntheticNodes()
- edges := cg.CreateNode(target).In
-
- // TODO(adonovan): sort + dedup calls to ensure test determinism.
-
- q.Output(lprog.Fset, &callersResult{
- target: target,
- callgraph: cg,
- edges: edges,
- })
- return nil
-}
-
-// directCallsTo inspects the whole program and returns a callgraph
-// containing edges for all direct calls to the target function.
-// directCallsTo returns nil if the function is ever address-taken.
-func directCallsTo(target *ssa.Function, entrypoints []*ssa.Function) *callgraph.Graph {
- cg := callgraph.New(nil) // use nil as root *Function
- targetNode := cg.CreateNode(target)
-
- // Is the function a program entry point?
- // If so, add edge from callgraph root.
- for _, f := range entrypoints {
- if f == target {
- callgraph.AddEdge(cg.Root, nil, targetNode)
- }
- }
-
- // Find receiver type (for methods).
- var recvType types.Type
- if recv := target.Signature.Recv(); recv != nil {
- recvType = recv.Type()
- }
-
- // Find all direct calls to function,
- // or a place where its address is taken.
- var space [32]*ssa.Value // preallocate
- for fn := range ssautil.AllFunctions(target.Prog) {
- for _, b := range fn.Blocks {
- for _, instr := range b.Instrs {
- // Is this a method (T).f of a concrete type T
- // whose runtime type descriptor is address-taken?
- // (To be fully sound, we would have to check that
- // the type doesn't make it to reflection as a
- // subelement of some other address-taken type.)
- if recvType != nil {
- if mi, ok := instr.(*ssa.MakeInterface); ok {
- if types.Identical(mi.X.Type(), recvType) {
- return nil // T is address-taken
- }
- if ptr, ok := mi.X.Type().(*types.Pointer); ok &&
- types.Identical(ptr.Elem(), recvType) {
- return nil // *T is address-taken
- }
- }
- }
-
- // Direct call to target?
- rands := instr.Operands(space[:0])
- if site, ok := instr.(ssa.CallInstruction); ok &&
- site.Common().Value == target {
- callgraph.AddEdge(cg.CreateNode(fn), site, targetNode)
- rands = rands[1:] // skip .Value (rands[0])
- }
-
- // Address-taken?
- for _, rand := range rands {
- if rand != nil && *rand == target {
- return nil
- }
- }
- }
- }
- }
-
- return cg
-}
-
-func entryPoints(mains []*ssa.Package) []*ssa.Function {
- var entrypoints []*ssa.Function
- for _, pkg := range mains {
- entrypoints = append(entrypoints, pkg.Func("init"))
- if main := pkg.Func("main"); main != nil && pkg.Pkg.Name() == "main" {
- entrypoints = append(entrypoints, main)
- }
- }
- return entrypoints
-}
-
-type callersResult struct {
- target *ssa.Function
- callgraph *callgraph.Graph
- edges []*callgraph.Edge
-}
-
-func (r *callersResult) PrintPlain(printf printfFunc) {
- root := r.callgraph.Root
- if r.edges == nil {
- printf(r.target, "%s is not reachable in this program.", r.target)
- } else {
- printf(r.target, "%s is called from these %d sites:", r.target, len(r.edges))
- for _, edge := range r.edges {
- if edge.Caller == root {
- printf(r.target, "the root of the call graph")
- } else {
- printf(edge, "\t%s from %s", edge.Description(), edge.Caller.Func)
- }
- }
- }
-}
-
-func (r *callersResult) JSON(fset *token.FileSet) []byte {
- var callers []serial.Caller
- for _, edge := range r.edges {
- callers = append(callers, serial.Caller{
- Caller: edge.Caller.Func.String(),
- Pos: fset.Position(edge.Pos()).String(),
- Desc: edge.Description(),
- })
- }
- return toJSON(callers)
-}
diff --git a/cmd/guru/callstack.go b/cmd/guru/callstack.go
deleted file mode 100644
index c3d6d6e..0000000
--- a/cmd/guru/callstack.go
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "go/token"
-
- "golang.org/x/tools/cmd/guru/serial"
- "golang.org/x/tools/go/callgraph"
- "golang.org/x/tools/go/callgraph/static"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-)
-
-// The callstack function displays an arbitrary path from a root of the callgraph
-// to the function at the current position.
-//
-// The information may be misleading in a context-insensitive
-// analysis. e.g. the call path X->Y->Z might be infeasible if Y never
-// calls Z when it is called from X. TODO(adonovan): think about UI.
-//
-// TODO(adonovan): permit user to specify a starting point other than
-// the analysis root.
-func callstack(q *Query) error {
- fset := token.NewFileSet()
- lconf := loader.Config{Fset: fset, Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
-
- // Load/parse/type-check the program.
- lprog, err := loadWithSoftErrors(&lconf)
- if err != nil {
- return err
- }
-
- qpos, err := parseQueryPos(lprog, q.Pos, false)
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, 0)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- pkg := prog.Package(qpos.info.Pkg)
- if pkg == nil {
- return fmt.Errorf("no SSA package")
- }
-
- if !ssa.HasEnclosingFunction(pkg, qpos.path) {
- return fmt.Errorf("this position is not inside a function")
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- target := ssa.EnclosingFunction(pkg, qpos.path)
- if target == nil {
- return fmt.Errorf("no SSA function built for this location (dead code?)")
- }
-
- var callpath []*callgraph.Edge
- isEnd := func(n *callgraph.Node) bool { return n.Func == target }
-
- // First, build a callgraph containing only static call edges,
- // and search for an arbitrary path from a root to the target function.
- // This is quick, and the user wants a static path if one exists.
- cg := static.CallGraph(prog)
- cg.DeleteSyntheticNodes()
- for _, ep := range entryPoints(ptaConfig.Mains) {
- callpath = callgraph.PathSearch(cg.CreateNode(ep), isEnd)
- if callpath != nil {
- break
- }
- }
-
- // No fully static path found.
- // Run the pointer analysis and build a complete call graph.
- if callpath == nil {
- ptaConfig.BuildCallGraph = true
- cg := ptrAnalysis(ptaConfig).CallGraph
- cg.DeleteSyntheticNodes()
- callpath = callgraph.PathSearch(cg.Root, isEnd)
- if callpath != nil {
- callpath = callpath[1:] // remove synthetic edge from <root>
- }
- }
-
- q.Output(fset, &callstackResult{
- qpos: qpos,
- target: target,
- callpath: callpath,
- })
- return nil
-}
-
-type callstackResult struct {
- qpos *queryPos
- target *ssa.Function
- callpath []*callgraph.Edge
-}
-
-func (r *callstackResult) PrintPlain(printf printfFunc) {
- if r.callpath != nil {
- printf(r.qpos, "Found a call path from root to %s", r.target)
- printf(r.target, "%s", r.target)
- for i := len(r.callpath) - 1; i >= 0; i-- {
- edge := r.callpath[i]
- printf(edge, "%s from %s", edge.Description(), edge.Caller.Func)
- }
- } else {
- printf(r.target, "%s is unreachable in this analysis scope", r.target)
- }
-}
-
-func (r *callstackResult) JSON(fset *token.FileSet) []byte {
- var callers []serial.Caller
- for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first)
- edge := r.callpath[i]
- callers = append(callers, serial.Caller{
- Pos: fset.Position(edge.Pos()).String(),
- Caller: edge.Caller.Func.String(),
- Desc: edge.Description(),
- })
- }
- return toJSON(&serial.CallStack{
- Pos: fset.Position(r.target.Pos()).String(),
- Target: r.target.String(),
- Callers: callers,
- })
-}
diff --git a/cmd/guru/guru.go b/cmd/guru/guru.go
index f8e6cfa..575136c 100644
--- a/cmd/guru/guru.go
+++ b/cmd/guru/guru.go
@@ -24,10 +24,7 @@
"strings"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
)
type printfFunc func(pos interface{}, format string, args ...interface{})
@@ -70,11 +67,6 @@
Pos string // query position
Build *build.Context // package loading configuration
- // pointer analysis options
- Scope []string // main packages in (*loader.Config).FromArgs syntax
- PTALog io.Writer // (optional) pointer-analysis log file
- Reflection bool // model reflection soundly (currently slow).
-
// result-printing function, safe for concurrent use
Output func(*token.FileSet, QueryResult)
}
@@ -82,18 +74,6 @@
// Run runs an guru query and populates its Fset and Result.
func Run(mode string, q *Query) error {
switch mode {
- case "callees":
- return callees(q)
- case "callers":
- return callers(q)
- case "callstack":
- return callstack(q)
- case "peers":
- return peers(q)
- case "pointsto":
- return pointsto(q)
- case "whicherrs":
- return whicherrs(q)
case "definition":
return definition(q)
case "describe":
@@ -106,46 +86,13 @@
return referrers(q)
case "what":
return what(q)
+ case "callees", "callers", "pointsto", "whicherrs", "callstack", "peers":
+ return fmt.Errorf("mode %q is no longer supported (see Go issue #59676)", mode)
default:
return fmt.Errorf("invalid mode: %q", mode)
}
}
-func setPTAScope(lconf *loader.Config, scope []string) error {
- pkgs := buildutil.ExpandPatterns(lconf.Build, scope)
- if len(pkgs) == 0 {
- return fmt.Errorf("no packages specified for pointer analysis scope")
- }
- // The value of each entry in pkgs is true,
- // giving ImportWithTests (not Import) semantics.
- lconf.ImportPkgs = pkgs
- return nil
-}
-
-// Create a pointer.Config whose scope is the initial packages of lprog
-// and their dependencies.
-func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
- // For each initial package (specified on the command line),
- // analyze the package if it has a main function.
- var mains []*ssa.Package
- for _, info := range lprog.InitialPackages() {
- p := prog.Package(info.Pkg)
-
- // Add package to the pointer analysis scope.
- if p.Pkg.Name() == "main" && p.Func("main") != nil {
- mains = append(mains, p)
- }
- }
- if mains == nil {
- return nil, fmt.Errorf("analysis scope has no main and no tests")
- }
- return &pointer.Config{
- Log: ptaLog,
- Reflection: reflection,
- Mains: mains,
- }, nil
-}
-
// importQueryPackage finds the package P containing the
// query position and tells conf to import it.
// It returns the package's path.
@@ -307,15 +254,6 @@
lconf.TypeChecker.Error = func(err error) {}
}
-// ptrAnalysis runs the pointer analysis and returns its result.
-func ptrAnalysis(conf *pointer.Config) *pointer.Result {
- result, err := pointer.Analyze(conf)
- if err != nil {
- panic(err) // pointer analysis internal error
- }
- return result
-}
-
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
// deref returns a pointer's element type; otherwise it returns typ.
@@ -333,7 +271,7 @@
// - a token.Pos, denoting a position
// - an ast.Node, denoting an interval
// - anything with a Pos() method:
-// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
+// ssa.Member, ssa.Value, ssa.Instruction, types.Object, etc.
// - a QueryPos, denoting the extent of the user's query.
// - nil, meaning no position at all.
//
diff --git a/cmd/guru/guru_test.go b/cmd/guru/guru_test.go
index 44ec2ca..905a9e2 100644
--- a/cmd/guru/guru_test.go
+++ b/cmd/guru/guru_test.go
@@ -172,7 +172,6 @@
var buildContext = build.Default
buildContext.GOPATH = "testdata"
- pkg := filepath.Dir(strings.TrimPrefix(q.filename, "testdata/src/"))
gopathAbs, _ := filepath.Abs(buildContext.GOPATH)
@@ -195,11 +194,9 @@
}
query := guru.Query{
- Pos: q.queryPos,
- Build: &buildContext,
- Scope: []string{pkg},
- Reflection: true,
- Output: outputFn,
+ Pos: q.queryPos,
+ Build: &buildContext,
+ Output: outputFn,
}
if err := guru.Run(q.verb, &query); err != nil {
@@ -243,28 +240,17 @@
for _, filename := range []string{
"testdata/src/alias/alias.go",
- "testdata/src/calls/main.go",
"testdata/src/describe/main.go",
"testdata/src/freevars/main.go",
"testdata/src/implements/main.go",
"testdata/src/implements-methods/main.go",
"testdata/src/imports/main.go",
- "testdata/src/peers/main.go",
- "testdata/src/pointsto/main.go",
"testdata/src/referrers/main.go",
- "testdata/src/reflection/main.go",
"testdata/src/what/main.go",
- "testdata/src/whicherrs/main.go",
- "testdata/src/softerrs/main.go",
- // JSON:
- // TODO(adonovan): most of these are very similar; combine them.
- "testdata/src/calls-json/main.go",
- "testdata/src/peers-json/main.go",
"testdata/src/definition-json/main.go",
"testdata/src/describe-json/main.go",
"testdata/src/implements-json/main.go",
"testdata/src/implements-methods-json/main.go",
- "testdata/src/pointsto-json/main.go",
"testdata/src/referrers-json/main.go",
"testdata/src/what-json/main.go",
} {
diff --git a/cmd/guru/implements.go b/cmd/guru/implements.go
index 527e88b..9e4d0db 100644
--- a/cmd/guru/implements.go
+++ b/cmd/guru/implements.go
@@ -34,12 +34,7 @@
}
// Set the packages to search.
- if len(q.Scope) > 0 {
- // Inspect all packages in the analysis scope, if specified.
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
- } else {
+ {
// Otherwise inspect the forward and reverse
// transitive closure of the selected package.
// (In theory even this is incomplete.)
diff --git a/cmd/guru/main.go b/cmd/guru/main.go
index 7ad083e..283b1db 100644
--- a/cmd/guru/main.go
+++ b/cmd/guru/main.go
@@ -10,18 +10,15 @@
package main // import "golang.org/x/tools/cmd/guru"
import (
- "bufio"
"flag"
"fmt"
"go/build"
"go/token"
- "io"
"log"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
- "strings"
"sync"
"golang.org/x/tools/go/buildutil"
@@ -64,10 +61,8 @@
freevars show free variables of selection
implements show 'implements' relation for selected type or method
peers show send/receive corresponding to selected channel op
- pointsto show variables the selected pointer may point to
referrers show all refs to entity denoted by selected identifier
what show basic information about the selected syntax node
- whicherrs show possible values of the selected error variable
The position argument specifies the filename and byte offset (or range)
of the syntax element to query. For example:
@@ -137,25 +132,6 @@
os.Exit(2)
}
- // Set up points-to analysis log file.
- var ptalog io.Writer
- if *ptalogFlag != "" {
- if f, err := os.Create(*ptalogFlag); err != nil {
- log.Fatalf("Failed to create PTA log file: %s", err)
- } else {
- buf := bufio.NewWriter(f)
- ptalog = buf
- defer func() {
- if err := buf.Flush(); err != nil {
- log.Printf("flush: %s", err)
- }
- if err := f.Close(); err != nil {
- log.Printf("close: %s", err)
- }
- }()
- }
- }
-
// Profiling support.
if *cpuprofileFlag != "" {
f, err := os.Create(*cpuprofileFlag)
@@ -202,20 +178,11 @@
}
}
- // Avoid corner case of split("").
- var scope []string
- if *scopeFlag != "" {
- scope = strings.Split(*scopeFlag, ",")
- }
-
// Ask the guru.
query := Query{
- Pos: posn,
- Build: ctxt,
- Scope: scope,
- PTALog: ptalog,
- Reflection: *reflectFlag,
- Output: output,
+ Pos: posn,
+ Build: ctxt,
+ Output: output,
}
if err := Run(mode, &query); err != nil {
diff --git a/cmd/guru/peers.go b/cmd/guru/peers.go
deleted file mode 100644
index 6e138bf0..0000000
--- a/cmd/guru/peers.go
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "go/types"
- "sort"
-
- "golang.org/x/tools/cmd/guru/serial"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-)
-
-// peers enumerates, for a given channel send (or receive) operation,
-// the set of possible receives (or sends) that correspond to it.
-//
-// TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
-// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
-// or the implicit receive in "for v := range ch".
-func peers(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
-
- // Load/parse/type-check the program.
- lprog, err := loadWithSoftErrors(&lconf)
- if err != nil {
- return err
- }
-
- qpos, err := parseQueryPos(lprog, q.Pos, false)
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- opPos := findOp(qpos)
- if opPos == token.NoPos {
- return fmt.Errorf("there is no channel operation here")
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- var queryOp chanOp // the originating send or receive operation
- var ops []chanOp // all sends/receives of opposite direction
-
- // Look at all channel operations in the whole ssa.Program.
- // Build a list of those of same type as the query.
- allFuncs := ssautil.AllFunctions(prog)
- for fn := range allFuncs {
- for _, b := range fn.Blocks {
- for _, instr := range b.Instrs {
- for _, op := range chanOps(instr) {
- ops = append(ops, op)
- if op.pos == opPos {
- queryOp = op // we found the query op
- }
- }
- }
- }
- }
- if queryOp.ch == nil {
- return fmt.Errorf("ssa.Instruction for send/receive not found")
- }
-
- // Discard operations of wrong channel element type.
- // Build set of channel ssa.Values as query to pointer analysis.
- // We compare channels by element types, not channel types, to
- // ignore both directionality and type names.
- queryType := queryOp.ch.Type()
- queryElemType := queryType.Underlying().(*types.Chan).Elem()
- ptaConfig.AddQuery(queryOp.ch)
- i := 0
- for _, op := range ops {
- if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
- ptaConfig.AddQuery(op.ch)
- ops[i] = op
- i++
- }
- }
- ops = ops[:i]
-
- // Run the pointer analysis.
- ptares := ptrAnalysis(ptaConfig)
-
- // Find the points-to set.
- queryChanPtr := ptares.Queries[queryOp.ch]
-
- // Ascertain which make(chan) labels the query's channel can alias.
- var makes []token.Pos
- for _, label := range queryChanPtr.PointsTo().Labels() {
- makes = append(makes, label.Pos())
- }
- sort.Sort(byPos(makes))
-
- // Ascertain which channel operations can alias the same make(chan) labels.
- var sends, receives, closes []token.Pos
- for _, op := range ops {
- if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
- switch op.dir {
- case types.SendOnly:
- sends = append(sends, op.pos)
- case types.RecvOnly:
- receives = append(receives, op.pos)
- case types.SendRecv:
- closes = append(closes, op.pos)
- }
- }
- }
- sort.Sort(byPos(sends))
- sort.Sort(byPos(receives))
- sort.Sort(byPos(closes))
-
- q.Output(lprog.Fset, &peersResult{
- queryPos: opPos,
- queryType: queryType,
- makes: makes,
- sends: sends,
- receives: receives,
- closes: closes,
- })
- return nil
-}
-
-// findOp returns the position of the enclosing send/receive/close op.
-// For send and receive operations, this is the position of the <- token;
-// for close operations, it's the Lparen of the function call.
-//
-// TODO(adonovan): handle implicit receive operations from 'for...range chan' statements.
-func findOp(qpos *queryPos) token.Pos {
- for _, n := range qpos.path {
- switch n := n.(type) {
- case *ast.UnaryExpr:
- if n.Op == token.ARROW {
- return n.OpPos
- }
- case *ast.SendStmt:
- return n.Arrow
- case *ast.CallExpr:
- // close function call can only exist as a direct identifier
- if close, ok := unparen(n.Fun).(*ast.Ident); ok {
- if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" {
- return n.Lparen
- }
- }
- }
- }
- return token.NoPos
-}
-
-// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
-type chanOp struct {
- ch ssa.Value
- dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close
- pos token.Pos
-}
-
-// chanOps returns a slice of all the channel operations in the instruction.
-func chanOps(instr ssa.Instruction) []chanOp {
- // TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too.
- var ops []chanOp
- switch instr := instr.(type) {
- case *ssa.UnOp:
- if instr.Op == token.ARROW {
- ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
- }
- case *ssa.Send:
- ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
- case *ssa.Select:
- for _, st := range instr.States {
- ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
- }
- case ssa.CallInstruction:
- cc := instr.Common()
- if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
- ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
- }
- }
- return ops
-}
-
-// TODO(adonovan): show the line of text for each pos, like "referrers" does.
-type peersResult struct {
- queryPos token.Pos // of queried channel op
- queryType types.Type // type of queried channel
- makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs
-}
-
-func (r *peersResult) PrintPlain(printf printfFunc) {
- if len(r.makes) == 0 {
- printf(r.queryPos, "This channel can't point to anything.")
- return
- }
- printf(r.queryPos, "This channel of type %s may be:", r.queryType)
- for _, alloc := range r.makes {
- printf(alloc, "\tallocated here")
- }
- for _, send := range r.sends {
- printf(send, "\tsent to, here")
- }
- for _, receive := range r.receives {
- printf(receive, "\treceived from, here")
- }
- for _, clos := range r.closes {
- printf(clos, "\tclosed, here")
- }
-}
-
-func (r *peersResult) JSON(fset *token.FileSet) []byte {
- peers := &serial.Peers{
- Pos: fset.Position(r.queryPos).String(),
- Type: r.queryType.String(),
- }
- for _, alloc := range r.makes {
- peers.Allocs = append(peers.Allocs, fset.Position(alloc).String())
- }
- for _, send := range r.sends {
- peers.Sends = append(peers.Sends, fset.Position(send).String())
- }
- for _, receive := range r.receives {
- peers.Receives = append(peers.Receives, fset.Position(receive).String())
- }
- for _, clos := range r.closes {
- peers.Closes = append(peers.Closes, fset.Position(clos).String())
- }
- return toJSON(peers)
-}
-
-// -------- utils --------
-
-// NB: byPos is not deterministic across packages since it depends on load order.
-// Use lessPos if the tests need it.
-type byPos []token.Pos
-
-func (p byPos) Len() int { return len(p) }
-func (p byPos) Less(i, j int) bool { return p[i] < p[j] }
-func (p byPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/cmd/guru/pointsto.go b/cmd/guru/pointsto.go
deleted file mode 100644
index e760844..0000000
--- a/cmd/guru/pointsto.go
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "go/types"
- "sort"
-
- "golang.org/x/tools/cmd/guru/serial"
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-)
-
-// pointsto runs the pointer analysis on the selected expression,
-// and reports its points-to set (for a pointer-like expression)
-// or its dynamic types (for an interface, reflect.Value, or
-// reflect.Type expression) and their points-to sets.
-//
-// All printed sets are sorted to ensure determinism.
-func pointsto(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
-
- // Load/parse/type-check the program.
- lprog, err := loadWithSoftErrors(&lconf)
- if err != nil {
- return err
- }
-
- qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- path, action := findInterestingNode(qpos.info, qpos.path)
- if action != actionExpr {
- return fmt.Errorf("pointer analysis wants an expression; got %s",
- astutil.NodeDescription(qpos.path[0]))
- }
-
- var expr ast.Expr
- var obj types.Object
- switch n := path[0].(type) {
- case *ast.ValueSpec:
- // ambiguous ValueSpec containing multiple names
- return fmt.Errorf("multiple value specification")
- case *ast.Ident:
- obj = qpos.info.ObjectOf(n)
- expr = n
- case ast.Expr:
- expr = n
- default:
- // TODO(adonovan): is this reachable?
- return fmt.Errorf("unexpected AST for expr: %T", n)
- }
-
- // Reject non-pointerlike types (includes all constants---except nil).
- // TODO(adonovan): reject nil too.
- typ := qpos.info.TypeOf(expr)
- if !pointer.CanPoint(typ) {
- return fmt.Errorf("pointer analysis wants an expression of reference type; got %s", typ)
- }
-
- // Determine the ssa.Value for the expression.
- var value ssa.Value
- var isAddr bool
- if obj != nil {
- // def/ref of func/var object
- value, isAddr, err = ssaValueForIdent(prog, qpos.info, obj, path)
- } else {
- value, isAddr, err = ssaValueForExpr(prog, qpos.info, path)
- }
- if err != nil {
- return err // e.g. trivially dead code
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- // Run the pointer analysis.
- ptrs, err := runPTA(ptaConfig, value, isAddr)
- if err != nil {
- return err // e.g. analytically unreachable
- }
-
- q.Output(lprog.Fset, &pointstoResult{
- qpos: qpos,
- typ: typ,
- ptrs: ptrs,
- })
- return nil
-}
-
-// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
-// to the root of the AST is path. isAddr reports whether the
-// ssa.Value is the address denoted by the ast.Ident, not its value.
-func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
- switch obj := obj.(type) {
- case *types.Var:
- pkg := prog.Package(qinfo.Pkg)
- pkg.Build()
- if v, addr := prog.VarValue(obj, pkg, path); v != nil {
- return v, addr, nil
- }
- return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name())
-
- case *types.Func:
- fn := prog.FuncValue(obj)
- if fn == nil {
- return nil, false, fmt.Errorf("%s is an interface method", obj)
- }
- // TODO(adonovan): there's no point running PTA on a *Func ident.
- // Eliminate this feature.
- return fn, false, nil
- }
- panic(obj)
-}
-
-// ssaValueForExpr returns the ssa.Value of the non-ast.Ident
-// expression whose path to the root of the AST is path.
-func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
- pkg := prog.Package(qinfo.Pkg)
- pkg.SetDebugMode(true)
- pkg.Build()
-
- fn := ssa.EnclosingFunction(pkg, path)
- if fn == nil {
- return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)")
- }
-
- if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil {
- return v, addr, nil
- }
-
- return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn)
-}
-
-// runPTA runs the pointer analysis of the selected SSA value or address.
-func runPTA(conf *pointer.Config, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) {
- T := v.Type()
- if isAddr {
- conf.AddIndirectQuery(v)
- T = deref(T)
- } else {
- conf.AddQuery(v)
- }
- ptares := ptrAnalysis(conf)
-
- var ptr pointer.Pointer
- if isAddr {
- ptr = ptares.IndirectQueries[v]
- } else {
- ptr = ptares.Queries[v]
- }
- if ptr == (pointer.Pointer{}) {
- return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)")
- }
- pts := ptr.PointsTo()
-
- if pointer.CanHaveDynamicTypes(T) {
- // Show concrete types for interface/reflect.Value expression.
- if concs := pts.DynamicTypes(); concs.Len() > 0 {
- concs.Iterate(func(conc types.Type, pta interface{}) {
- labels := pta.(pointer.PointsToSet).Labels()
- sort.Sort(byPosAndString(labels)) // to ensure determinism
- ptrs = append(ptrs, pointerResult{conc, labels})
- })
- }
- } else {
- // Show labels for other expressions.
- labels := pts.Labels()
- sort.Sort(byPosAndString(labels)) // to ensure determinism
- ptrs = append(ptrs, pointerResult{T, labels})
- }
- sort.Sort(byTypeString(ptrs)) // to ensure determinism
- return ptrs, nil
-}
-
-type pointerResult struct {
- typ types.Type // type of the pointer (always concrete)
- labels []*pointer.Label // set of labels
-}
-
-type pointstoResult struct {
- qpos *queryPos
- typ types.Type // type of expression
- ptrs []pointerResult // pointer info (typ is concrete => len==1)
-}
-
-func (r *pointstoResult) PrintPlain(printf printfFunc) {
- if pointer.CanHaveDynamicTypes(r.typ) {
- // Show concrete types for interface, reflect.Type or
- // reflect.Value expression.
-
- if len(r.ptrs) > 0 {
- printf(r.qpos, "this %s may contain these dynamic types:", r.qpos.typeString(r.typ))
- for _, ptr := range r.ptrs {
- var obj types.Object
- if nt, ok := deref(ptr.typ).(*types.Named); ok {
- obj = nt.Obj()
- }
- if len(ptr.labels) > 0 {
- printf(obj, "\t%s, may point to:", r.qpos.typeString(ptr.typ))
- printLabels(printf, ptr.labels, "\t\t")
- } else {
- printf(obj, "\t%s", r.qpos.typeString(ptr.typ))
- }
- }
- } else {
- printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ)
- }
- } else {
- // Show labels for other expressions.
- if ptr := r.ptrs[0]; len(ptr.labels) > 0 {
- printf(r.qpos, "this %s may point to these objects:",
- r.qpos.typeString(r.typ))
- printLabels(printf, ptr.labels, "\t")
- } else {
- printf(r.qpos, "this %s may not point to anything.",
- r.qpos.typeString(r.typ))
- }
- }
-}
-
-func (r *pointstoResult) JSON(fset *token.FileSet) []byte {
- var pts []serial.PointsTo
- for _, ptr := range r.ptrs {
- var namePos string
- if nt, ok := deref(ptr.typ).(*types.Named); ok {
- namePos = fset.Position(nt.Obj().Pos()).String()
- }
- var labels []serial.PointsToLabel
- for _, l := range ptr.labels {
- labels = append(labels, serial.PointsToLabel{
- Pos: fset.Position(l.Pos()).String(),
- Desc: l.String(),
- })
- }
- pts = append(pts, serial.PointsTo{
- Type: r.qpos.typeString(ptr.typ),
- NamePos: namePos,
- Labels: labels,
- })
- }
- return toJSON(pts)
-}
-
-type byTypeString []pointerResult
-
-func (a byTypeString) Len() int { return len(a) }
-func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() }
-func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type byPosAndString []*pointer.Label
-
-func (a byPosAndString) Len() int { return len(a) }
-func (a byPosAndString) Less(i, j int) bool {
- cmp := a[i].Pos() - a[j].Pos()
- return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String())
-}
-func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) {
- // TODO(adonovan): due to context-sensitivity, many of these
- // labels may differ only by context, which isn't apparent.
- for _, label := range labels {
- printf(label, "%s%s", prefix, label)
- }
-}
diff --git a/cmd/guru/serial/serial.go b/cmd/guru/serial/serial.go
index 082e6cf..3af7f47 100644
--- a/cmd/guru/serial/serial.go
+++ b/cmd/guru/serial/serial.go
@@ -10,18 +10,12 @@
//
// Query Result stream
// ----- -------------
-// callees Callees
-// callers Caller ...
-// callstack CallStack
// definition Definition
// describe Describe
// freevars FreeVar ...
// implements Implements
-// peers Peers
-// pointsto PointsTo ...
// referrers ReferrersInitial ReferrersPackage ...
// what What
-// whicherrs WhichErrs
//
// All 'pos' strings in the output are of the form "file:line:col",
// where line is the 1-based line number and col is the 1-based byte index.
diff --git a/cmd/guru/testdata/src/calls-json/main.go b/cmd/guru/testdata/src/calls-json/main.go
deleted file mode 100644
index 9d58ed1..0000000
--- a/cmd/guru/testdata/src/calls-json/main.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package main
-
-// Tests of call-graph queries, -format=json.
-// See go.tools/guru/guru_test.go for explanation.
-// See calls-json.golden for expected query results.
-
-func call(f func()) {
- f() // @callees @callees-f "f"
-}
-
-func main() {
- call(func() {
- // @callers callers-main.anon "^"
- // @callstack callstack-main.anon "^"
- })
-}
diff --git a/cmd/guru/testdata/src/calls-json/main.golden b/cmd/guru/testdata/src/calls-json/main.golden
deleted file mode 100644
index 27dc509..0000000
--- a/cmd/guru/testdata/src/calls-json/main.golden
+++ /dev/null
@@ -1,28 +0,0 @@
--------- @callees @callees-f --------
-{
- "pos": "testdata/src/calls-json/main.go:8:3",
- "desc": "dynamic function call",
- "callees": [
- {
- "name": "calls-json.main$1",
- "pos": "testdata/src/calls-json/main.go:12:7"
- }
- ]
-}
--------- @callstack callstack-main.anon --------
-{
- "pos": "testdata/src/calls-json/main.go:12:7",
- "target": "calls-json.main$1",
- "callers": [
- {
- "pos": "testdata/src/calls-json/main.go:8:3",
- "desc": "dynamic function call",
- "caller": "calls-json.call"
- },
- {
- "pos": "testdata/src/calls-json/main.go:12:6",
- "desc": "static function call",
- "caller": "calls-json.main"
- }
- ]
-}
diff --git a/cmd/guru/testdata/src/calls/main.go b/cmd/guru/testdata/src/calls/main.go
deleted file mode 100644
index a208914..0000000
--- a/cmd/guru/testdata/src/calls/main.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package main
-
-import (
- "fmt"
-)
-
-// Tests of call-graph queries.
-// See go.tools/guru/guru_test.go for explanation.
-// See calls.golden for expected query results.
-
-func A(x *int) { // @pointsto pointsto-A-x "x"
- // @callers callers-A "^"
- // @callstack callstack-A "^"
-}
-
-func B(x *int) { // @pointsto pointsto-B-x "x"
- // @callers callers-B "^"
-}
-
-func foo() {
-}
-
-// apply is not (yet) treated context-sensitively.
-func apply(f func(x *int), x *int) {
- f(x) // @callees callees-apply "f"
- // @callers callers-apply "^"
-}
-
-// store *is* treated context-sensitively,
-// so the points-to sets for pc, pd are precise.
-func store(ptr **int, value *int) {
- *ptr = value
- // @callers callers-store "^"
-}
-
-func call(f func() *int) {
- // Result points to anon function.
- f() // @pointsto pointsto-result-f "f"
-
- // Target of call is anon function.
- f() // @callees callees-main.call-f "f"
-
- // @callers callers-main.call "^"
-}
-
-func main() {
- var a, b int
- go apply(A, &a) // @callees callees-main-apply1 "app"
- defer apply(B, &b)
-
- var c, d int
- var pc, pd *int // @pointsto pointsto-pc "pc"
- store(&pc, &c)
- store(&pd, &d)
- _ = pd // @pointsto pointsto-pd "pd"
-
- call(func() *int {
- // We are called twice from main.call
- // @callers callers-main.anon "^"
- return &a
- })
-
- // Errors
- _ = "no function call here" // @callees callees-err-no-call "no"
- print("builtin") // @callees callees-err-builtin "builtin"
- _ = string("type conversion") // @callees callees-err-conversion "str"
- call(nil) // @callees callees-err-bad-selection "call\\(nil"
- if false {
- main() // @callees callees-err-deadcode1 "main"
- }
- var nilFunc func()
- nilFunc() // @callees callees-err-nil-func "nilFunc"
- var i interface {
- f()
- }
- i.f() // @callees callees-err-nil-interface "i.f"
-
- i = new(myint)
- i.f() // @callees callees-not-a-wrapper "f"
-
- // statically dispatched calls. Handled specially by callees, so test that they work.
- foo() // @callees callees-static-call "foo"
- fmt.Println() // @callees callees-qualified-call "Println"
- m := new(method)
- m.f() // @callees callees-static-method-call "f"
- g := new(embeddedIface)
- g.iface = m
- g.f() // @callees callees-implicit-selection-method-call "f"
-}
-
-type myint int
-
-func (myint) f() {
- // @callers callers-not-a-wrapper "^"
-}
-
-type method int
-
-func (method) f() {
-}
-
-type embeddedIface struct {
- iface
-}
-
-type iface interface {
- f()
-}
-
-var dynamic = func() {}
-
-func deadcode() {
- main() // @callees callees-err-deadcode2 "main"
- // @callers callers-err-deadcode "^"
- // @callstack callstack-err-deadcode "^"
-
- // Within dead code, dynamic calls have no callees.
- dynamic() // @callees callees-err-deadcode3 "dynamic"
-}
-
-// This code belongs to init.
-var global = 123 // @callers callers-global "global"
-
-// The package initializer may be called by other packages' inits, or
-// in this case, the root of the callgraph. The source-level init functions
-// are in turn called by it.
-func init() {
- // @callstack callstack-init "^"
-}
diff --git a/cmd/guru/testdata/src/calls/main.golden b/cmd/guru/testdata/src/calls/main.golden
deleted file mode 100644
index ab68e95..0000000
--- a/cmd/guru/testdata/src/calls/main.golden
+++ /dev/null
@@ -1,125 +0,0 @@
--------- @pointsto pointsto-A-x --------
-this *int may point to these objects:
- a
- b
-
--------- @callstack callstack-A --------
-Found a call path from root to calls.A
-calls.A
-dynamic function call from calls.apply
-concurrent static function call from calls.main
-
--------- @pointsto pointsto-B-x --------
-this *int may point to these objects:
- a
- b
-
--------- @callers callers-B --------
-calls.B is called from these 1 sites:
- dynamic function call from calls.apply
-
--------- @callees callees-apply --------
-this dynamic function call dispatches to:
- calls.A
- calls.B
-
--------- @callers callers-apply --------
-calls.apply is called from these 2 sites:
- concurrent static function call from calls.main
- deferred static function call from calls.main
-
--------- @callers callers-store --------
-calls.store is called from these 2 sites:
- static function call from calls.main
- static function call from calls.main
-
--------- @pointsto pointsto-result-f --------
-this func() *int may point to these objects:
- calls.main$1
-
--------- @callees callees-main.call-f --------
-this dynamic function call dispatches to:
- calls.main$1
-
--------- @callers callers-main.call --------
-calls.call is called from these 2 sites:
- static function call from calls.main
- static function call from calls.main
-
--------- @callees callees-main-apply1 --------
-this static function call dispatches to:
- calls.apply
-
--------- @pointsto pointsto-pc --------
-this *int may point to these objects:
- c
-
--------- @pointsto pointsto-pd --------
-this *int may point to these objects:
- d
-
--------- @callees callees-err-no-call --------
-
-Error: there is no function call here
--------- @callees callees-err-builtin --------
-
-Error: this is a call to the built-in 'print' operator
--------- @callees callees-err-conversion --------
-
-Error: this is a type conversion, not a function call
--------- @callees callees-err-bad-selection --------
-
-Error: ambiguous selection within function call (or conversion)
--------- @callees callees-err-deadcode1 --------
-this static function call dispatches to:
- calls.main
-
--------- @callees callees-err-nil-func --------
-dynamic function call on nil value
-
--------- @callees callees-err-nil-interface --------
-dynamic method call on nil value
-
--------- @callees callees-not-a-wrapper --------
-this dynamic method call dispatches to:
- (calls.myint).f
-
--------- @callees callees-static-call --------
-this static function call dispatches to:
- calls.foo
-
--------- @callees callees-qualified-call --------
-this static function call dispatches to:
- fmt.Println
-
--------- @callees callees-static-method-call --------
-this static function call dispatches to:
- (calls.method).f
-
--------- @callees callees-implicit-selection-method-call --------
-this dynamic method call dispatches to:
- (calls.method).f
-
--------- @callers callers-not-a-wrapper --------
-(calls.myint).f is called from these 1 sites:
- dynamic method call from calls.main
-
--------- @callees callees-err-deadcode2 --------
-this static function call dispatches to:
- calls.main
-
--------- @callstack callstack-err-deadcode --------
-calls.deadcode is unreachable in this analysis scope
-
--------- @callees callees-err-deadcode3 --------
-
-Error: this call site is unreachable in this analysis
--------- @callers callers-global --------
-calls.init is called from these 1 sites:
-the root of the call graph
-
--------- @callstack callstack-init --------
-Found a call path from root to calls.init#1
-calls.init#1
-static function call from calls.init
-
diff --git a/cmd/guru/testdata/src/imports/main.go b/cmd/guru/testdata/src/imports/main.go
index 9fe2b71..0fc40f2 100644
--- a/cmd/guru/testdata/src/imports/main.go
+++ b/cmd/guru/testdata/src/imports/main.go
@@ -21,7 +21,7 @@
var t lib.Type // @describe ref-type "Type"
p := t.Method(&a) // @describe ref-method "Method"
- print(*p + 1) // @pointsto p "p "
+ print(*p + 1)
var _ lib.Type // @describe ref-pkg "lib"
diff --git a/cmd/guru/testdata/src/imports/main.golden b/cmd/guru/testdata/src/imports/main.golden
index 1e12217..18a3e22 100644
--- a/cmd/guru/testdata/src/imports/main.golden
+++ b/cmd/guru/testdata/src/imports/main.golden
@@ -37,10 +37,6 @@
reference to method func (lib.Type).Method(x *int) *int
defined here
--------- @pointsto p --------
-this *int may point to these objects:
- imports.a
-
-------- @describe ref-pkg --------
reference to package "lib"
const Const untyped int = 3
diff --git a/cmd/guru/testdata/src/peers-json/main.go b/cmd/guru/testdata/src/peers-json/main.go
deleted file mode 100644
index ef63992..0000000
--- a/cmd/guru/testdata/src/peers-json/main.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package main
-
-// Tests of channel 'peers' query, -format=json.
-// See go.tools/guru/guru_test.go for explanation.
-// See peers-json.golden for expected query results.
-
-func main() {
- chA := make(chan *int)
- <-chA
- select {
- case <-chA: // @peers peer-recv-chA "<-"
- }
-}
diff --git a/cmd/guru/testdata/src/peers-json/main.golden b/cmd/guru/testdata/src/peers-json/main.golden
deleted file mode 100644
index 50d5716..0000000
--- a/cmd/guru/testdata/src/peers-json/main.golden
+++ /dev/null
@@ -1,12 +0,0 @@
--------- @peers peer-recv-chA --------
-{
- "pos": "testdata/src/peers-json/main.go:11:7",
- "type": "chan *int",
- "allocs": [
- "testdata/src/peers-json/main.go:8:13"
- ],
- "receives": [
- "testdata/src/peers-json/main.go:9:2",
- "testdata/src/peers-json/main.go:11:7"
- ]
-}
diff --git a/cmd/guru/testdata/src/peers/main.go b/cmd/guru/testdata/src/peers/main.go
deleted file mode 100644
index 40ee205..0000000
--- a/cmd/guru/testdata/src/peers/main.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package main
-
-// Tests of channel 'peers' query.
-// See go.tools/guru/guru_test.go for explanation.
-// See peers.golden for expected query results.
-
-var a2 int
-
-func main() {
- chA := make(chan *int)
- a1 := 1
- chA <- &a1
-
- chA2 := make(chan *int, 2)
- if a2 == 0 {
- chA = chA2
- }
-
- chB := make(chan *int)
- b := 3
- chB <- &b
-
- <-chA // @pointsto pointsto-chA "chA"
- <-chA2 // @pointsto pointsto-chA2 "chA2"
- <-chB // @pointsto pointsto-chB "chB"
-
- select {
- case rA := <-chA: // @peers peer-recv-chA "<-"
- _ = rA // @pointsto pointsto-rA "rA"
- case rB := <-chB: // @peers peer-recv-chB "<-"
- _ = rB // @pointsto pointsto-rB "rB"
-
- case <-chA: // @peers peer-recv-chA' "<-"
-
- case chA2 <- &a2: // @peers peer-send-chA' "<-"
- }
-
- for range chA {
- }
-
- close(chA) // @peers peer-close-chA "chA"
-
- chC := make(chan *int)
- (close)(chC) // @peers peer-close-chC "chC"
-
- close := func(ch chan *int) chan *int {
- return ch
- }
-
- close(chC) <- &b // @peers peer-send-chC "chC"
- <-close(chC) // @peers peer-recv-chC "chC"
-}
diff --git a/cmd/guru/testdata/src/peers/main.golden b/cmd/guru/testdata/src/peers/main.golden
deleted file mode 100644
index f97e672..0000000
--- a/cmd/guru/testdata/src/peers/main.golden
+++ /dev/null
@@ -1,100 +0,0 @@
--------- @pointsto pointsto-chA --------
-this chan *int may point to these objects:
- makechan
- makechan
-
--------- @pointsto pointsto-chA2 --------
-this chan *int may point to these objects:
- makechan
-
--------- @pointsto pointsto-chB --------
-this chan *int may point to these objects:
- makechan
-
--------- @peers peer-recv-chA --------
-This channel of type chan *int may be:
- allocated here
- allocated here
- sent to, here
- sent to, here
- received from, here
- received from, here
- received from, here
- received from, here
- received from, here
- closed, here
-
--------- @pointsto pointsto-rA --------
-this *int may point to these objects:
- peers.a2
- a1
-
--------- @peers peer-recv-chB --------
-This channel of type chan *int may be:
- allocated here
- sent to, here
- received from, here
- received from, here
-
--------- @pointsto pointsto-rB --------
-this *int may point to these objects:
- b
-
--------- @peers peer-recv-chA' --------
-This channel of type chan *int may be:
- allocated here
- allocated here
- sent to, here
- sent to, here
- received from, here
- received from, here
- received from, here
- received from, here
- received from, here
- closed, here
-
--------- @peers peer-send-chA' --------
-This channel of type chan *int may be:
- allocated here
- sent to, here
- received from, here
- received from, here
- received from, here
- received from, here
- received from, here
- closed, here
-
--------- @peers peer-close-chA --------
-This channel of type chan *int may be:
- allocated here
- allocated here
- sent to, here
- sent to, here
- received from, here
- received from, here
- received from, here
- received from, here
- received from, here
- closed, here
-
--------- @peers peer-close-chC --------
-This channel of type chan *int may be:
- allocated here
- sent to, here
- received from, here
- closed, here
-
--------- @peers peer-send-chC --------
-This channel of type chan *int may be:
- allocated here
- sent to, here
- received from, here
- closed, here
-
--------- @peers peer-recv-chC --------
-This channel of type chan *int may be:
- allocated here
- sent to, here
- received from, here
- closed, here
-
diff --git a/cmd/guru/testdata/src/pointsto-json/main.go b/cmd/guru/testdata/src/pointsto-json/main.go
deleted file mode 100644
index 0a9f318..0000000
--- a/cmd/guru/testdata/src/pointsto-json/main.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package main
-
-// Tests of 'pointsto' queries, -format=json.
-// See go.tools/guru/guru_test.go for explanation.
-// See pointsto-json.golden for expected query results.
-
-func main() { //
- var s struct{ x [3]int }
- p := &s.x[0] // @pointsto val-p "p"
- _ = p
-
- var i I = C(0)
- if i == nil {
- i = new(D)
- }
- print(i) // @pointsto val-i "\\bi\\b"
-}
-
-type I interface {
- f()
-}
-
-type C int
-type D struct{}
-
-func (c C) f() {}
-func (d *D) f() {}
diff --git a/cmd/guru/testdata/src/pointsto-json/main.golden b/cmd/guru/testdata/src/pointsto-json/main.golden
deleted file mode 100644
index 06a2204..0000000
--- a/cmd/guru/testdata/src/pointsto-json/main.golden
+++ /dev/null
@@ -1,29 +0,0 @@
--------- @pointsto val-p --------
-[
- {
- "type": "*int",
- "labels": [
- {
- "pos": "testdata/src/pointsto-json/main.go:8:6",
- "desc": "s.x[*]"
- }
- ]
- }
-]
--------- @pointsto val-i --------
-[
- {
- "type": "*D",
- "namepos": "testdata/src/pointsto-json/main.go:24:6",
- "labels": [
- {
- "pos": "testdata/src/pointsto-json/main.go:14:10",
- "desc": "new"
- }
- ]
- },
- {
- "type": "C",
- "namepos": "testdata/src/pointsto-json/main.go:23:6"
- }
-]
diff --git a/cmd/guru/testdata/src/pointsto/main.go b/cmd/guru/testdata/src/pointsto/main.go
deleted file mode 100644
index c4ba2e2..0000000
--- a/cmd/guru/testdata/src/pointsto/main.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package main
-
-// Tests of 'pointsto' query.
-// See go.tools/guru/guru_test.go for explanation.
-// See pointsto.golden for expected query results.
-
-const pi = 3.141 // @pointsto const "pi"
-
-var global = new(string) // NB: ssa.Global is indirect, i.e. **string
-
-func main() {
- livecode()
-
- // func objects
- _ = main // @pointsto func-ref-main "main"
- _ = (*C).f // @pointsto func-ref-*C.f "..C..f"
- _ = D.f // @pointsto func-ref-D.f "D.f"
- _ = I.f // @pointsto func-ref-I.f "I.f"
- var d D
- var i I
- _ = d.f // @pointsto func-ref-d.f "d.f"
- _ = i.f // @pointsto func-ref-i.f "i.f"
-
- // var objects
- anon := func() {
- _ = d.f // @pointsto ref-lexical-d.f "d.f"
- }
- _ = anon // @pointsto ref-anon "anon"
- _ = global // @pointsto ref-global "global"
-
- // SSA affords some local flow sensitivity.
- var a, b int
- var x = &a // @pointsto var-def-x-1 "x"
- _ = x // @pointsto var-ref-x-1 "x"
- x = &b // @pointsto var-def-x-2 "x"
- _ = x // @pointsto var-ref-x-2 "x"
-
- i = new(C) // @pointsto var-ref-i-C "i"
- if i != nil {
- i = D{} // @pointsto var-ref-i-D "i"
- }
- print(i) // @pointsto var-ref-i "\\bi\\b"
-
- m := map[string]*int{"a": &a}
- mapval, _ := m["a"] // @pointsto map-lookup,ok "m..a.."
- _ = mapval // @pointsto mapval "mapval"
- _ = m // @pointsto m "m"
-
- if false {
- panic(3) // @pointsto builtin-panic "panic"
- }
-
- // NB: s.f is addressable per (*ssa.Program).VarValue,
- // but our query concerns the object, not its address.
- s := struct{ f interface{} }{f: make(chan bool)}
- print(s.f) // @pointsto var-ref-s-f "s.f"
-}
-
-func livecode() {} // @pointsto func-live "livecode"
-
-func deadcode() { // @pointsto func-dead "deadcode"
- // Pointer analysis can't run on dead code.
- var b = new(int) // @pointsto b "b"
- _ = b
-}
-
-type I interface {
- f()
-}
-
-type C int
-type D struct{}
-
-func (c *C) f() {}
-func (d D) f() {}
diff --git a/cmd/guru/testdata/src/pointsto/main.golden b/cmd/guru/testdata/src/pointsto/main.golden
deleted file mode 100644
index 40a830f..0000000
--- a/cmd/guru/testdata/src/pointsto/main.golden
+++ /dev/null
@@ -1,96 +0,0 @@
--------- @pointsto const --------
-
-Error: pointer analysis wants an expression of reference type; got untyped float
--------- @pointsto func-ref-main --------
-this func() may point to these objects:
- pointsto.main
-
--------- @pointsto func-ref-*C.f --------
-this func() may point to these objects:
- (*pointsto.C).f
-
--------- @pointsto func-ref-D.f --------
-this func() may point to these objects:
- (pointsto.D).f
-
--------- @pointsto func-ref-I.f --------
-
-Error: func (pointsto.I).f() is an interface method
--------- @pointsto func-ref-d.f --------
-this func() may point to these objects:
- (pointsto.D).f
-
--------- @pointsto func-ref-i.f --------
-
-Error: func (pointsto.I).f() is an interface method
--------- @pointsto ref-lexical-d.f --------
-this func() may point to these objects:
- (pointsto.D).f
-
--------- @pointsto ref-anon --------
-this func() may point to these objects:
- pointsto.main$1
-
--------- @pointsto ref-global --------
-this *string may point to these objects:
- new
-
--------- @pointsto var-def-x-1 --------
-this *int may point to these objects:
- a
-
--------- @pointsto var-ref-x-1 --------
-this *int may point to these objects:
- a
-
--------- @pointsto var-def-x-2 --------
-this *int may point to these objects:
- b
-
--------- @pointsto var-ref-x-2 --------
-this *int may point to these objects:
- b
-
--------- @pointsto var-ref-i-C --------
-this I may contain these dynamic types:
- *C, may point to:
- new
-
--------- @pointsto var-ref-i-D --------
-this I may contain these dynamic types:
- D
-
--------- @pointsto var-ref-i --------
-this I may contain these dynamic types:
- *C, may point to:
- new
- D
-
--------- @pointsto map-lookup,ok --------
-
-Error: pointer analysis wants an expression of reference type; got (*int, bool)
--------- @pointsto mapval --------
-this *int may point to these objects:
- a
-
--------- @pointsto m --------
-this map[string]*int may point to these objects:
- makemap
-
--------- @pointsto builtin-panic --------
-
-Error: pointer analysis wants an expression of reference type; got ()
--------- @pointsto var-ref-s-f --------
-this any may contain these dynamic types:
- chan bool, may point to:
- makechan
-
--------- @pointsto func-live --------
-
-Error: pointer analysis did not find expression (dead code?)
--------- @pointsto func-dead --------
-
-Error: pointer analysis did not find expression (dead code?)
--------- @pointsto b --------
-
-Error: pointer analysis did not find expression (dead code?)
diff --git a/cmd/guru/testdata/src/reflection/main.go b/cmd/guru/testdata/src/reflection/main.go
deleted file mode 100644
index 392643b..0000000
--- a/cmd/guru/testdata/src/reflection/main.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package main
-
-// This is a test of 'pointsto', but we split it into a separate file
-// so that pointsto.go doesn't have to import "reflect" each time.
-
-import "reflect"
-
-var a int
-var b bool
-
-func main() {
- m := make(map[*int]*bool)
- m[&a] = &b
-
- mrv := reflect.ValueOf(m)
- if a > 0 {
- mrv = reflect.ValueOf(&b)
- }
- if a > 0 {
- mrv = reflect.ValueOf(&a)
- }
-
- _ = mrv // @pointsto mrv "mrv"
- p1 := mrv.Interface() // @pointsto p1 "p1"
- p2 := mrv.MapKeys() // @pointsto p2 "p2"
- p3 := p2[0] // @pointsto p3 "p3"
- p4 := reflect.TypeOf(p1) // @pointsto p4 "p4"
-
- _, _, _, _ = p1, p2, p3, p4
-}
diff --git a/cmd/guru/testdata/src/reflection/main.golden b/cmd/guru/testdata/src/reflection/main.golden
deleted file mode 100644
index 2a84071..0000000
--- a/cmd/guru/testdata/src/reflection/main.golden
+++ /dev/null
@@ -1,34 +0,0 @@
--------- @pointsto mrv --------
-this reflect.Value may contain these dynamic types:
- *bool, may point to:
- reflection.b
- *int, may point to:
- reflection.a
- map[*int]*bool, may point to:
- makemap
-
--------- @pointsto p1 --------
-this any may contain these dynamic types:
- *bool, may point to:
- reflection.b
- *int, may point to:
- reflection.a
- map[*int]*bool, may point to:
- makemap
-
--------- @pointsto p2 --------
-this []reflect.Value may point to these objects:
- <alloc in (reflect.Value).MapKeys>
-
--------- @pointsto p3 --------
-this reflect.Value may contain these dynamic types:
- *int, may point to:
- reflection.a
-
--------- @pointsto p4 --------
-this reflect.Type may contain these dynamic types:
- *reflect.rtype, may point to:
- *bool
- *int
- map[*int]*bool
-
diff --git a/cmd/guru/testdata/src/softerrs/main.go b/cmd/guru/testdata/src/softerrs/main.go
deleted file mode 100644
index f7254b8..0000000
--- a/cmd/guru/testdata/src/softerrs/main.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package main
-
-// Tests of various queries on a program containing only "soft" errors.
-// See go.tools/guru/guru_test.go for explanation.
-// See main.golden for expected query results.
-
-func _() {
- var i int // "unused var" is a soft error
-}
-
-func f() {} // @callers softerrs-callers-f "f"
-
-func main() {
- f() // @describe softerrs-describe-f "f"
-}
diff --git a/cmd/guru/testdata/src/softerrs/main.golden b/cmd/guru/testdata/src/softerrs/main.golden
deleted file mode 100644
index ae95f46..0000000
--- a/cmd/guru/testdata/src/softerrs/main.golden
+++ /dev/null
@@ -1,8 +0,0 @@
--------- @callers softerrs-callers-f --------
-softerrs.f is called from these 1 sites:
- static function call from softerrs.main
-
--------- @describe softerrs-describe-f --------
-reference to func f()
-defined here
-
diff --git a/cmd/guru/testdata/src/what-json/main.golden b/cmd/guru/testdata/src/what-json/main.golden
index 320c52b..760f9d7 100644
--- a/cmd/guru/testdata/src/what-json/main.golden
+++ b/cmd/guru/testdata/src/what-json/main.golden
@@ -33,16 +33,11 @@
}
],
"modes": [
- "callees",
- "callers",
- "callstack",
"definition",
"describe",
"freevars",
"implements",
- "pointsto",
- "referrers",
- "whicherrs"
+ "referrers"
],
"srcdir": "testdata/src",
"importpath": "what-json"
@@ -81,9 +76,7 @@
"describe",
"freevars",
"implements",
- "pointsto",
- "referrers",
- "whicherrs"
+ "referrers"
],
"srcdir": "testdata/src",
"importpath": "what-json",
diff --git a/cmd/guru/testdata/src/what/main.golden b/cmd/guru/testdata/src/what/main.golden
index f113e2f..dbd1cc2 100644
--- a/cmd/guru/testdata/src/what/main.golden
+++ b/cmd/guru/testdata/src/what/main.golden
@@ -1,7 +1,7 @@
-------- @what pkgdecl --------
identifier
source file
-modes: [definition describe freevars implements pointsto referrers whicherrs]
+modes: [definition describe freevars implements referrers]
srcdir: testdata/src
import path: what
@@ -12,7 +12,7 @@
block
function declaration
source file
-modes: [callees callers callstack definition describe freevars implements pointsto referrers whicherrs]
+modes: [definition describe freevars implements referrers]
srcdir: testdata/src
import path: what
@@ -22,7 +22,7 @@
block
function declaration
source file
-modes: [callers callstack describe freevars pointsto whicherrs]
+modes: [describe freevars]
srcdir: testdata/src
import path: what
@@ -33,7 +33,7 @@
block
function declaration
source file
-modes: [callers callstack definition describe freevars implements peers pointsto referrers whicherrs]
+modes: [definition describe freevars implements referrers]
srcdir: testdata/src
import path: what
ch
diff --git a/cmd/guru/testdata/src/whicherrs/main.go b/cmd/guru/testdata/src/whicherrs/main.go
deleted file mode 100644
index d1613c5..0000000
--- a/cmd/guru/testdata/src/whicherrs/main.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package main
-
-type errType string
-
-const constErr errType = "blah"
-
-func (et errType) Error() string {
- return string(et)
-}
-
-var errVar error = errType("foo")
-
-func genErr(i int) error {
- switch i {
- case 0:
- return constErr
- case 1:
- return errVar
- default:
- return nil
- }
-}
-
-func unreachable() {
- err := errVar // @whicherrs func-dead "err"
- _ = err
-}
-
-func main() {
- err := genErr(0) // @whicherrs localerrs "err"
- _ = err
-}
diff --git a/cmd/guru/testdata/src/whicherrs/main.golden b/cmd/guru/testdata/src/whicherrs/main.golden
deleted file mode 100644
index 3484752..0000000
--- a/cmd/guru/testdata/src/whicherrs/main.golden
+++ /dev/null
@@ -1,11 +0,0 @@
--------- @whicherrs func-dead --------
-
-Error: pointer analysis did not find expression (dead code?)
--------- @whicherrs localerrs --------
-this error may point to these globals:
- errVar
-this error may contain these constants:
- constErr
-this error may contain these dynamic types:
- errType
-
diff --git a/cmd/guru/what.go b/cmd/guru/what.go
index 7ebabbd..422c6c1 100644
--- a/cmd/guru/what.go
+++ b/cmd/guru/what.go
@@ -43,22 +43,11 @@
}
for _, n := range qpos.path {
- switch n := n.(type) {
+ switch n.(type) {
case *ast.Ident:
enable["definition"] = true
enable["referrers"] = true
enable["implements"] = true
- case *ast.CallExpr:
- enable["callees"] = true
- case *ast.FuncDecl:
- enable["callers"] = true
- enable["callstack"] = true
- case *ast.SendStmt:
- enable["peers"] = true
- case *ast.UnaryExpr:
- if n.Op == token.ARROW {
- enable["peers"] = true
- }
}
// For implements, we approximate findInterestingNode.
@@ -73,37 +62,10 @@
enable["implements"] = true
}
}
-
- // For pointsto and whicherrs, we approximate findInterestingNode.
- if _, ok := enable["pointsto"]; !ok {
- switch n.(type) {
- case ast.Stmt,
- *ast.ArrayType,
- *ast.StructType,
- *ast.FuncType,
- *ast.InterfaceType,
- *ast.MapType,
- *ast.ChanType:
- // not an expression
- enable["pointsto"] = false
- enable["whicherrs"] = false
-
- case ast.Expr, ast.Decl, *ast.ValueSpec:
- // an expression, maybe
- enable["pointsto"] = true
- enable["whicherrs"] = true
-
- default:
- // Comment, Field, KeyValueExpr, etc: ascend.
- }
- }
}
// If we don't have an exact selection, disable modes that need one.
if !qpos.exact {
- enable["callees"] = false
- enable["pointsto"] = false
- enable["whicherrs"] = false
enable["describe"] = false
}
diff --git a/cmd/guru/whicherrs.go b/cmd/guru/whicherrs.go
deleted file mode 100644
index 3a81bf5..0000000
--- a/cmd/guru/whicherrs.go
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "go/ast"
- "go/token"
- "go/types"
- "sort"
-
- "golang.org/x/tools/cmd/guru/serial"
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-)
-
-var builtinErrorType = types.Universe.Lookup("error").Type()
-
-// whicherrs takes an position to an error and tries to find all types, constants
-// and global value which a given error can point to and which can be checked from the
-// scope where the error lives.
-// In short, it returns a list of things that can be checked against in order to handle
-// an error properly.
-//
-// TODO(dmorsing): figure out if fields in errors like *os.PathError.Err
-// can be queried recursively somehow.
-func whicherrs(q *Query) error {
- lconf := loader.Config{Build: q.Build}
-
- if err := setPTAScope(&lconf, q.Scope); err != nil {
- return err
- }
-
- // Load/parse/type-check the program.
- lprog, err := loadWithSoftErrors(&lconf)
- if err != nil {
- return err
- }
-
- qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
- if err != nil {
- return err
- }
-
- prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
-
- ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
- if err != nil {
- return err
- }
-
- path, action := findInterestingNode(qpos.info, qpos.path)
- if action != actionExpr {
- return fmt.Errorf("whicherrs wants an expression; got %s",
- astutil.NodeDescription(qpos.path[0]))
- }
- var expr ast.Expr
- var obj types.Object
- switch n := path[0].(type) {
- case *ast.ValueSpec:
- // ambiguous ValueSpec containing multiple names
- return fmt.Errorf("multiple value specification")
- case *ast.Ident:
- obj = qpos.info.ObjectOf(n)
- expr = n
- case ast.Expr:
- expr = n
- default:
- return fmt.Errorf("unexpected AST for expr: %T", n)
- }
-
- typ := qpos.info.TypeOf(expr)
- if !types.Identical(typ, builtinErrorType) {
- return fmt.Errorf("selection is not an expression of type 'error'")
- }
- // Determine the ssa.Value for the expression.
- var value ssa.Value
- if obj != nil {
- // def/ref of func/var object
- value, _, err = ssaValueForIdent(prog, qpos.info, obj, path)
- } else {
- value, _, err = ssaValueForExpr(prog, qpos.info, path)
- }
- if err != nil {
- return err // e.g. trivially dead code
- }
-
- // Defer SSA construction till after errors are reported.
- prog.Build()
-
- globals := findVisibleErrs(prog, qpos)
- constants := findVisibleConsts(prog, qpos)
-
- res := &whicherrsResult{
- qpos: qpos,
- errpos: expr.Pos(),
- }
-
- // TODO(adonovan): the following code is heavily duplicated
- // w.r.t. "pointsto". Refactor?
-
- // Find the instruction which initialized the
- // global error. If more than one instruction has stored to the global
- // remove the global from the set of values that we want to query.
- allFuncs := ssautil.AllFunctions(prog)
- for fn := range allFuncs {
- for _, b := range fn.Blocks {
- for _, instr := range b.Instrs {
- store, ok := instr.(*ssa.Store)
- if !ok {
- continue
- }
- gval, ok := store.Addr.(*ssa.Global)
- if !ok {
- continue
- }
- gbl, ok := globals[gval]
- if !ok {
- continue
- }
- // we already found a store to this global
- // The normal error define is just one store in the init
- // so we just remove this global from the set we want to query
- if gbl != nil {
- delete(globals, gval)
- }
- globals[gval] = store.Val
- }
- }
- }
-
- ptaConfig.AddQuery(value)
- for _, v := range globals {
- ptaConfig.AddQuery(v)
- }
-
- ptares := ptrAnalysis(ptaConfig)
- valueptr := ptares.Queries[value]
- if valueptr == (pointer.Pointer{}) {
- return fmt.Errorf("pointer analysis did not find expression (dead code?)")
- }
- for g, v := range globals {
- ptr, ok := ptares.Queries[v]
- if !ok {
- continue
- }
- if !ptr.MayAlias(valueptr) {
- continue
- }
- res.globals = append(res.globals, g)
- }
- pts := valueptr.PointsTo()
- dedup := make(map[*ssa.NamedConst]bool)
- for _, label := range pts.Labels() {
- // These values are either MakeInterfaces or reflect
- // generated interfaces. For the purposes of this
- // analysis, we don't care about reflect generated ones
- makeiface, ok := label.Value().(*ssa.MakeInterface)
- if !ok {
- continue
- }
- constval, ok := makeiface.X.(*ssa.Const)
- if !ok {
- continue
- }
- c := constants[*constval]
- if c != nil && !dedup[c] {
- dedup[c] = true
- res.consts = append(res.consts, c)
- }
- }
- concs := pts.DynamicTypes()
- concs.Iterate(func(conc types.Type, _ interface{}) {
- // go/types is a bit annoying here.
- // We want to find all the types that we can
- // typeswitch or assert to. This means finding out
- // if the type pointed to can be seen by us.
- //
- // For the purposes of this analysis, we care only about
- // TypeNames of Named or pointer-to-Named types.
- // We ignore other types (e.g. structs) that implement error.
- var name *types.TypeName
- switch t := conc.(type) {
- case *types.Pointer:
- named, ok := t.Elem().(*types.Named)
- if !ok {
- return
- }
- name = named.Obj()
- case *types.Named:
- name = t.Obj()
- default:
- return
- }
- if !isAccessibleFrom(name, qpos.info.Pkg) {
- return
- }
- res.types = append(res.types, &errorType{conc, name})
- })
- sort.Sort(membersByPosAndString(res.globals))
- sort.Sort(membersByPosAndString(res.consts))
- sort.Sort(sorterrorType(res.types))
-
- q.Output(lprog.Fset, res)
- return nil
-}
-
-// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil.
-func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value {
- globals := make(map[*ssa.Global]ssa.Value)
- for _, pkg := range prog.AllPackages() {
- for _, mem := range pkg.Members {
- gbl, ok := mem.(*ssa.Global)
- if !ok {
- continue
- }
- gbltype := gbl.Type()
- // globals are always pointers
- if !types.Identical(deref(gbltype), builtinErrorType) {
- continue
- }
- if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) {
- continue
- }
- globals[gbl] = nil
- }
- }
- return globals
-}
-
-// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil.
-func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst {
- constants := make(map[ssa.Const]*ssa.NamedConst)
- for _, pkg := range prog.AllPackages() {
- for _, mem := range pkg.Members {
- obj, ok := mem.(*ssa.NamedConst)
- if !ok {
- continue
- }
- consttype := obj.Type()
- if !types.AssignableTo(consttype, builtinErrorType) {
- continue
- }
- if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) {
- continue
- }
- constants[*obj.Value] = obj
- }
- }
-
- return constants
-}
-
-type membersByPosAndString []ssa.Member
-
-func (a membersByPosAndString) Len() int { return len(a) }
-func (a membersByPosAndString) Less(i, j int) bool {
- cmp := a[i].Pos() - a[j].Pos()
- return cmp < 0 || cmp == 0 && a[i].String() < a[j].String()
-}
-func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type sorterrorType []*errorType
-
-func (a sorterrorType) Len() int { return len(a) }
-func (a sorterrorType) Less(i, j int) bool {
- cmp := a[i].obj.Pos() - a[j].obj.Pos()
- return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String()
-}
-func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type errorType struct {
- typ types.Type // concrete type N or *N that implements error
- obj *types.TypeName // the named type N
-}
-
-type whicherrsResult struct {
- qpos *queryPos
- errpos token.Pos
- globals []ssa.Member
- consts []ssa.Member
- types []*errorType
-}
-
-func (r *whicherrsResult) PrintPlain(printf printfFunc) {
- if len(r.globals) > 0 {
- printf(r.qpos, "this error may point to these globals:")
- for _, g := range r.globals {
- printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg))
- }
- }
- if len(r.consts) > 0 {
- printf(r.qpos, "this error may contain these constants:")
- for _, c := range r.consts {
- printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg))
- }
- }
- if len(r.types) > 0 {
- printf(r.qpos, "this error may contain these dynamic types:")
- for _, t := range r.types {
- printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ))
- }
- }
-}
-
-func (r *whicherrsResult) JSON(fset *token.FileSet) []byte {
- we := &serial.WhichErrs{}
- we.ErrPos = fset.Position(r.errpos).String()
- for _, g := range r.globals {
- we.Globals = append(we.Globals, fset.Position(g.Pos()).String())
- }
- for _, c := range r.consts {
- we.Constants = append(we.Constants, fset.Position(c.Pos()).String())
- }
- for _, t := range r.types {
- var et serial.WhichErrsType
- et.Type = r.qpos.typeString(t.typ)
- et.Position = fset.Position(t.obj.Pos()).String()
- we.Types = append(we.Types, et)
- }
- return toJSON(we)
-}
diff --git a/go/callgraph/callgraph_test.go b/go/callgraph/callgraph_test.go
index dd6baaf..aa8aca0 100644
--- a/go/callgraph/callgraph_test.go
+++ b/go/callgraph/callgraph_test.go
@@ -14,7 +14,6 @@
"golang.org/x/tools/go/callgraph/static"
"golang.org/x/tools/go/callgraph/vta"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
@@ -139,21 +138,6 @@
}
}
-func BenchmarkPTA(b *testing.B) {
- b.StopTimer()
- _, main := example()
- b.StartTimer()
-
- for i := 0; i < b.N; i++ {
- config := &pointer.Config{Mains: []*ssa.Package{main.Pkg}, BuildCallGraph: true}
- res, err := pointer.Analyze(config)
- if err != nil {
- b.Fatal(err)
- }
- logStats(b, i == 0, "pta", res.CallGraph, main)
- }
-}
-
func BenchmarkVTA(b *testing.B) {
b.StopTimer()
prog, main := example()