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()