cmd/guru: support streaming plain and -json output

Visible changes:
- "referrers" queries now emit a stream of results,
  so they start appearing quickly even in large queries.
  We no longer report the total number of matches.
- packageReferrers now also uses AfterTypeCheck hook and streaming.
- XML support has been dropped.
- The -format flag has been replaced by -json.

JSON protocol changes:
- The enclosing Result struct has been removed.
- Likewise the 'mode' field (since the caller knows it already)
- "freevars" and "referrers" now emit a stream of objects
  In the case of referrers, the first object has a different from the rest.
- The "referrers" results include the text of the matching line
  (parity with -json=false)

Implementation details:
- the concurrency-safe q.Output function can be called
  many times, each with a queryResult to print.
- fset is no longer saved in Query (cleaner)
- queryResult methods renamed PrintPlain, JSON

Change-Id: I41a4e3f57f266fcf043ece4045bca82c6f6a356f
Reviewed-on: https://go-review.googlesource.com/21397
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Alan Donovan 2016-04-01 15:04:45 -04:00
parent 13c24a6d6a
commit 2da0720e4f
26 changed files with 1141 additions and 1074 deletions

View File

@ -32,7 +32,6 @@ func callees(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
if err != nil {
@ -69,10 +68,10 @@ func callees(q *Query) error {
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.result = &calleesTypesResult{
q.Output(lprog.Fset, &calleesTypesResult{
site: e,
callee: obj,
}
})
return nil
}
case *ast.SelectorExpr:
@ -83,10 +82,10 @@ func callees(q *Query) error {
// or to top level function.
callee := qpos.info.Uses[funexpr.Sel]
if obj, ok := callee.(*types.Func); ok {
q.result = &calleesTypesResult{
q.Output(lprog.Fset, &calleesTypesResult{
site: e,
callee: obj,
}
})
return nil
}
} else if sel.Kind() == types.MethodVal {
@ -98,10 +97,10 @@ func callees(q *Query) error {
recvtype := method.Type().(*types.Signature).Recv().Type()
if !types.IsInterface(recvtype) {
// static method call
q.result = &calleesTypesResult{
q.Output(lprog.Fset, &calleesTypesResult{
site: e,
callee: method,
}
})
return nil
}
}
@ -139,10 +138,10 @@ func callees(q *Query) error {
return err
}
q.result = &calleesSSAResult{
q.Output(lprog.Fset, &calleesSSAResult{
site: site,
funcs: funcs,
}
})
return nil
}
@ -204,7 +203,7 @@ type calleesTypesResult struct {
callee *types.Func
}
func (r *calleesSSAResult) display(printf printfFunc) {
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())
@ -216,37 +215,37 @@ func (r *calleesSSAResult) display(printf printfFunc) {
}
}
func (r *calleesSSAResult) toSerial(res *serial.Result, fset *token.FileSet) {
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.CalleesItem{
j.Callees = append(j.Callees, &serial.Callee{
Name: callee.String(),
Pos: fset.Position(callee.Pos()).String(),
})
}
res.Callees = j
return toJSON(j)
}
func (r *calleesTypesResult) display(printf printfFunc) {
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) toSerial(res *serial.Result, fset *token.FileSet) {
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.CalleesItem{
&serial.CalleesItem{
j.Callees = []*serial.Callee{
&serial.Callee{
Name: r.callee.FullName(),
Pos: fset.Position(r.callee.Pos()).String(),
},
}
res.Callees = j
return toJSON(j)
}
// NB: byFuncPos is not deterministic across packages since it depends on load order.

View File

@ -31,7 +31,6 @@ func callers(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil {
@ -77,11 +76,11 @@ func callers(q *Query) error {
// TODO(adonovan): sort + dedup calls to ensure test determinism.
q.result = &callersResult{
q.Output(lprog.Fset, &callersResult{
target: target,
callgraph: cg,
edges: edges,
}
})
return nil
}
@ -167,7 +166,7 @@ type callersResult struct {
edges []*callgraph.Edge
}
func (r *callersResult) display(printf printfFunc) {
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)
@ -183,7 +182,7 @@ func (r *callersResult) display(printf printfFunc) {
}
}
func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *callersResult) JSON(fset *token.FileSet) []byte {
var callers []serial.Caller
for _, edge := range r.edges {
callers = append(callers, serial.Caller{
@ -192,5 +191,5 @@ func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) {
Desc: edge.Description(),
})
}
res.Callers = callers
return toJSON(callers)
}

View File

@ -96,12 +96,11 @@ func callstack(q *Query) error {
}
}
q.Fset = fset
q.result = &callstackResult{
q.Output(fset, &callstackResult{
qpos: qpos,
target: target,
callpath: callpath,
}
})
return nil
}
@ -111,7 +110,7 @@ type callstackResult struct {
callpath []*callgraph.Edge
}
func (r *callstackResult) display(printf printfFunc) {
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)
@ -124,7 +123,7 @@ func (r *callstackResult) display(printf printfFunc) {
}
}
func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) {
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]
@ -134,9 +133,9 @@ func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) {
Desc: edge.Description(),
})
}
res.Callstack = &serial.CallStack{
return toJSON(&serial.CallStack{
Pos: fset.Position(r.target.Pos()).String(),
Target: r.target.String(),
Callers: callers,
}
})
}

View File

@ -31,11 +31,10 @@ func definition(q *Query) error {
}
if obj := id.Obj; obj != nil && obj.Pos().IsValid() {
q.Fset = qpos.fset
q.result = &definitionResult{
q.Output(qpos.fset, &definitionResult{
pos: obj.Pos(),
descr: fmt.Sprintf("%s %s", obj.Kind, obj.Name),
}
})
return nil // success
}
}
@ -53,7 +52,6 @@ func definition(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil {
@ -77,10 +75,10 @@ func definition(q *Query) error {
return fmt.Errorf("%s is built in", obj.Name())
}
q.result = &definitionResult{
q.Output(lprog.Fset, &definitionResult{
pos: obj.Pos(),
descr: qpos.objectString(obj),
}
})
return nil
}
@ -89,14 +87,13 @@ type definitionResult struct {
descr string // description of object it denotes
}
func (r *definitionResult) display(printf printfFunc) {
func (r *definitionResult) PrintPlain(printf printfFunc) {
printf(r.pos, "defined here as %s", r.descr)
}
func (r *definitionResult) toSerial(res *serial.Result, fset *token.FileSet) {
definition := &serial.Definition{
func (r *definitionResult) JSON(fset *token.FileSet) []byte {
return toJSON(&serial.Definition{
Desc: r.descr,
ObjPos: fset.Position(r.pos).String(),
}
res.Definition = definition
})
}

View File

@ -40,7 +40,6 @@ func describe(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos)
if err != nil {
@ -52,43 +51,48 @@ func describe(q *Query) error {
astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
}
var qr QueryResult
path, action := findInterestingNode(qpos.info, qpos.path)
switch action {
case actionExpr:
q.result, err = describeValue(qpos, path)
qr, err = describeValue(qpos, path)
case actionType:
q.result, err = describeType(qpos, path)
qr, err = describeType(qpos, path)
case actionPackage:
q.result, err = describePackage(qpos, path)
qr, err = describePackage(qpos, path)
case actionStmt:
q.result, err = describeStmt(qpos, path)
qr, err = describeStmt(qpos, path)
case actionUnknown:
q.result = &describeUnknownResult{path[0]}
qr = &describeUnknownResult{path[0]}
default:
panic(action) // unreachable
}
return err
if err != nil {
return err
}
q.Output(lprog.Fset, qr)
return nil
}
type describeUnknownResult struct {
node ast.Node
}
func (r *describeUnknownResult) display(printf printfFunc) {
func (r *describeUnknownResult) PrintPlain(printf printfFunc) {
// Nothing much to say about misc syntax.
printf(r.node, "%s", astutil.NodeDescription(r.node))
}
func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) {
res.Describe = &serial.Describe{
func (r *describeUnknownResult) JSON(fset *token.FileSet) []byte {
return toJSON(&serial.Describe{
Desc: astutil.NodeDescription(r.node),
Pos: fset.Position(r.node.Pos()).String(),
}
})
}
type action int
@ -350,7 +354,7 @@ type describeValueResult struct {
fields []describeField
}
func (r *describeValueResult) display(printf printfFunc) {
func (r *describeValueResult) PrintPlain(printf printfFunc) {
var prefix, suffix string
if r.constVal != nil {
suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal))
@ -393,7 +397,7 @@ func (r *describeValueResult) display(printf printfFunc) {
printFields(printf, r.expr, r.fields)
}
func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *describeValueResult) JSON(fset *token.FileSet) []byte {
var value, objpos string
if r.constVal != nil {
value = r.constVal.String()
@ -402,7 +406,7 @@ func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet)
objpos = fset.Position(r.obj.Pos()).String()
}
res.Describe = &serial.Describe{
return toJSON(&serial.Describe{
Desc: astutil.NodeDescription(r.expr),
Pos: fset.Position(r.expr.Pos()).String(),
Detail: "value",
@ -411,7 +415,7 @@ func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet)
Value: value,
ObjPos: objpos,
},
}
})
}
// ---- TYPE ------------------------------------------------------------
@ -519,11 +523,12 @@ func printFields(printf printfFunc, node ast.Node, fields []describeField) {
}
}
func (r *describeTypeResult) display(printf printfFunc) {
func (r *describeTypeResult) PrintPlain(printf printfFunc) {
printf(r.node, "%s", r.description)
// Show the underlying type for a reference to a named type.
if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
// TODO(adonovan): improve display of complex struct/interface types.
printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying()))
}
@ -540,13 +545,13 @@ func (r *describeTypeResult) display(printf printfFunc) {
printFields(printf, r.node, r.fields)
}
func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *describeTypeResult) JSON(fset *token.FileSet) []byte {
var namePos, nameDef string
if nt, ok := r.typ.(*types.Named); ok {
namePos = fset.Position(nt.Obj().Pos()).String()
nameDef = nt.Underlying().String()
}
res.Describe = &serial.Describe{
return toJSON(&serial.Describe{
Desc: r.description,
Pos: fset.Position(r.node.Pos()).String(),
Detail: "type",
@ -556,7 +561,7 @@ func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
NameDef: nameDef,
Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset),
},
}
})
}
// ---- PACKAGE ------------------------------------------------------------
@ -633,7 +638,7 @@ type describeMember struct {
methods []*types.Selection // in types.MethodSet order
}
func (r *describePackageResult) display(printf printfFunc) {
func (r *describePackageResult) PrintPlain(printf printfFunc) {
printf(r.node, "%s", r.description)
// Compute max width of name "column".
@ -700,7 +705,7 @@ func formatMember(obj types.Object, maxname int) string {
return buf.String()
}
func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *describePackageResult) JSON(fset *token.FileSet) []byte {
var members []*serial.DescribeMember
for _, mem := range r.members {
typ := mem.obj.Type()
@ -720,7 +725,7 @@ func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet
Methods: methodsToSerial(r.pkg, mem.methods, fset),
})
}
res.Describe = &serial.Describe{
return toJSON(&serial.Describe{
Desc: r.description,
Pos: fset.Position(r.node.Pos()).String(),
Detail: "package",
@ -728,7 +733,7 @@ func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet
Path: r.pkg.Path(),
Members: members,
},
}
})
}
func tokenOf(o types.Object) string {
@ -778,16 +783,16 @@ type describeStmtResult struct {
description string
}
func (r *describeStmtResult) display(printf printfFunc) {
func (r *describeStmtResult) PrintPlain(printf printfFunc) {
printf(r.node, "%s", r.description)
}
func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) {
res.Describe = &serial.Describe{
func (r *describeStmtResult) JSON(fset *token.FileSet) []byte {
return toJSON(&serial.Describe{
Desc: r.description,
Pos: fset.Position(r.node.Pos()).String(),
Detail: "unknown",
}
})
}
// ------------------- Utilities -------------------

View File

@ -42,7 +42,6 @@ func freevars(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil {
@ -156,10 +155,10 @@ func freevars(q *Query) error {
}
sort.Sort(byRef(refs))
q.result = &freevarsResult{
q.Output(lprog.Fset, &freevarsResult{
qpos: qpos,
refs: refs,
}
})
return nil
}
@ -175,7 +174,7 @@ type freevarsRef struct {
obj types.Object
}
func (r *freevarsResult) display(printf printfFunc) {
func (r *freevarsResult) PrintPlain(printf printfFunc) {
if len(r.refs) == 0 {
printf(r.qpos, "No free identifiers.")
} else {
@ -192,18 +191,20 @@ func (r *freevarsResult) display(printf printfFunc) {
}
}
func (r *freevarsResult) toSerial(res *serial.Result, fset *token.FileSet) {
var refs []*serial.FreeVar
for _, ref := range r.refs {
refs = append(refs,
&serial.FreeVar{
Pos: fset.Position(ref.obj.Pos()).String(),
Kind: ref.kind,
Ref: ref.ref,
Type: ref.typ.String(),
})
func (r *freevarsResult) JSON(fset *token.FileSet) []byte {
var buf bytes.Buffer
for i, ref := range r.refs {
if i > 0 {
buf.WriteByte('\n')
}
buf.Write(toJSON(serial.FreeVar{
Pos: fset.Position(ref.obj.Pos()).String(),
Kind: ref.kind,
Ref: ref.ref,
Type: ref.typ.String(),
}))
}
res.Freevars = refs
return buf.Bytes()
}
// -------- utils --------

View File

@ -301,11 +301,9 @@ function containing the current point."
(defun go-guru-definition ()
"Jump to the definition of the selected identifier."
(interactive)
;; TODO(adonovan): use -format=sexpr when available to avoid a
;; dependency and to simplify parsing.
(let* ((res (with-current-buffer (go-guru--exec "definition" nil '("-format=json"))
(let* ((res (with-current-buffer (go-guru--exec "definition" nil '("-json"))
(goto-char (point-min))
(cdr (car (json-read)))))
(json-read)))
(desc (cdr (assoc 'desc res))))
(push-mark)
(ring-insert find-tag-marker-ring (point-marker))
@ -360,11 +358,10 @@ expression (of type 'error') may refer."
(defun go-guru-what ()
"Run a 'what' query and return the parsed JSON response as an
associative list."
(let ((res (with-current-buffer (go-guru--exec "what" nil '("-format=json") t)
(goto-char (point-min))
(cdr (car (json-read))))))
res))
association list."
(with-current-buffer (go-guru--exec "what" nil '("-json") t)
(goto-char (point-min))
(json-read)))
(defun go-guru--hl-symbols (posn face id)
"Highlight the symbols at the positions POSN by creating

View File

@ -11,6 +11,7 @@ package main
// (&T{}, var t T, new(T), new(struct{array [3]T}), etc.
import (
"encoding/json"
"fmt"
"go/ast"
"go/build"
@ -18,9 +19,9 @@ import (
"go/token"
"go/types"
"io"
"log"
"path/filepath"
"golang.org/x/tools/cmd/guru/serial"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
@ -30,10 +31,15 @@ import (
type printfFunc func(pos interface{}, format string, args ...interface{})
// queryResult is the interface of each query-specific result type.
type queryResult interface {
toSerial(res *serial.Result, fset *token.FileSet)
display(printf printfFunc)
// A QueryResult is an item of output. Each query produces a stream of
// query results, calling Query.Output for each one.
type QueryResult interface {
// JSON returns the QueryResult in JSON form.
JSON(fset *token.FileSet) []byte
// PrintPlain prints the QueryResult in plain text form.
// The implementation calls printfFunc to print each line of output.
PrintPlain(printf printfFunc)
}
// A QueryPos represents the position provided as input to a query:
@ -65,7 +71,6 @@ func (qpos *queryPos) selectionString(sel *types.Selection) string {
// A Query specifies a single guru query.
type Query struct {
Mode string // query mode ("callers", etc)
Pos string // query position
Build *build.Context // package loading configuration
@ -74,32 +79,13 @@ type Query struct {
PTALog io.Writer // (optional) pointer-analysis log file
Reflection bool // model reflection soundly (currently slow).
// Populated during Run()
Fset *token.FileSet
result queryResult
}
// Serial returns an instance of serial.Result, which implements the
// {xml,json}.Marshaler interfaces so that query results can be
// serialized as JSON or XML.
//
func (q *Query) Serial() *serial.Result {
resj := &serial.Result{Mode: q.Mode}
q.result.toSerial(resj, q.Fset)
return resj
}
// WriteTo writes the guru query result res to out in a compiler diagnostic format.
func (q *Query) WriteTo(out io.Writer) {
printf := func(pos interface{}, format string, args ...interface{}) {
fprintf(out, q.Fset, pos, format, args...)
}
q.result.display(printf)
// result-printing function
Output func(*token.FileSet, QueryResult)
}
// Run runs an guru query and populates its Fset and Result.
func Run(q *Query) error {
switch q.Mode {
func Run(mode string, q *Query) error {
switch mode {
case "callees":
return callees(q)
case "callers":
@ -125,7 +111,7 @@ func Run(q *Query) error {
case "what":
return what(q)
default:
return fmt.Errorf("invalid mode: %q", q.Mode)
return fmt.Errorf("invalid mode: %q", mode)
}
}
@ -319,7 +305,6 @@ func deref(typ types.Type) types.Type {
//
// The output format is is compatible with the 'gnu'
// compilation-error-regexp in Emacs' compilation mode.
// TODO(adonovan): support other editors.
//
func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
var start, end token.Pos
@ -360,3 +345,11 @@ func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, a
fmt.Fprintf(w, format, args...)
io.WriteString(w, "\n")
}
func toJSON(x interface{}) []byte {
b, err := json.MarshalIndent(x, "", "\t")
if err != nil {
log.Fatalf("JSON error: %v", err)
}
return b
}

View File

@ -28,7 +28,6 @@ package main_test
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"go/build"
@ -41,8 +40,10 @@ import (
"path/filepath"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"testing"
guru "golang.org/x/tools/cmd/guru"
@ -150,51 +151,53 @@ func parseQueries(t *testing.T, filename string) []*query {
return queries
}
// WriteResult writes res (-format=plain) to w, stripping file locations.
func WriteResult(w io.Writer, q *guru.Query) {
capture := new(bytes.Buffer) // capture standard output
q.WriteTo(capture)
for _, line := range strings.Split(capture.String(), "\n") {
// Remove a "file:line: " prefix.
if i := strings.Index(line, ": "); i >= 0 {
line = line[i+2:]
}
fmt.Fprintf(w, "%s\n", line)
}
}
// doQuery poses query q to the guru and writes its response and
// error (if any) to out.
func doQuery(out io.Writer, q *query, useJson bool) {
func doQuery(out io.Writer, q *query, json bool) {
fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)
var buildContext = build.Default
buildContext.GOPATH = "testdata"
pkg := filepath.Dir(strings.TrimPrefix(q.filename, "testdata/src/"))
var outputMu sync.Mutex // guards out, jsons
var jsons []string
output := func(fset *token.FileSet, qr guru.QueryResult) {
outputMu.Lock()
defer outputMu.Unlock()
if json {
jsons = append(jsons, string(qr.JSON(fset)))
} else {
// suppress position information
qr.PrintPlain(func(_ interface{}, format string, args ...interface{}) {
fmt.Fprintf(out, format, args...)
io.WriteString(out, "\n")
})
}
}
query := guru.Query{
Mode: q.verb,
Pos: q.queryPos,
Build: &buildContext,
Scope: []string{pkg},
Reflection: true,
Output: output,
}
if err := guru.Run(&query); err != nil {
if err := guru.Run(q.verb, &query); err != nil {
fmt.Fprintf(out, "\nError: %s\n", err)
return
}
if useJson {
// JSON output
b, err := json.MarshalIndent(query.Serial(), "", "\t")
if err != nil {
fmt.Fprintf(out, "JSON error: %s\n", err.Error())
return
if json {
if q.verb == "referrers" {
sort.Strings(jsons[1:]) // for determinism
}
for _, json := range jsons {
fmt.Fprintf(out, "%s\n", json)
}
out.Write(b)
fmt.Fprintln(out)
} else {
// "plain" (compiler diagnostic format) output
WriteResult(out, &query)
io.WriteString(out, "\n")
}
}
@ -230,7 +233,7 @@ func TestGuru(t *testing.T) {
"testdata/src/referrers-json/main.go",
"testdata/src/what-json/main.go",
} {
useJson := strings.Contains(filename, "-json/")
json := strings.Contains(filename, "-json/")
queries := parseQueries(t, filename)
golden := filename + "lden"
got := filename + "t"
@ -245,7 +248,7 @@ func TestGuru(t *testing.T) {
// Run the guru on each query, redirecting its output
// and error (if any) to the foo.got file.
for _, q := range queries {
doQuery(gotfh, q, useJson)
doQuery(gotfh, q, json)
}
// Compare foo.got with foo.golden.
@ -277,11 +280,10 @@ func TestIssue14684(t *testing.T) {
var buildContext = build.Default
buildContext.GOPATH = "testdata"
query := guru.Query{
Mode: "freevars",
Pos: "testdata/src/README.txt:#1",
Build: &buildContext,
}
err := guru.Run(&query)
err := guru.Run("freevars", &query)
if err == nil {
t.Fatal("guru query succeeded unexpectedly")
}

View File

@ -63,7 +63,6 @@ func implements(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil {
@ -179,9 +178,9 @@ func implements(q *Query) error {
}
}
q.result = &implementsResult{
q.Output(lprog.Fset, &implementsResult{
qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
}
})
return nil
}
@ -201,7 +200,7 @@ type implementsResult struct {
fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any
}
func (r *implementsResult) display(printf printfFunc) {
func (r *implementsResult) PrintPlain(printf printfFunc) {
relation := "is implemented by"
meth := func(sel *types.Selection) {
@ -298,8 +297,15 @@ func (r *implementsResult) display(printf printfFunc) {
}
}
func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
res.Implements = &serial.Implements{
func (r *implementsResult) JSON(fset *token.FileSet) []byte {
var method *serial.DescribeMethod
if r.method != nil {
method = &serial.DescribeMethod{
Name: r.qpos.objectString(r.method),
Pos: fset.Position(r.method.Pos()).String(),
}
}
return toJSON(&serial.Implements{
T: makeImplementsType(r.t, fset),
AssignableTo: makeImplementsTypes(r.to, fset),
AssignableFrom: makeImplementsTypes(r.from, fset),
@ -307,13 +313,9 @@ func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
}
if r.method != nil {
res.Implements.Method = &serial.DescribeMethod{
Name: r.qpos.objectString(r.method),
Pos: fset.Position(r.method.Pos()).String(),
}
}
Method: method,
})
}
func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType {

View File

@ -14,11 +14,10 @@ package main // import "golang.org/x/tools/cmd/guru"
import (
"bufio"
"bytes"
"encoding/json"
"encoding/xml"
"flag"
"fmt"
"go/build"
"go/token"
"io"
"io/ioutil"
"log"
@ -27,6 +26,7 @@ import (
"runtime/pprof"
"strconv"
"strings"
"sync"
"golang.org/x/tools/go/buildutil"
)
@ -36,7 +36,7 @@ var (
modifiedFlag = flag.Bool("modified", false, "read archive of modified files from standard input")
scopeFlag = flag.String("scope", "", "comma-separated list of `packages` the analysis should be limited to")
ptalogFlag = flag.String("ptalog", "", "write points-to analysis log to `file`")
formatFlag = flag.String("format", "plain", "output `format`; one of {plain,json,xml}")
jsonFlag = flag.Bool("json", false, "emit output in JSON format")
reflectFlag = flag.Bool("reflect", false, "analyze reflection soundly (slow)")
cpuprofileFlag = flag.String("cpuprofile", "", "write CPU profile to `file`")
)
@ -71,11 +71,10 @@ of the syntax element to query. For example:
foo.go:#123,#128
bar.go:#123
The -format flag controls the output format:
plain an editor-friendly format in which every line of output
is of the form "pos: text", where pos is "-" if unknown.
json structured data in JSON syntax.
xml structured data in XML syntax.
The -json flag causes guru to emit output in JSON format;
golang.org/x/tools/cmd/guru/serial defines its schema.
Otherwise, the output is in an editor-friendly format in which
every line has the form "pos: text", where pos is "-" if unknown.
The -modified flag causes guru to read an archive from standard input.
Files in this archive will be used in preference to those in
@ -163,14 +162,6 @@ func main() {
defer pprof.StopCPUProfile()
}
// -format flag
switch *formatFlag {
case "json", "plain", "xml":
// ok
default:
log.Fatalf("illegal -format value: %q.\n"+useHelp, *formatFlag)
}
ctxt := &build.Default
// If there were modified files,
@ -191,39 +182,35 @@ func main() {
}
}
var outputMu sync.Mutex
output := func(fset *token.FileSet, qr QueryResult) {
outputMu.Lock()
defer outputMu.Unlock()
if *jsonFlag {
// JSON output
fmt.Printf("%s\n", qr.JSON(fset))
} else {
// plain output
printf := func(pos interface{}, format string, args ...interface{}) {
fprintf(os.Stdout, fset, pos, format, args...)
}
qr.PrintPlain(printf)
}
}
// Ask the guru.
query := Query{
Mode: mode,
Pos: posn,
Build: ctxt,
Scope: strings.Split(*scopeFlag, ","),
PTALog: ptalog,
Reflection: *reflectFlag,
Output: output,
}
if err := Run(&query); err != nil {
if err := Run(mode, &query); err != nil {
log.Fatal(err)
}
// Print the result.
switch *formatFlag {
case "json":
b, err := json.MarshalIndent(query.Serial(), "", "\t")
if err != nil {
log.Fatalf("JSON error: %s", err)
}
os.Stdout.Write(b)
case "xml":
b, err := xml.MarshalIndent(query.Serial(), "", "\t")
if err != nil {
log.Fatalf("XML error: %s", err)
}
os.Stdout.Write(b)
case "plain":
query.WriteTo(os.Stdout)
}
}
func parseArchive(archive io.Reader) (map[string][]byte, error) {

View File

@ -35,7 +35,6 @@ func peers(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil {
@ -127,14 +126,14 @@ func peers(q *Query) error {
sort.Sort(byPos(receives))
sort.Sort(byPos(closes))
q.result = &peersResult{
q.Output(lprog.Fset, &peersResult{
queryPos: opPos,
queryType: queryType,
makes: makes,
sends: sends,
receives: receives,
closes: closes,
}
})
return nil
}
@ -195,13 +194,14 @@ func chanOps(instr ssa.Instruction) []chanOp {
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) display(printf printfFunc) {
func (r *peersResult) PrintPlain(printf printfFunc) {
if len(r.makes) == 0 {
printf(r.queryPos, "This channel can't point to anything.")
return
@ -221,7 +221,7 @@ func (r *peersResult) display(printf printfFunc) {
}
}
func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *peersResult) JSON(fset *token.FileSet) []byte {
peers := &serial.Peers{
Pos: fset.Position(r.queryPos).String(),
Type: r.queryType.String(),
@ -238,7 +238,7 @@ func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
for _, clos := range r.closes {
peers.Closes = append(peers.Closes, fset.Position(clos).String())
}
res.Peers = peers
return toJSON(peers)
}
// -------- utils --------

View File

@ -38,7 +38,6 @@ func pointsto(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
if err != nil {
@ -103,11 +102,11 @@ func pointsto(q *Query) error {
return err // e.g. analytically unreachable
}
q.result = &pointstoResult{
q.Output(lprog.Fset, &pointstoResult{
qpos: qpos,
typ: typ,
ptrs: ptrs,
}
})
return nil
}
@ -209,7 +208,7 @@ type pointstoResult struct {
ptrs []pointerResult // pointer info (typ is concrete => len==1)
}
func (r *pointstoResult) display(printf printfFunc) {
func (r *pointstoResult) PrintPlain(printf printfFunc) {
if pointer.CanHaveDynamicTypes(r.typ) {
// Show concrete types for interface, reflect.Type or
// reflect.Value expression.
@ -244,7 +243,7 @@ func (r *pointstoResult) display(printf printfFunc) {
}
}
func (r *pointstoResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *pointstoResult) JSON(fset *token.FileSet) []byte {
var pts []serial.PointsTo
for _, ptr := range r.ptrs {
var namePos string
@ -264,7 +263,7 @@ func (r *pointstoResult) toSerial(res *serial.Result, fset *token.FileSet) {
Labels: labels,
})
}
res.PointsTo = pts
return toJSON(pts)
}
type byTypeString []pointerResult

View File

@ -82,17 +82,13 @@ func referrers(q *Query) error {
return globalReferrers(q, qpos.info.Pkg.Path(), defpkg, objposn, pkglevel)
}
// Find uses of obj within the query package.
refs := usesOf(obj, qpos.info)
sort.Sort(byNamePos{fset, refs})
q.Fset = fset
q.result = &referrersResult{
build: q.Build,
fset: fset,
q.Output(fset, &referrersInitialResult{
qinfo: qpos.info,
obj: obj,
refs: refs,
}
})
outputUses(q, fset, usesOf(obj, qpos.info), obj.Pkg())
return nil // success
}
@ -113,8 +109,8 @@ func classify(obj types.Object) (global, pkglevel bool) {
return false, false
}
// packageReferrers finds all references to the specified package
// throughout the workspace and populates q.result.
// packageReferrers reports all references to the specified package
// throughout the workspace.
func packageReferrers(q *Query, path string) error {
// Scan the workspace and build the import graph.
// Ignore broken packages.
@ -134,34 +130,86 @@ func packageReferrers(q *Query, path string) error {
},
}
allowErrors(&lconf)
// The importgraph doesn't treat external test packages
// as separate nodes, so we must use ImportWithTests.
for path := range users {
lconf.ImportWithTests(path)
}
lprog, err := lconf.Load()
if err != nil {
return err
// Subtle! AfterTypeCheck needs no mutex for qpkg because the
// topological import order gives us the necessary happens-before edges.
// TODO(adonovan): what about import cycles?
var qpkg *types.Package
// For efficiency, we scan each package for references
// just after it has been type-checked. The loader calls
// AfterTypeCheck (concurrently), providing us with a stream of
// packages.
lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
// AfterTypeCheck may be called twice for the same package due to augmentation.
if info.Pkg.Path() == path && qpkg == nil {
// Found the package of interest.
qpkg = info.Pkg
fakepkgname := types.NewPkgName(token.NoPos, qpkg, qpkg.Name(), qpkg)
q.Output(fset, &referrersInitialResult{
qinfo: info,
obj: fakepkgname, // bogus
})
}
// Only inspect packages that directly import the
// declaring package (and thus were type-checked).
if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
// Find PkgNames that refer to qpkg.
// TODO(adonovan): perhaps more useful would be to show imports
// of the package instead of qualified identifiers.
var refs []*ast.Ident
for id, obj := range info.Uses {
if obj, ok := obj.(*types.PkgName); ok && obj.Imported() == qpkg {
refs = append(refs, id)
}
}
outputUses(q, fset, refs, info.Pkg)
}
clearInfoFields(info) // save memory
}
// Find uses of [a fake PkgName that imports] the package.
//
// TODO(adonovan): perhaps more useful would be to show imports
// of the package instead of qualified identifiers.
qinfo := lprog.Package(path)
obj := types.NewPkgName(token.NoPos, qinfo.Pkg, qinfo.Pkg.Name(), qinfo.Pkg)
refs := usesOf(obj, lprog.InitialPackages()...)
sort.Sort(byNamePos{fset, refs})
q.Fset = fset
q.result = &referrersResult{
build: q.Build,
fset: fset,
qinfo: qinfo,
obj: obj,
refs: refs,
lconf.Load() // ignore error
if qpkg == nil {
log.Fatalf("query package %q not found during reloading", path)
}
return nil
}
// globalReferrers finds references throughout the entire workspace to the
func usesOf(queryObj types.Object, info *loader.PackageInfo) []*ast.Ident {
var refs []*ast.Ident
for id, obj := range info.Uses {
if sameObj(queryObj, obj) {
refs = append(refs, id)
}
}
return refs
}
// outputUses outputs a result describing refs, which appear in the package denoted by info.
func outputUses(q *Query, fset *token.FileSet, refs []*ast.Ident, pkg *types.Package) {
if len(refs) > 0 {
sort.Sort(byNamePos{fset, refs})
q.Output(fset, &referrersPackageResult{
pkg: pkg,
build: q.Build,
fset: fset,
refs: refs,
})
}
}
// globalReferrers reports references throughout the entire workspace to the
// object at the specified source position. Its defining package is defpkg,
// and the query package is qpkg. isPkgLevel indicates whether the object
// is defined at package-level.
@ -207,6 +255,11 @@ func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position, isPk
// so we can't use them here.
// TODO(adonovan): smooth things out once the other changes have landed.
// Results are reported concurrently from within the
// AfterTypeCheck hook. The program may provide a useful stream
// of information even if the user doesn't let the program run
// to completion.
var (
mu sync.Mutex
qobj types.Object
@ -217,8 +270,9 @@ func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position, isPk
// just after it has been type-checked. The loader calls
// AfterTypeCheck (concurrently), providing us with a stream of
// packages.
ch := make(chan []*ast.Ident)
lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
// AfterTypeCheck may be called twice for the same package due to augmentation.
// Only inspect packages that depend on the declaring package
// (and thus were type-checked).
if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
@ -233,66 +287,32 @@ func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position, isPk
log.Fatalf("object at %s not found in package %s",
objposn, defpkg)
}
// Object found.
qinfo = info
q.Output(fset, &referrersInitialResult{
qinfo: qinfo,
obj: qobj,
})
}
obj := qobj
mu.Unlock()
// Look for references to the query object.
if obj != nil {
ch <- usesOf(obj, info)
outputUses(q, fset, usesOf(obj, info), info.Pkg)
}
}
// TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
// (Requires go/types change for Go 1.7.)
// info.Pkg.Scope().ClearChildren()
// Discard the file ASTs and their accumulated type
// information to save memory.
info.Files = nil
info.Defs = make(map[*ast.Ident]types.Object)
info.Uses = make(map[*ast.Ident]types.Object)
info.Implicits = make(map[ast.Node]types.Object)
// Also, disable future collection of wholly unneeded
// type information for the package in case there is
// more type-checking to do (augmentation).
info.Types = nil
info.Scopes = nil
info.Selections = nil
clearInfoFields(info) // save memory
}
go func() {
lconf.Load() // ignore error
close(ch)
}()
var refs []*ast.Ident
for ids := range ch {
refs = append(refs, ids...)
}
sort.Sort(byNamePos{fset, refs})
lconf.Load() // ignore error
if qobj == nil {
log.Fatal("query object not found during reloading")
}
// TODO(adonovan): in a follow-up, do away with the
// analyze/display split so we can print a stream of output
// directly from the AfterTypeCheck hook.
// (We should not assume that users let the program run long
// enough for Load to return.)
q.Fset = fset
q.result = &referrersResult{
build: q.Build,
fset: fset,
qinfo: qinfo,
obj: qobj,
refs: refs,
}
return nil // success
}
@ -318,20 +338,6 @@ func findObject(fset *token.FileSet, info *types.Info, objposn token.Position) t
return nil
}
// usesOf returns all identifiers in the packages denoted by infos
// that refer to queryObj.
func usesOf(queryObj types.Object, infos ...*loader.PackageInfo) []*ast.Ident {
var refs []*ast.Ident
for _, info := range infos {
for id, obj := range info.Uses {
if sameObj(queryObj, obj) {
refs = append(refs, id)
}
}
}
return refs
}
// same reports whether x and y are identical, or both are PkgNames
// that import the same Package.
//
@ -347,6 +353,26 @@ func sameObj(x, y types.Object) bool {
return false
}
func clearInfoFields(info *loader.PackageInfo) {
// TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
// (Requires go/types change for Go 1.7.)
// info.Pkg.Scope().ClearChildren()
// Discard the file ASTs and their accumulated type
// information to save memory.
info.Files = nil
info.Defs = make(map[*ast.Ident]types.Object)
info.Uses = make(map[*ast.Ident]types.Object)
info.Implicits = make(map[ast.Node]types.Object)
// Also, disable future collection of wholly unneeded
// type information for the package in case there is
// more type-checking to do (augmentation).
info.Types = nil
info.Scopes = nil
info.Selections = nil
}
// -------- utils --------
// An deterministic ordering for token.Pos that doesn't
@ -371,19 +397,39 @@ func (p byNamePos) Less(i, j int) bool {
return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
}
type referrersResult struct {
// referrersInitialResult is the initial result of a "referrers" query.
type referrersInitialResult struct {
qinfo *loader.PackageInfo
obj types.Object // object it denotes
}
func (r *referrersInitialResult) PrintPlain(printf printfFunc) {
printf(r.obj, "references to %s",
types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg)))
}
func (r *referrersInitialResult) JSON(fset *token.FileSet) []byte {
var objpos string
if pos := r.obj.Pos(); pos.IsValid() {
objpos = fset.Position(pos).String()
}
return toJSON(&serial.ReferrersInitial{
Desc: r.obj.String(),
ObjPos: objpos,
})
}
// referrersPackageResult is the streaming result for one package of a "referrers" query.
type referrersPackageResult struct {
pkg *types.Package
build *build.Context
fset *token.FileSet
qinfo *loader.PackageInfo
qpos *queryPos
obj types.Object // object it denotes
refs []*ast.Ident // set of all other references to it
}
func (r *referrersResult) display(printf printfFunc) {
printf(r.obj, "%d references to %s",
len(r.refs), types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg)))
// forEachRef calls f(id, text) for id in r.refs, in order.
// Text is the text of the line on which id appears.
func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) {
// Show referring lines, like grep.
type fileinfo struct {
refs []*ast.Ident
@ -432,13 +478,13 @@ func (r *referrersResult) display(printf printfFunc) {
if more := len(fi.refs) - 1; more > 0 {
suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
}
printf(fi.refs[0], "%v%s", err, suffix)
f(fi.refs[0], err.Error()+suffix)
continue
}
lines := bytes.Split(v.([]byte), []byte("\n"))
for i, ref := range fi.refs {
printf(ref, "%s", lines[fi.linenums[i]-1])
f(ref, string(lines[fi.linenums[i]-1]))
}
}
}
@ -458,15 +504,19 @@ func readFile(ctxt *build.Context, filename string) ([]byte, error) {
return buf.Bytes(), nil
}
func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) {
referrers := &serial.Referrers{
Desc: r.obj.String(),
}
if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos()
referrers.ObjPos = fset.Position(pos).String()
}
for _, ref := range r.refs {
referrers.Refs = append(referrers.Refs, fset.Position(ref.NamePos).String())
}
res.Referrers = referrers
func (r *referrersPackageResult) PrintPlain(printf printfFunc) {
r.foreachRef(func(id *ast.Ident, text string) {
printf(id, "%s", text)
})
}
func (r *referrersPackageResult) JSON(fset *token.FileSet) []byte {
refs := serial.ReferrersPackage{Package: r.pkg.Path()}
r.foreachRef(func(id *ast.Ident, text string) {
refs.Refs = append(refs.Refs, serial.Ref{
Pos: fset.Position(id.NamePos).String(),
Text: text,
})
})
return toJSON(refs)
}

View File

@ -2,17 +2,30 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package serial defines the guru's schema for structured data
// serialization using JSON, XML, etc.
package serial
// All 'pos' strings are of the form "file:line:col".
// TODO(adonovan): improve performance by sharing filename strings.
// TODO(adonovan): improve precision by providing the start/end
// interval when available.
// Package serial defines the guru's schema for -json output.
//
// TODO(adonovan): consider richer encodings of types, functions,
// methods, etc.
// The output of a guru query is a stream of one or more JSON objects.
// This table shows the types of objects in the result stream for each
// query type.
//
// 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.
package serial
// A Peers is the result of a 'peers' query.
// If Allocs is empty, the selected channel can't point to anything.
@ -25,12 +38,22 @@ type Peers struct {
Closes []string `json:"closes,omitempty"` // locations of aliased close(ch) ops
}
// A Referrers is the result of a 'referrers' query.
type Referrers struct {
ObjPos string `json:"objpos,omitempty"` // location of the definition
Desc string `json:"desc"` // description of the denoted object
Refs []string `json:"refs,omitempty"` // locations of all references
}
// A "referrers" query emits a ReferrersInitial object followed by zero or
// more ReferrersPackage objects, one per package that contains a reference.
type (
ReferrersInitial struct {
ObjPos string `json:"objpos,omitempty"` // location of the definition
Desc string `json:"desc"` // description of the denoted object
}
ReferrersPackage struct {
Package string `json:"package"`
Refs []Ref `json:"refs"` // non-empty list of references within this package
}
Ref struct {
Pos string `json:"pos"` // location of all references
Text string `json:"text"` // text of the referring line
}
)
// A Definition is the result of a 'definition' query.
type Definition struct {
@ -38,20 +61,21 @@ type Definition struct {
Desc string `json:"desc"` // description of the denoted object
}
type CalleesItem struct {
Name string `json:"name"` // full name of called function
Pos string `json:"pos"` // location of called function
}
// A Callees is the result of a 'callees' query.
//
// Callees is nonempty unless the call was a dynamic call on a
// provably nil func or interface value.
type Callees struct {
Pos string `json:"pos"` // location of selected call site
Desc string `json:"desc"` // description of call site
Callees []*CalleesItem `json:"callees,omitempty"` // set of possible call targets
}
type (
Callees struct {
Pos string `json:"pos"` // location of selected call site
Desc string `json:"desc"` // description of call site
Callees []*Callee `json:"callees"`
}
Callee struct {
Name string `json:"name"` // full name of called function
Pos string `json:"pos"` // location of called function
}
)
// A Caller is one element of the slice returned by a 'callers' query.
// (Callstack also contains a similar slice.)
@ -233,27 +257,3 @@ type WhichErrsType struct {
Type string `json:"type,omitempty"`
Position string `json:"position,omitempty"`
}
// A Result is the common result of any guru query.
// It contains a query-specific result element.
//
// TODO(adonovan): perhaps include other info such as: analysis scope,
// raw query position, stack of ast nodes, query package, etc.
type Result struct {
Mode string `json:"mode"` // mode of the query
// Exactly one of the following fields is populated:
// the one specified by 'mode'.
Callees *Callees `json:"callees,omitempty"`
Callers []Caller `json:"callers,omitempty"`
Callstack *CallStack `json:"callstack,omitempty"`
Definition *Definition `json:"definition,omitempty"`
Describe *Describe `json:"describe,omitempty"`
Freevars []*FreeVar `json:"freevars,omitempty"`
Implements *Implements `json:"implements,omitempty"`
Peers *Peers `json:"peers,omitempty"`
PointsTo []PointsTo `json:"pointsto,omitempty"`
Referrers *Referrers `json:"referrers,omitempty"`
What *What `json:"what,omitempty"`
WhichErrs *WhichErrs `json:"whicherrs,omitempty"`
}

View File

@ -1,34 +1,28 @@
-------- @callees @callees-f --------
{
"mode": "callees",
"callees": {
"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"
}
]
}
"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 --------
{
"mode": "callstack",
"callstack": {
"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"
}
]
}
"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"
}
]
}

View File

@ -1,111 +1,96 @@
-------- @describe pkgdecl --------
{
"mode": "describe",
"describe": {
"desc": "definition of package \"describe-json\"",
"pos": "testdata/src/describe-json/main.go:1:9",
"detail": "package",
"package": {
"path": "describe-json",
"members": [
{
"name": "C",
"type": "int",
"pos": "testdata/src/describe-json/main.go:25:6",
"kind": "type",
"methods": [
{
"name": "method (C) f()",
"pos": "testdata/src/describe-json/main.go:28:12"
}
]
},
{
"name": "D",
"type": "struct{}",
"pos": "testdata/src/describe-json/main.go:26:6",
"kind": "type",
"methods": [
{
"name": "method (*D) f()",
"pos": "testdata/src/describe-json/main.go:29:13"
}
]
},
{
"name": "I",
"type": "interface{f()}",
"pos": "testdata/src/describe-json/main.go:21:6",
"kind": "type",
"methods": [
{
"name": "method (I) f()",
"pos": "testdata/src/describe-json/main.go:22:2"
}
]
},
{
"name": "main",
"type": "func()",
"pos": "testdata/src/describe-json/main.go:7:6",
"kind": "func"
}
]
}
"desc": "definition of package \"describe-json\"",
"pos": "testdata/src/describe-json/main.go:1:9",
"detail": "package",
"package": {
"path": "describe-json",
"members": [
{
"name": "C",
"type": "int",
"pos": "testdata/src/describe-json/main.go:25:6",
"kind": "type",
"methods": [
{
"name": "method (C) f()",
"pos": "testdata/src/describe-json/main.go:28:12"
}
]
},
{
"name": "D",
"type": "struct{}",
"pos": "testdata/src/describe-json/main.go:26:6",
"kind": "type",
"methods": [
{
"name": "method (*D) f()",
"pos": "testdata/src/describe-json/main.go:29:13"
}
]
},
{
"name": "I",
"type": "interface{f()}",
"pos": "testdata/src/describe-json/main.go:21:6",
"kind": "type",
"methods": [
{
"name": "method (I) f()",
"pos": "testdata/src/describe-json/main.go:22:2"
}
]
},
{
"name": "main",
"type": "func()",
"pos": "testdata/src/describe-json/main.go:7:6",
"kind": "func"
}
]
}
}
-------- @describe desc-val-p --------
{
"mode": "describe",
"describe": {
"desc": "identifier",
"pos": "testdata/src/describe-json/main.go:9:2",
"detail": "value",
"value": {
"type": "*int",
"objpos": "testdata/src/describe-json/main.go:9:2"
}
"desc": "identifier",
"pos": "testdata/src/describe-json/main.go:9:2",
"detail": "value",
"value": {
"type": "*int",
"objpos": "testdata/src/describe-json/main.go:9:2"
}
}
-------- @describe desc-val-i --------
{
"mode": "describe",
"describe": {
"desc": "identifier",
"pos": "testdata/src/describe-json/main.go:16:8",
"detail": "value",
"value": {
"type": "I",
"objpos": "testdata/src/describe-json/main.go:12:6"
}
"desc": "identifier",
"pos": "testdata/src/describe-json/main.go:16:8",
"detail": "value",
"value": {
"type": "I",
"objpos": "testdata/src/describe-json/main.go:12:6"
}
}
-------- @describe desc-stmt --------
{
"mode": "describe",
"describe": {
"desc": "go statement",
"pos": "testdata/src/describe-json/main.go:18:2",
"detail": "unknown"
}
"desc": "go statement",
"pos": "testdata/src/describe-json/main.go:18:2",
"detail": "unknown"
}
-------- @describe desc-type-C --------
{
"mode": "describe",
"describe": {
"desc": "definition of type C (size 8, align 8)",
"pos": "testdata/src/describe-json/main.go:25:6",
"detail": "type",
"type": {
"type": "C",
"namepos": "testdata/src/describe-json/main.go:25:6",
"namedef": "int",
"methods": [
{
"name": "method (C) f()",
"pos": "testdata/src/describe-json/main.go:28:12"
}
]
}
"desc": "definition of type C (size 8, align 8)",
"pos": "testdata/src/describe-json/main.go:25:6",
"detail": "type",
"type": {
"type": "C",
"namepos": "testdata/src/describe-json/main.go:25:6",
"namedef": "int",
"methods": [
{
"name": "method (C) f()",
"pos": "testdata/src/describe-json/main.go:28:12"
}
]
}
}

View File

@ -1,159 +1,135 @@
-------- @implements E --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-json.E",
"pos": "testdata/src/implements-json/main.go:10:6",
"kind": "interface"
}
"type": {
"name": "implements-json.E",
"pos": "testdata/src/implements-json/main.go:10:6",
"kind": "interface"
}
}
-------- @implements F --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-json.C",
"pos": "testdata/src/implements-json/main.go:21:6",
"kind": "pointer"
},
{
"name": "implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6",
"kind": "struct"
},
{
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
}
]
}
}
-------- @implements FG --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6",
"kind": "pointer"
}
],
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
]
}
}
-------- @implements slice --------
{
"mode": "implements",
"implements": {
"type": {
"name": "[]int",
"pos": "-",
"kind": "slice"
}
}
}
-------- @implements C --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-json.C",
"pos": "testdata/src/implements-json/main.go:21:6",
"kind": "basic"
},
"fromptr": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
]
}
}
-------- @implements starC --------
{
"mode": "implements",
"implements": {
"type": {
"type": {
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-json.C",
"pos": "testdata/src/implements-json/main.go:21:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
]
}
}
-------- @implements D --------
{
"mode": "implements",
"implements": {
"type": {
{
"name": "implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6",
"kind": "struct"
},
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
],
"fromptr": [
{
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
}
]
}
{
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
}
]
}
-------- @implements starD --------
-------- @implements FG --------
{
"mode": "implements",
"implements": {
"type": {
"type": {
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
},
{
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
}
]
}
],
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
]
}
-------- @implements slice --------
{
"type": {
"name": "[]int",
"pos": "-",
"kind": "slice"
}
}
-------- @implements C --------
{
"type": {
"name": "implements-json.C",
"pos": "testdata/src/implements-json/main.go:21:6",
"kind": "basic"
},
"fromptr": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
]
}
-------- @implements starC --------
{
"type": {
"name": "*implements-json.C",
"pos": "testdata/src/implements-json/main.go:21:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
]
}
-------- @implements D --------
{
"type": {
"name": "implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6",
"kind": "struct"
},
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
}
],
"fromptr": [
{
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
}
]
}
-------- @implements starD --------
{
"type": {
"name": "*implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-json.F",
"pos": "testdata/src/implements-json/main.go:12:6",
"kind": "interface"
},
{
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
}
]
}

View File

@ -1,290 +1,266 @@
-------- @implements F.f --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-methods-json.C",
"pos": "testdata/src/implements-methods-json/main.go:21:6",
"kind": "pointer"
},
{
"name": "implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "struct"
},
{
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (F).f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
},
"to_method": [
{
"name": "method (*C) f()",
"pos": "testdata/src/implements-methods-json/main.go:24:13"
},
{
"name": "method (D) f()",
"pos": "testdata/src/implements-methods-json/main.go:25:12"
},
{
"name": "method (FG) f()",
"pos": "testdata/src/implements-methods-json/main.go:17:2"
}
]
}
}
-------- @implements FG.f --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "pointer"
}
],
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"method": {
"name": "func (FG).f()",
"pos": "testdata/src/implements-methods-json/main.go:17:2"
},
"to_method": [
{
"name": "method (*D) f()",
"pos": "testdata/src/implements-methods-json/main.go:25:12"
}
],
"from_method": [
{
"name": "method (F) f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
}
]
}
}
-------- @implements FG.g --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "pointer"
}
],
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"method": {
"name": "func (FG).g() []int",
"pos": "testdata/src/implements-methods-json/main.go:18:2"
},
"to_method": [
{
"name": "method (*D) g() []int",
"pos": "testdata/src/implements-methods-json/main.go:27:13"
}
],
"from_method": [
{
"name": "",
"pos": ""
}
]
}
}
-------- @implements *C.f --------
{
"mode": "implements",
"implements": {
"type": {
"type": {
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-methods-json.C",
"pos": "testdata/src/implements-methods-json/main.go:21:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"method": {
"name": "func (*C).f()",
"pos": "testdata/src/implements-methods-json/main.go:24:13"
},
"from_method": [
{
"name": "method (F) f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
}
]
}
}
-------- @implements D.f --------
{
"mode": "implements",
"implements": {
"type": {
{
"name": "implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "struct"
},
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"fromptr": [
{
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (D).f()",
{
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (F).f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
},
"to_method": [
{
"name": "method (*C) f()",
"pos": "testdata/src/implements-methods-json/main.go:24:13"
},
{
"name": "method (D) f()",
"pos": "testdata/src/implements-methods-json/main.go:25:12"
},
"from_method": [
{
"name": "method (F) f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
}
],
"fromptr_method": [
{
"name": "method (FG) f()",
"pos": "testdata/src/implements-methods-json/main.go:17:2"
}
]
}
{
"name": "method (FG) f()",
"pos": "testdata/src/implements-methods-json/main.go:17:2"
}
]
}
-------- @implements *D.g --------
-------- @implements FG.f --------
{
"mode": "implements",
"implements": {
"type": {
"type": {
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
},
{
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (*D).g() []int",
}
],
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"method": {
"name": "func (FG).f()",
"pos": "testdata/src/implements-methods-json/main.go:17:2"
},
"to_method": [
{
"name": "method (*D) f()",
"pos": "testdata/src/implements-methods-json/main.go:25:12"
}
],
"from_method": [
{
"name": "method (F) f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
}
]
}
-------- @implements FG.g --------
{
"type": {
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "pointer"
}
],
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"method": {
"name": "func (FG).g() []int",
"pos": "testdata/src/implements-methods-json/main.go:18:2"
},
"to_method": [
{
"name": "method (*D) g() []int",
"pos": "testdata/src/implements-methods-json/main.go:27:13"
}
],
"from_method": [
{
"name": "",
"pos": ""
}
]
}
-------- @implements *C.f --------
{
"type": {
"name": "*implements-methods-json.C",
"pos": "testdata/src/implements-methods-json/main.go:21:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"method": {
"name": "func (*C).f()",
"pos": "testdata/src/implements-methods-json/main.go:24:13"
},
"from_method": [
{
"name": "method (F) f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
}
]
}
-------- @implements D.f --------
{
"type": {
"name": "implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "struct"
},
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
}
],
"fromptr": [
{
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (D).f()",
"pos": "testdata/src/implements-methods-json/main.go:25:12"
},
"from_method": [
{
"name": "method (F) f()",
"pos": "testdata/src/implements-methods-json/main.go:13:2"
}
],
"fromptr_method": [
{
"name": "method (FG) f()",
"pos": "testdata/src/implements-methods-json/main.go:17:2"
}
]
}
-------- @implements *D.g --------
{
"type": {
"name": "*implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "pointer"
},
"from": [
{
"name": "implements-methods-json.F",
"pos": "testdata/src/implements-methods-json/main.go:12:6",
"kind": "interface"
},
"from_method": [
{
"name": "",
"pos": ""
},
{
"name": "method (FG) g() []int",
"pos": "testdata/src/implements-methods-json/main.go:18:2"
}
]
}
{
"name": "implements-methods-json.FG",
"pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (*D).g() []int",
"pos": "testdata/src/implements-methods-json/main.go:27:13"
},
"from_method": [
{
"name": "",
"pos": ""
},
{
"name": "method (FG) g() []int",
"pos": "testdata/src/implements-methods-json/main.go:18:2"
}
]
}
-------- @implements Len --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-methods-json.sorter",
"pos": "testdata/src/implements-methods-json/main.go:29:6",
"kind": "slice"
},
"from": [
{
"name": "lib.Sorter",
"pos": "testdata/src/lib/lib.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (sorter).Len() int",
"pos": "testdata/src/implements-methods-json/main.go:31:15"
},
"from_method": [
{
"name": "method (lib.Sorter) Len() int",
"pos": "testdata/src/lib/lib.go:17:2"
}
]
}
"type": {
"name": "implements-methods-json.sorter",
"pos": "testdata/src/implements-methods-json/main.go:29:6",
"kind": "slice"
},
"from": [
{
"name": "lib.Sorter",
"pos": "testdata/src/lib/lib.go:16:6",
"kind": "interface"
}
],
"method": {
"name": "func (sorter).Len() int",
"pos": "testdata/src/implements-methods-json/main.go:31:15"
},
"from_method": [
{
"name": "method (lib.Sorter) Len() int",
"pos": "testdata/src/lib/lib.go:17:2"
}
]
}
-------- @implements I.Method --------
{
"mode": "implements",
"implements": {
"type": {
"name": "implements-methods-json.I",
"pos": "testdata/src/implements-methods-json/main.go:35:6",
"kind": "interface"
},
"to": [
{
"name": "lib.Type",
"pos": "testdata/src/lib/lib.go:3:6",
"kind": "basic"
}
],
"method": {
"name": "func (I).Method(*int) *int",
"pos": "testdata/src/implements-methods-json/main.go:36:2"
},
"to_method": [
{
"name": "method (lib.Type) Method(x *int) *int",
"pos": "testdata/src/lib/lib.go:5:13"
}
]
}
"type": {
"name": "implements-methods-json.I",
"pos": "testdata/src/implements-methods-json/main.go:35:6",
"kind": "interface"
},
"to": [
{
"name": "lib.Type",
"pos": "testdata/src/lib/lib.go:3:6",
"kind": "basic"
}
],
"method": {
"name": "func (I).Method(*int) *int",
"pos": "testdata/src/implements-methods-json/main.go:36:2"
},
"to_method": [
{
"name": "method (lib.Type) Method(x *int) *int",
"pos": "testdata/src/lib/lib.go:5:13"
}
]
}

View File

@ -1,15 +1,12 @@
-------- @peers peer-recv-chA --------
{
"mode": "peers",
"peers": {
"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"
]
}
"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"
]
}

View File

@ -1,35 +1,29 @@
-------- @pointsto val-p --------
{
"mode": "pointsto",
"pointsto": [
{
"type": "*int",
"labels": [
{
"pos": "testdata/src/pointsto-json/main.go:8:6",
"desc": "s.x[*]"
}
]
}
]
}
[
{
"type": "*int",
"labels": [
{
"pos": "testdata/src/pointsto-json/main.go:8:6",
"desc": "s.x[*]"
}
]
}
]
-------- @pointsto val-i --------
{
"mode": "pointsto",
"pointsto": [
{
"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"
}
]
}
[
{
"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"
}
]

View File

@ -1,64 +1,184 @@
-------- @referrers ref-package --------
{
"mode": "referrers",
"referrers": {
"desc": "package lib",
"refs": [
"testdata/src/describe/main.go:91:8",
"testdata/src/imports/main.go:18:12",
"testdata/src/imports/main.go:19:2",
"testdata/src/imports/main.go:20:2",
"testdata/src/imports/main.go:21:8",
"testdata/src/imports/main.go:26:8",
"testdata/src/referrers-json/main.go:14:8",
"testdata/src/referrers-json/main.go:14:19",
"testdata/src/referrers/ext_test.go:10:7",
"testdata/src/referrers/int_test.go:7:7",
"testdata/src/referrers/main.go:16:8",
"testdata/src/referrers/main.go:16:19"
]
}
"desc": "package lib"
}
{
"package": "describe",
"refs": [
{
"pos": "testdata/src/describe/main.go:91:8",
"text": "\tvar _ lib.Outer // @describe lib-outer \"Outer\""
}
]
}
{
"package": "imports",
"refs": [
{
"pos": "testdata/src/imports/main.go:18:12",
"text": "\tconst c = lib.Const // @describe ref-const \"Const\""
},
{
"pos": "testdata/src/imports/main.go:19:2",
"text": "\tlib.Func() // @describe ref-func \"Func\""
},
{
"pos": "testdata/src/imports/main.go:20:2",
"text": "\tlib.Var++ // @describe ref-var \"Var\""
},
{
"pos": "testdata/src/imports/main.go:21:8",
"text": "\tvar t lib.Type // @describe ref-type \"Type\""
},
{
"pos": "testdata/src/imports/main.go:26:8",
"text": "\tvar _ lib.Type // @describe ref-pkg \"lib\""
}
]
}
{
"package": "referrers",
"refs": [
{
"pos": "testdata/src/referrers/int_test.go:7:7",
"text": "\t_ = (lib.Type).Method // ref from internal test package"
}
]
}
{
"package": "referrers",
"refs": [
{
"pos": "testdata/src/referrers/main.go:16:8",
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
},
{
"pos": "testdata/src/referrers/main.go:16:19",
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
}
]
}
{
"package": "referrers-json",
"refs": [
{
"pos": "testdata/src/referrers-json/main.go:14:8",
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
},
{
"pos": "testdata/src/referrers-json/main.go:14:19",
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
}
]
}
{
"package": "referrers_test",
"refs": [
{
"pos": "testdata/src/referrers/ext_test.go:10:7",
"text": "\t_ = (lib.Type).Method // ref from external test package"
}
]
}
-------- @referrers ref-method --------
{
"mode": "referrers",
"referrers": {
"objpos": "testdata/src/lib/lib.go:5:13",
"desc": "func (lib.Type).Method(x *int) *int",
"refs": [
"testdata/src/imports/main.go:22:9",
"testdata/src/referrers-json/main.go:15:8",
"testdata/src/referrers-json/main.go:16:8",
"testdata/src/referrers/ext_test.go:10:17",
"testdata/src/referrers/int_test.go:7:17",
"testdata/src/referrers/main.go:17:8",
"testdata/src/referrers/main.go:18:8"
]
}
"objpos": "testdata/src/lib/lib.go:5:13",
"desc": "func (lib.Type).Method(x *int) *int"
}
{
"package": "imports",
"refs": [
{
"pos": "testdata/src/imports/main.go:22:9",
"text": "\tp := t.Method(\u0026a) // @describe ref-method \"Method\""
}
]
}
{
"package": "referrers",
"refs": [
{
"pos": "testdata/src/referrers/int_test.go:7:17",
"text": "\t_ = (lib.Type).Method // ref from internal test package"
}
]
}
{
"package": "referrers",
"refs": [
{
"pos": "testdata/src/referrers/main.go:17:8",
"text": "\t_ = v.Method // @referrers ref-method \"Method\""
},
{
"pos": "testdata/src/referrers/main.go:18:8",
"text": "\t_ = v.Method"
}
]
}
{
"package": "referrers-json",
"refs": [
{
"pos": "testdata/src/referrers-json/main.go:15:8",
"text": "\t_ = v.Method // @referrers ref-method \"Method\""
},
{
"pos": "testdata/src/referrers-json/main.go:16:8",
"text": "\t_ = v.Method"
}
]
}
{
"package": "referrers_test",
"refs": [
{
"pos": "testdata/src/referrers/ext_test.go:10:17",
"text": "\t_ = (lib.Type).Method // ref from external test package"
}
]
}
-------- @referrers ref-local --------
{
"mode": "referrers",
"referrers": {
"objpos": "testdata/src/referrers-json/main.go:14:6",
"desc": "var v lib.Type",
"refs": [
"testdata/src/referrers-json/main.go:15:6",
"testdata/src/referrers-json/main.go:16:6",
"testdata/src/referrers-json/main.go:17:2",
"testdata/src/referrers-json/main.go:18:2"
]
}
"objpos": "testdata/src/referrers-json/main.go:14:6",
"desc": "var v lib.Type"
}
{
"package": "referrers-json",
"refs": [
{
"pos": "testdata/src/referrers-json/main.go:15:6",
"text": "\t_ = v.Method // @referrers ref-method \"Method\""
},
{
"pos": "testdata/src/referrers-json/main.go:16:6",
"text": "\t_ = v.Method"
},
{
"pos": "testdata/src/referrers-json/main.go:17:2",
"text": "\tv++ //@referrers ref-local \"v\""
},
{
"pos": "testdata/src/referrers-json/main.go:18:2",
"text": "\tv++"
}
]
}
-------- @referrers ref-field --------
{
"mode": "referrers",
"referrers": {
"objpos": "testdata/src/referrers-json/main.go:10:2",
"desc": "field f int",
"refs": [
"testdata/src/referrers-json/main.go:20:10",
"testdata/src/referrers-json/main.go:23:5"
]
}
"objpos": "testdata/src/referrers-json/main.go:10:2",
"desc": "field f int"
}
{
"package": "referrers-json",
"refs": [
{
"pos": "testdata/src/referrers-json/main.go:20:10",
"text": "\t_ = s{}.f // @referrers ref-field \"f\""
},
{
"pos": "testdata/src/referrers-json/main.go:23:5",
"text": "\ts2.f = 1"
}
]
}

View File

@ -1,50 +1,50 @@
-------- @referrers package-decl --------
1 references to package main ("referrers")
references to package main ("referrers")
var _ renamed.T
-------- @referrers type --------
2 references to type s struct{f int}
references to type s struct{f int}
_ = s{}.f // @referrers ref-field "f"
var s2 s
-------- @referrers ref-package --------
12 references to package lib
references to package lib
var v lib.Type = lib.Const // @referrers ref-package "lib"
var v lib.Type = lib.Const // @referrers ref-package "lib"
var v lib.Type = lib.Const // @referrers ref-package "lib"
var v lib.Type = lib.Const // @referrers ref-package "lib"
var _ lib.Outer // @describe lib-outer "Outer"
const c = lib.Const // @describe ref-const "Const"
lib.Func() // @describe ref-func "Func"
lib.Var++ // @describe ref-var "Var"
var t lib.Type // @describe ref-type "Type"
var _ lib.Type // @describe ref-pkg "lib"
var v lib.Type = lib.Const // @referrers ref-package "lib"
var v lib.Type = lib.Const // @referrers ref-package "lib"
_ = (lib.Type).Method // ref from external test package
_ = (lib.Type).Method // ref from internal test package
var v lib.Type = lib.Const // @referrers ref-package "lib"
var v lib.Type = lib.Const // @referrers ref-package "lib"
_ = (lib.Type).Method // ref from external test package
-------- @referrers ref-method --------
7 references to func (Type).Method(x *int) *int
references to func (Type).Method(x *int) *int
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
p := t.Method(&a) // @describe ref-method "Method"
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
_ = (lib.Type).Method // ref from external test package
_ = (lib.Type).Method // ref from internal test package
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
_ = (lib.Type).Method // ref from external test package
-------- @referrers ref-local --------
4 references to var v lib.Type
references to var v lib.Type
_ = v.Method // @referrers ref-method "Method"
_ = v.Method
v++ //@referrers ref-local "v"
v++
-------- @referrers ref-field --------
2 references to field f int
references to field f int
_ = s{}.f // @referrers ref-field "f"
s2.f = 1
-------- @referrers ref-type-U --------
2 references to type U int
references to type U int
open testdata/src/referrers/nosuchfile.y: no such file or directory (+ 1 more refs in this file)

View File

@ -1,51 +1,48 @@
-------- @what call --------
{
"mode": "what",
"what": {
"enclosing": [
{
"desc": "identifier",
"start": 175,
"end": 176
},
{
"desc": "function call (or conversion)",
"start": 175,
"end": 178
},
{
"desc": "expression statement",
"start": 175,
"end": 178
},
{
"desc": "block",
"start": 172,
"end": 198
},
{
"desc": "function declaration",
"start": 160,
"end": 198
},
{
"desc": "source file",
"start": 0,
"end": 198
}
],
"modes": [
"callees",
"callers",
"callstack",
"definition",
"describe",
"freevars",
"implements",
"pointsto",
"referrers"
],
"srcdir": "testdata/src",
"importpath": "what-json"
}
"enclosing": [
{
"desc": "identifier",
"start": 175,
"end": 176
},
{
"desc": "function call (or conversion)",
"start": 175,
"end": 178
},
{
"desc": "expression statement",
"start": 175,
"end": 178
},
{
"desc": "block",
"start": 172,
"end": 198
},
{
"desc": "function declaration",
"start": 160,
"end": 198
},
{
"desc": "source file",
"start": 0,
"end": 198
}
],
"modes": [
"callees",
"callers",
"callstack",
"definition",
"describe",
"freevars",
"implements",
"pointsto",
"referrers"
],
"srcdir": "testdata/src",
"importpath": "what-json"
}

View File

@ -29,10 +29,9 @@ func what(q *Query) error {
if err != nil {
return err
}
q.Fset = qpos.fset
// (ignore errors)
srcdir, importPath, _ := guessImportPath(q.Fset.File(qpos.start).Name(), q.Build)
srcdir, importPath, _ := guessImportPath(qpos.fset.File(qpos.start).Name(), q.Build)
// Determine which query modes are applicable to the selection.
enable := map[string]bool{
@ -129,14 +128,14 @@ func what(q *Query) error {
})
}
q.result = &whatResult{
q.Output(qpos.fset, &whatResult{
path: qpos.path,
srcdir: srcdir,
importPath: importPath,
modes: modes,
object: object,
sameids: sameids,
}
})
return nil
}
@ -211,7 +210,7 @@ type whatResult struct {
sameids []token.Pos
}
func (r *whatResult) display(printf printfFunc) {
func (r *whatResult) PrintPlain(printf printfFunc) {
for _, n := range r.path {
printf(n, "%s", astutil.NodeDescription(n))
}
@ -223,7 +222,7 @@ func (r *whatResult) display(printf printfFunc) {
}
}
func (r *whatResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *whatResult) JSON(fset *token.FileSet) []byte {
var enclosing []serial.SyntaxNode
for _, n := range r.path {
enclosing = append(enclosing, serial.SyntaxNode{
@ -238,12 +237,12 @@ func (r *whatResult) toSerial(res *serial.Result, fset *token.FileSet) {
sameids = append(sameids, fset.Position(pos).String())
}
res.What = &serial.What{
return toJSON(&serial.What{
Modes: r.modes,
SrcDir: r.srcdir,
ImportPath: r.importPath,
Enclosing: enclosing,
Object: r.object,
SameIDs: sameids,
}
})
}

View File

@ -41,7 +41,6 @@ func whicherrs(q *Query) error {
if err != nil {
return err
}
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
if err != nil {
@ -209,7 +208,7 @@ func whicherrs(q *Query) error {
sort.Sort(membersByPosAndString(res.consts))
sort.Sort(sorterrorType(res.types))
q.result = res
q.Output(lprog.Fset, res)
return nil
}
@ -290,7 +289,7 @@ type whicherrsResult struct {
types []*errorType
}
func (r *whicherrsResult) display(printf printfFunc) {
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 {
@ -311,7 +310,7 @@ func (r *whicherrsResult) display(printf printfFunc) {
}
}
func (r *whicherrsResult) toSerial(res *serial.Result, fset *token.FileSet) {
func (r *whicherrsResult) JSON(fset *token.FileSet) []byte {
we := &serial.WhichErrs{}
we.ErrPos = fset.Position(r.errpos).String()
for _, g := range r.globals {
@ -326,5 +325,5 @@ func (r *whicherrsResult) toSerial(res *serial.Result, fset *token.FileSet) {
et.Position = fset.Position(t.obj.Pos()).String()
we.Types = append(we.Types, et)
}
res.WhichErrs = we
return toJSON(we)
}