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 { if err != nil {
return err return err
} }
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
if err != nil { 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()) return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
case *types.Func: case *types.Func:
// This is a static function call // This is a static function call
q.result = &calleesTypesResult{ q.Output(lprog.Fset, &calleesTypesResult{
site: e, site: e,
callee: obj, callee: obj,
} })
return nil return nil
} }
case *ast.SelectorExpr: case *ast.SelectorExpr:
@ -83,10 +82,10 @@ func callees(q *Query) error {
// or to top level function. // or to top level function.
callee := qpos.info.Uses[funexpr.Sel] callee := qpos.info.Uses[funexpr.Sel]
if obj, ok := callee.(*types.Func); ok { if obj, ok := callee.(*types.Func); ok {
q.result = &calleesTypesResult{ q.Output(lprog.Fset, &calleesTypesResult{
site: e, site: e,
callee: obj, callee: obj,
} })
return nil return nil
} }
} else if sel.Kind() == types.MethodVal { } else if sel.Kind() == types.MethodVal {
@ -98,10 +97,10 @@ func callees(q *Query) error {
recvtype := method.Type().(*types.Signature).Recv().Type() recvtype := method.Type().(*types.Signature).Recv().Type()
if !types.IsInterface(recvtype) { if !types.IsInterface(recvtype) {
// static method call // static method call
q.result = &calleesTypesResult{ q.Output(lprog.Fset, &calleesTypesResult{
site: e, site: e,
callee: method, callee: method,
} })
return nil return nil
} }
} }
@ -139,10 +138,10 @@ func callees(q *Query) error {
return err return err
} }
q.result = &calleesSSAResult{ q.Output(lprog.Fset, &calleesSSAResult{
site: site, site: site,
funcs: funcs, funcs: funcs,
} })
return nil return nil
} }
@ -204,7 +203,7 @@ type calleesTypesResult struct {
callee *types.Func callee *types.Func
} }
func (r *calleesSSAResult) display(printf printfFunc) { func (r *calleesSSAResult) PrintPlain(printf printfFunc) {
if len(r.funcs) == 0 { if len(r.funcs) == 0 {
// dynamic call on a provably nil func/interface // dynamic call on a provably nil func/interface
printf(r.site, "%s on nil value", r.site.Common().Description()) 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{ j := &serial.Callees{
Pos: fset.Position(r.site.Pos()).String(), Pos: fset.Position(r.site.Pos()).String(),
Desc: r.site.Common().Description(), Desc: r.site.Common().Description(),
} }
for _, callee := range r.funcs { for _, callee := range r.funcs {
j.Callees = append(j.Callees, &serial.CalleesItem{ j.Callees = append(j.Callees, &serial.Callee{
Name: callee.String(), Name: callee.String(),
Pos: fset.Position(callee.Pos()).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.site, "this static function call dispatches to:")
printf(r.callee, "\t%s", r.callee.FullName()) 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{ j := &serial.Callees{
Pos: fset.Position(r.site.Pos()).String(), Pos: fset.Position(r.site.Pos()).String(),
Desc: "static function call", Desc: "static function call",
} }
j.Callees = []*serial.CalleesItem{ j.Callees = []*serial.Callee{
&serial.CalleesItem{ &serial.Callee{
Name: r.callee.FullName(), Name: r.callee.FullName(),
Pos: fset.Position(r.callee.Pos()).String(), 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. // 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 { if err != nil {
return err return err
} }
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false) qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil { if err != nil {
@ -77,11 +76,11 @@ func callers(q *Query) error {
// TODO(adonovan): sort + dedup calls to ensure test determinism. // TODO(adonovan): sort + dedup calls to ensure test determinism.
q.result = &callersResult{ q.Output(lprog.Fset, &callersResult{
target: target, target: target,
callgraph: cg, callgraph: cg,
edges: edges, edges: edges,
} })
return nil return nil
} }
@ -167,7 +166,7 @@ type callersResult struct {
edges []*callgraph.Edge edges []*callgraph.Edge
} }
func (r *callersResult) display(printf printfFunc) { func (r *callersResult) PrintPlain(printf printfFunc) {
root := r.callgraph.Root root := r.callgraph.Root
if r.edges == nil { if r.edges == nil {
printf(r.target, "%s is not reachable in this program.", r.target) 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 var callers []serial.Caller
for _, edge := range r.edges { for _, edge := range r.edges {
callers = append(callers, serial.Caller{ callers = append(callers, serial.Caller{
@ -192,5 +191,5 @@ func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) {
Desc: edge.Description(), Desc: edge.Description(),
}) })
} }
res.Callers = callers return toJSON(callers)
} }

View File

@ -96,12 +96,11 @@ func callstack(q *Query) error {
} }
} }
q.Fset = fset q.Output(fset, &callstackResult{
q.result = &callstackResult{
qpos: qpos, qpos: qpos,
target: target, target: target,
callpath: callpath, callpath: callpath,
} })
return nil return nil
} }
@ -111,7 +110,7 @@ type callstackResult struct {
callpath []*callgraph.Edge callpath []*callgraph.Edge
} }
func (r *callstackResult) display(printf printfFunc) { func (r *callstackResult) PrintPlain(printf printfFunc) {
if r.callpath != nil { if r.callpath != nil {
printf(r.qpos, "Found a call path from root to %s", r.target) printf(r.qpos, "Found a call path from root to %s", r.target)
printf(r.target, "%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 var callers []serial.Caller
for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first) for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first)
edge := r.callpath[i] edge := r.callpath[i]
@ -134,9 +133,9 @@ func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) {
Desc: edge.Description(), Desc: edge.Description(),
}) })
} }
res.Callstack = &serial.CallStack{ return toJSON(&serial.CallStack{
Pos: fset.Position(r.target.Pos()).String(), Pos: fset.Position(r.target.Pos()).String(),
Target: r.target.String(), Target: r.target.String(),
Callers: callers, Callers: callers,
} })
} }

View File

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

View File

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

View File

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

View File

@ -301,11 +301,9 @@ function containing the current point."
(defun go-guru-definition () (defun go-guru-definition ()
"Jump to the definition of the selected identifier." "Jump to the definition of the selected identifier."
(interactive) (interactive)
;; TODO(adonovan): use -format=sexpr when available to avoid a (let* ((res (with-current-buffer (go-guru--exec "definition" nil '("-json"))
;; dependency and to simplify parsing.
(let* ((res (with-current-buffer (go-guru--exec "definition" nil '("-format=json"))
(goto-char (point-min)) (goto-char (point-min))
(cdr (car (json-read))))) (json-read)))
(desc (cdr (assoc 'desc res)))) (desc (cdr (assoc 'desc res))))
(push-mark) (push-mark)
(ring-insert find-tag-marker-ring (point-marker)) (ring-insert find-tag-marker-ring (point-marker))
@ -360,11 +358,10 @@ expression (of type 'error') may refer."
(defun go-guru-what () (defun go-guru-what ()
"Run a 'what' query and return the parsed JSON response as an "Run a 'what' query and return the parsed JSON response as an
associative list." association list."
(let ((res (with-current-buffer (go-guru--exec "what" nil '("-format=json") t) (with-current-buffer (go-guru--exec "what" nil '("-json") t)
(goto-char (point-min)) (goto-char (point-min))
(cdr (car (json-read)))))) (json-read)))
res))
(defun go-guru--hl-symbols (posn face id) (defun go-guru--hl-symbols (posn face id)
"Highlight the symbols at the positions POSN by creating "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. // (&T{}, var t T, new(T), new(struct{array [3]T}), etc.
import ( import (
"encoding/json"
"fmt" "fmt"
"go/ast" "go/ast"
"go/build" "go/build"
@ -18,9 +19,9 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"io" "io"
"log"
"path/filepath" "path/filepath"
"golang.org/x/tools/cmd/guru/serial"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
@ -30,10 +31,15 @@ import (
type printfFunc func(pos interface{}, format string, args ...interface{}) type printfFunc func(pos interface{}, format string, args ...interface{})
// queryResult is the interface of each query-specific result type. // A QueryResult is an item of output. Each query produces a stream of
type queryResult interface { // query results, calling Query.Output for each one.
toSerial(res *serial.Result, fset *token.FileSet) type QueryResult interface {
display(printf printfFunc) // 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: // 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. // A Query specifies a single guru query.
type Query struct { type Query struct {
Mode string // query mode ("callers", etc)
Pos string // query position Pos string // query position
Build *build.Context // package loading configuration Build *build.Context // package loading configuration
@ -74,32 +79,13 @@ type Query struct {
PTALog io.Writer // (optional) pointer-analysis log file PTALog io.Writer // (optional) pointer-analysis log file
Reflection bool // model reflection soundly (currently slow). Reflection bool // model reflection soundly (currently slow).
// Populated during Run() // result-printing function
Fset *token.FileSet Output func(*token.FileSet, QueryResult)
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)
} }
// Run runs an guru query and populates its Fset and Result. // Run runs an guru query and populates its Fset and Result.
func Run(q *Query) error { func Run(mode string, q *Query) error {
switch q.Mode { switch mode {
case "callees": case "callees":
return callees(q) return callees(q)
case "callers": case "callers":
@ -125,7 +111,7 @@ func Run(q *Query) error {
case "what": case "what":
return what(q) return what(q)
default: 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' // The output format is is compatible with the 'gnu'
// compilation-error-regexp in Emacs' compilation mode. // 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{}) { func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
var start, end token.Pos 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...) fmt.Fprintf(w, format, args...)
io.WriteString(w, "\n") 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 ( import (
"bytes" "bytes"
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"go/build" "go/build"
@ -41,8 +40,10 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync"
"testing" "testing"
guru "golang.org/x/tools/cmd/guru" guru "golang.org/x/tools/cmd/guru"
@ -150,51 +151,53 @@ func parseQueries(t *testing.T, filename string) []*query {
return queries 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 // doQuery poses query q to the guru and writes its response and
// error (if any) to out. // 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) fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)
var buildContext = build.Default var buildContext = build.Default
buildContext.GOPATH = "testdata" buildContext.GOPATH = "testdata"
pkg := filepath.Dir(strings.TrimPrefix(q.filename, "testdata/src/")) 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{ query := guru.Query{
Mode: q.verb,
Pos: q.queryPos, Pos: q.queryPos,
Build: &buildContext, Build: &buildContext,
Scope: []string{pkg}, Scope: []string{pkg},
Reflection: true, 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) fmt.Fprintf(out, "\nError: %s\n", err)
return return
} }
if useJson { if json {
// JSON output if q.verb == "referrers" {
b, err := json.MarshalIndent(query.Serial(), "", "\t") sort.Strings(jsons[1:]) // for determinism
if err != nil { }
fmt.Fprintf(out, "JSON error: %s\n", err.Error()) for _, json := range jsons {
return fmt.Fprintf(out, "%s\n", json)
} }
out.Write(b)
fmt.Fprintln(out)
} else { } else {
// "plain" (compiler diagnostic format) output io.WriteString(out, "\n")
WriteResult(out, &query)
} }
} }
@ -230,7 +233,7 @@ func TestGuru(t *testing.T) {
"testdata/src/referrers-json/main.go", "testdata/src/referrers-json/main.go",
"testdata/src/what-json/main.go", "testdata/src/what-json/main.go",
} { } {
useJson := strings.Contains(filename, "-json/") json := strings.Contains(filename, "-json/")
queries := parseQueries(t, filename) queries := parseQueries(t, filename)
golden := filename + "lden" golden := filename + "lden"
got := filename + "t" got := filename + "t"
@ -245,7 +248,7 @@ func TestGuru(t *testing.T) {
// Run the guru on each query, redirecting its output // Run the guru on each query, redirecting its output
// and error (if any) to the foo.got file. // and error (if any) to the foo.got file.
for _, q := range queries { for _, q := range queries {
doQuery(gotfh, q, useJson) doQuery(gotfh, q, json)
} }
// Compare foo.got with foo.golden. // Compare foo.got with foo.golden.
@ -277,11 +280,10 @@ func TestIssue14684(t *testing.T) {
var buildContext = build.Default var buildContext = build.Default
buildContext.GOPATH = "testdata" buildContext.GOPATH = "testdata"
query := guru.Query{ query := guru.Query{
Mode: "freevars",
Pos: "testdata/src/README.txt:#1", Pos: "testdata/src/README.txt:#1",
Build: &buildContext, Build: &buildContext,
} }
err := guru.Run(&query) err := guru.Run("freevars", &query)
if err == nil { if err == nil {
t.Fatal("guru query succeeded unexpectedly") t.Fatal("guru query succeeded unexpectedly")
} }

View File

@ -63,7 +63,6 @@ func implements(q *Query) error {
if err != nil { if err != nil {
return err return err
} }
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false) qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil { 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, qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
} })
return nil return nil
} }
@ -201,7 +200,7 @@ type implementsResult struct {
fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any 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" relation := "is implemented by"
meth := func(sel *types.Selection) { 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) { func (r *implementsResult) JSON(fset *token.FileSet) []byte {
res.Implements = &serial.Implements{ 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), T: makeImplementsType(r.t, fset),
AssignableTo: makeImplementsTypes(r.to, fset), AssignableTo: makeImplementsTypes(r.to, fset),
AssignableFrom: makeImplementsTypes(r.from, 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), AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset), AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset), AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
} Method: method,
if r.method != nil { })
res.Implements.Method = &serial.DescribeMethod{
Name: r.qpos.objectString(r.method),
Pos: fset.Position(r.method.Pos()).String(),
}
}
} }
func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType { 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 ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/json"
"encoding/xml"
"flag" "flag"
"fmt" "fmt"
"go/build" "go/build"
"go/token"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -27,6 +26,7 @@ import (
"runtime/pprof" "runtime/pprof"
"strconv" "strconv"
"strings" "strings"
"sync"
"golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/buildutil"
) )
@ -36,7 +36,7 @@ var (
modifiedFlag = flag.Bool("modified", false, "read archive of modified files from standard input") 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") 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`") 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)") reflectFlag = flag.Bool("reflect", false, "analyze reflection soundly (slow)")
cpuprofileFlag = flag.String("cpuprofile", "", "write CPU profile to `file`") 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 foo.go:#123,#128
bar.go:#123 bar.go:#123
The -format flag controls the output format: The -json flag causes guru to emit output in JSON format;
plain an editor-friendly format in which every line of output golang.org/x/tools/cmd/guru/serial defines its schema.
is of the form "pos: text", where pos is "-" if unknown. Otherwise, the output is in an editor-friendly format in which
json structured data in JSON syntax. every line has the form "pos: text", where pos is "-" if unknown.
xml structured data in XML syntax.
The -modified flag causes guru to read an archive from standard input. The -modified flag causes guru to read an archive from standard input.
Files in this archive will be used in preference to those in Files in this archive will be used in preference to those in
@ -163,14 +162,6 @@ func main() {
defer pprof.StopCPUProfile() 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 ctxt := &build.Default
// If there were modified files, // 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. // Ask the guru.
query := Query{ query := Query{
Mode: mode,
Pos: posn, Pos: posn,
Build: ctxt, Build: ctxt,
Scope: strings.Split(*scopeFlag, ","), Scope: strings.Split(*scopeFlag, ","),
PTALog: ptalog, PTALog: ptalog,
Reflection: *reflectFlag, Reflection: *reflectFlag,
Output: output,
} }
if err := Run(&query); err != nil { if err := Run(mode, &query); err != nil {
log.Fatal(err) 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) { func parseArchive(archive io.Reader) (map[string][]byte, error) {

View File

@ -35,7 +35,6 @@ func peers(q *Query) error {
if err != nil { if err != nil {
return err return err
} }
q.Fset = lprog.Fset
qpos, err := parseQueryPos(lprog, q.Pos, false) qpos, err := parseQueryPos(lprog, q.Pos, false)
if err != nil { if err != nil {
@ -127,14 +126,14 @@ func peers(q *Query) error {
sort.Sort(byPos(receives)) sort.Sort(byPos(receives))
sort.Sort(byPos(closes)) sort.Sort(byPos(closes))
q.result = &peersResult{ q.Output(lprog.Fset, &peersResult{
queryPos: opPos, queryPos: opPos,
queryType: queryType, queryType: queryType,
makes: makes, makes: makes,
sends: sends, sends: sends,
receives: receives, receives: receives,
closes: closes, closes: closes,
} })
return nil return nil
} }
@ -195,13 +194,14 @@ func chanOps(instr ssa.Instruction) []chanOp {
return ops return ops
} }
// TODO(adonovan): show the line of text for each pos, like "referrers" does.
type peersResult struct { type peersResult struct {
queryPos token.Pos // of queried channel op queryPos token.Pos // of queried channel op
queryType types.Type // type of queried channel queryType types.Type // type of queried channel
makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs 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 { if len(r.makes) == 0 {
printf(r.queryPos, "This channel can't point to anything.") printf(r.queryPos, "This channel can't point to anything.")
return 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{ peers := &serial.Peers{
Pos: fset.Position(r.queryPos).String(), Pos: fset.Position(r.queryPos).String(),
Type: r.queryType.String(), Type: r.queryType.String(),
@ -238,7 +238,7 @@ func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
for _, clos := range r.closes { for _, clos := range r.closes {
peers.Closes = append(peers.Closes, fset.Position(clos).String()) peers.Closes = append(peers.Closes, fset.Position(clos).String())
} }
res.Peers = peers return toJSON(peers)
} }
// -------- utils -------- // -------- utils --------

View File

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

View File

@ -82,17 +82,13 @@ func referrers(q *Query) error {
return globalReferrers(q, qpos.info.Pkg.Path(), defpkg, objposn, pkglevel) return globalReferrers(q, qpos.info.Pkg.Path(), defpkg, objposn, pkglevel)
} }
// Find uses of obj within the query package. q.Output(fset, &referrersInitialResult{
refs := usesOf(obj, qpos.info)
sort.Sort(byNamePos{fset, refs})
q.Fset = fset
q.result = &referrersResult{
build: q.Build,
fset: fset,
qinfo: qpos.info, qinfo: qpos.info,
obj: obj, obj: obj,
refs: refs, })
}
outputUses(q, fset, usesOf(obj, qpos.info), obj.Pkg())
return nil // success return nil // success
} }
@ -113,8 +109,8 @@ func classify(obj types.Object) (global, pkglevel bool) {
return false, false return false, false
} }
// packageReferrers finds all references to the specified package // packageReferrers reports all references to the specified package
// throughout the workspace and populates q.result. // throughout the workspace.
func packageReferrers(q *Query, path string) error { func packageReferrers(q *Query, path string) error {
// Scan the workspace and build the import graph. // Scan the workspace and build the import graph.
// Ignore broken packages. // Ignore broken packages.
@ -134,34 +130,86 @@ func packageReferrers(q *Query, path string) error {
}, },
} }
allowErrors(&lconf) allowErrors(&lconf)
// The importgraph doesn't treat external test packages
// as separate nodes, so we must use ImportWithTests.
for path := range users { for path := range users {
lconf.ImportWithTests(path) lconf.ImportWithTests(path)
} }
lprog, err := lconf.Load()
if err != nil { // Subtle! AfterTypeCheck needs no mutex for qpkg because the
return err // 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. lconf.Load() // ignore error
//
// TODO(adonovan): perhaps more useful would be to show imports if qpkg == nil {
// of the package instead of qualified identifiers. log.Fatalf("query package %q not found during reloading", path)
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,
} }
return nil 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, // object at the specified source position. Its defining package is defpkg,
// and the query package is qpkg. isPkgLevel indicates whether the object // and the query package is qpkg. isPkgLevel indicates whether the object
// is defined at package-level. // 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. // so we can't use them here.
// TODO(adonovan): smooth things out once the other changes have landed. // 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 ( var (
mu sync.Mutex mu sync.Mutex
qobj types.Object 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 // just after it has been type-checked. The loader calls
// AfterTypeCheck (concurrently), providing us with a stream of // AfterTypeCheck (concurrently), providing us with a stream of
// packages. // packages.
ch := make(chan []*ast.Ident)
lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) { 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 // Only inspect packages that depend on the declaring package
// (and thus were type-checked). // (and thus were type-checked).
if lconf.TypeCheckFuncBodies(info.Pkg.Path()) { 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", log.Fatalf("object at %s not found in package %s",
objposn, defpkg) objposn, defpkg)
} }
// Object found.
qinfo = info qinfo = info
q.Output(fset, &referrersInitialResult{
qinfo: qinfo,
obj: qobj,
})
} }
obj := qobj obj := qobj
mu.Unlock() mu.Unlock()
// Look for references to the query object. // Look for references to the query object.
if obj != nil { 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. clearInfoFields(info) // save memory
// (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
} }
go func() { lconf.Load() // ignore error
lconf.Load() // ignore error
close(ch)
}()
var refs []*ast.Ident
for ids := range ch {
refs = append(refs, ids...)
}
sort.Sort(byNamePos{fset, refs})
if qobj == nil { if qobj == nil {
log.Fatal("query object not found during reloading") 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 return nil // success
} }
@ -318,20 +338,6 @@ func findObject(fset *token.FileSet, info *types.Info, objposn token.Position) t
return nil 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 // same reports whether x and y are identical, or both are PkgNames
// that import the same Package. // that import the same Package.
// //
@ -347,6 +353,26 @@ func sameObj(x, y types.Object) bool {
return false 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 -------- // -------- utils --------
// An deterministic ordering for token.Pos that doesn't // 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) 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 build *build.Context
fset *token.FileSet fset *token.FileSet
qinfo *loader.PackageInfo
qpos *queryPos
obj types.Object // object it denotes
refs []*ast.Ident // set of all other references to it refs []*ast.Ident // set of all other references to it
} }
func (r *referrersResult) display(printf printfFunc) { // forEachRef calls f(id, text) for id in r.refs, in order.
printf(r.obj, "%d references to %s", // Text is the text of the line on which id appears.
len(r.refs), types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg))) func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) {
// Show referring lines, like grep. // Show referring lines, like grep.
type fileinfo struct { type fileinfo struct {
refs []*ast.Ident refs []*ast.Ident
@ -432,13 +478,13 @@ func (r *referrersResult) display(printf printfFunc) {
if more := len(fi.refs) - 1; more > 0 { if more := len(fi.refs) - 1; more > 0 {
suffix = fmt.Sprintf(" (+ %d more refs in this file)", more) 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 continue
} }
lines := bytes.Split(v.([]byte), []byte("\n")) lines := bytes.Split(v.([]byte), []byte("\n"))
for i, ref := range fi.refs { 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 return buf.Bytes(), nil
} }
func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) { func (r *referrersPackageResult) PrintPlain(printf printfFunc) {
referrers := &serial.Referrers{ r.foreachRef(func(id *ast.Ident, text string) {
Desc: r.obj.String(), printf(id, "%s", text)
} })
if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos() }
referrers.ObjPos = fset.Position(pos).String()
} func (r *referrersPackageResult) JSON(fset *token.FileSet) []byte {
for _, ref := range r.refs { refs := serial.ReferrersPackage{Package: r.pkg.Path()}
referrers.Refs = append(referrers.Refs, fset.Position(ref.NamePos).String()) r.foreachRef(func(id *ast.Ident, text string) {
} refs.Refs = append(refs.Refs, serial.Ref{
res.Referrers = referrers 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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package serial defines the guru's schema for structured data // Package serial defines the guru's schema for -json output.
// 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.
// //
// TODO(adonovan): consider richer encodings of types, functions, // The output of a guru query is a stream of one or more JSON objects.
// methods, etc. // 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. // A Peers is the result of a 'peers' query.
// If Allocs is empty, the selected channel can't point to anything. // 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 Closes []string `json:"closes,omitempty"` // locations of aliased close(ch) ops
} }
// A Referrers is the result of a 'referrers' query. // A "referrers" query emits a ReferrersInitial object followed by zero or
type Referrers struct { // more ReferrersPackage objects, one per package that contains a reference.
ObjPos string `json:"objpos,omitempty"` // location of the definition type (
Desc string `json:"desc"` // description of the denoted object ReferrersInitial struct {
Refs []string `json:"refs,omitempty"` // locations of all references 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. // A Definition is the result of a 'definition' query.
type Definition struct { type Definition struct {
@ -38,20 +61,21 @@ type Definition struct {
Desc string `json:"desc"` // description of the denoted object 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. // A Callees is the result of a 'callees' query.
// //
// Callees is nonempty unless the call was a dynamic call on a // Callees is nonempty unless the call was a dynamic call on a
// provably nil func or interface value. // provably nil func or interface value.
type Callees struct { type (
Pos string `json:"pos"` // location of selected call site Callees struct {
Desc string `json:"desc"` // description of call site Pos string `json:"pos"` // location of selected call site
Callees []*CalleesItem `json:"callees,omitempty"` // set of possible call targets 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. // A Caller is one element of the slice returned by a 'callers' query.
// (Callstack also contains a similar slice.) // (Callstack also contains a similar slice.)
@ -233,27 +257,3 @@ type WhichErrsType struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Position string `json:"position,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 -------- -------- @callees @callees-f --------
{ {
"mode": "callees", "pos": "testdata/src/calls-json/main.go:8:3",
"callees": { "desc": "dynamic function call",
"pos": "testdata/src/calls-json/main.go:8:3", "callees": [
"desc": "dynamic function call", {
"callees": [ "name": "calls-json.main$1",
{ "pos": "testdata/src/calls-json/main.go:12:7"
"name": "calls-json.main$1", }
"pos": "testdata/src/calls-json/main.go:12:7" ]
}
]
}
} }
-------- @callstack callstack-main.anon -------- -------- @callstack callstack-main.anon --------
{ {
"mode": "callstack", "pos": "testdata/src/calls-json/main.go:12:7",
"callstack": { "target": "calls-json.main$1",
"pos": "testdata/src/calls-json/main.go:12:7", "callers": [
"target": "calls-json.main$1", {
"callers": [ "pos": "testdata/src/calls-json/main.go:8:3",
{ "desc": "dynamic function call",
"pos": "testdata/src/calls-json/main.go:8:3", "caller": "calls-json.call"
"desc": "dynamic function call", },
"caller": "calls-json.call" {
}, "pos": "testdata/src/calls-json/main.go:12:6",
{ "desc": "static function call",
"pos": "testdata/src/calls-json/main.go:12:6", "caller": "calls-json.main"
"desc": "static function call", }
"caller": "calls-json.main" ]
}
]
}
} }

View File

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

View File

@ -1,159 +1,135 @@
-------- @implements E -------- -------- @implements E --------
{ {
"mode": "implements", "type": {
"implements": { "name": "implements-json.E",
"type": { "pos": "testdata/src/implements-json/main.go:10:6",
"name": "implements-json.E", "kind": "interface"
"pos": "testdata/src/implements-json/main.go:10:6",
"kind": "interface"
}
} }
} }
-------- @implements F -------- -------- @implements F --------
{ {
"mode": "implements", "type": {
"implements": { "name": "implements-json.F",
"type": { "pos": "testdata/src/implements-json/main.go:12:6",
"name": "implements-json.F", "kind": "interface"
"pos": "testdata/src/implements-json/main.go:12:6", },
"kind": "interface" "to": [
}, {
"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": {
"name": "*implements-json.C", "name": "*implements-json.C",
"pos": "testdata/src/implements-json/main.go:21:6", "pos": "testdata/src/implements-json/main.go:21:6",
"kind": "pointer" "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", "name": "implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6", "pos": "testdata/src/implements-json/main.go:22:6",
"kind": "struct" "kind": "struct"
}, },
"from": [ {
{ "name": "implements-json.FG",
"name": "implements-json.F", "pos": "testdata/src/implements-json/main.go:16:6",
"pos": "testdata/src/implements-json/main.go:12:6", "kind": "interface"
"kind": "interface" }
} ]
],
"fromptr": [
{
"name": "implements-json.FG",
"pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
}
]
}
} }
-------- @implements starD -------- -------- @implements FG --------
{ {
"mode": "implements", "type": {
"implements": { "name": "implements-json.FG",
"type": { "pos": "testdata/src/implements-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-json.D", "name": "*implements-json.D",
"pos": "testdata/src/implements-json/main.go:22:6", "pos": "testdata/src/implements-json/main.go:22:6",
"kind": "pointer" "kind": "pointer"
}, }
"from": [ ],
{ "from": [
"name": "implements-json.F", {
"pos": "testdata/src/implements-json/main.go:12:6", "name": "implements-json.F",
"kind": "interface" "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" -------- @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 -------- -------- @implements F.f --------
{ {
"mode": "implements", "type": {
"implements": { "name": "implements-methods-json.F",
"type": { "pos": "testdata/src/implements-methods-json/main.go:12:6",
"name": "implements-methods-json.F", "kind": "interface"
"pos": "testdata/src/implements-methods-json/main.go:12:6", },
"kind": "interface" "to": [
}, {
"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": {
"name": "*implements-methods-json.C", "name": "*implements-methods-json.C",
"pos": "testdata/src/implements-methods-json/main.go:21:6", "pos": "testdata/src/implements-methods-json/main.go:21:6",
"kind": "pointer" "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", "name": "implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6", "pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "struct" "kind": "struct"
}, },
"from": [ {
{ "name": "implements-methods-json.FG",
"name": "implements-methods-json.F", "pos": "testdata/src/implements-methods-json/main.go:16:6",
"pos": "testdata/src/implements-methods-json/main.go:12:6", "kind": "interface"
"kind": "interface" }
} ],
], "method": {
"fromptr": [ "name": "func (F).f()",
{ "pos": "testdata/src/implements-methods-json/main.go:13:2"
"name": "implements-methods-json.FG", },
"pos": "testdata/src/implements-methods-json/main.go:16:6", "to_method": [
"kind": "interface" {
} "name": "method (*C) f()",
], "pos": "testdata/src/implements-methods-json/main.go:24:13"
"method": { },
"name": "func (D).f()", {
"name": "method (D) f()",
"pos": "testdata/src/implements-methods-json/main.go:25:12" "pos": "testdata/src/implements-methods-json/main.go:25:12"
}, },
"from_method": [ {
{ "name": "method (FG) f()",
"name": "method (F) f()", "pos": "testdata/src/implements-methods-json/main.go:17:2"
"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 -------- -------- @implements FG.f --------
{ {
"mode": "implements", "type": {
"implements": { "name": "implements-methods-json.FG",
"type": { "pos": "testdata/src/implements-methods-json/main.go:16:6",
"kind": "interface"
},
"to": [
{
"name": "*implements-methods-json.D", "name": "*implements-methods-json.D",
"pos": "testdata/src/implements-methods-json/main.go:22:6", "pos": "testdata/src/implements-methods-json/main.go:22:6",
"kind": "pointer" "kind": "pointer"
}, }
"from": [ ],
{ "from": [
"name": "implements-methods-json.F", {
"pos": "testdata/src/implements-methods-json/main.go:12:6", "name": "implements-methods-json.F",
"kind": "interface" "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", "method": {
"kind": "interface" "name": "func (FG).f()",
} "pos": "testdata/src/implements-methods-json/main.go:17:2"
], },
"method": { "to_method": [
"name": "func (*D).g() []int", {
"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" "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": "implements-methods-json.FG",
"name": "", "pos": "testdata/src/implements-methods-json/main.go:16:6",
"pos": "" "kind": "interface"
}, }
{ ],
"name": "method (FG) g() []int", "method": {
"pos": "testdata/src/implements-methods-json/main.go:18:2" "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 -------- -------- @implements Len --------
{ {
"mode": "implements", "type": {
"implements": { "name": "implements-methods-json.sorter",
"type": { "pos": "testdata/src/implements-methods-json/main.go:29:6",
"name": "implements-methods-json.sorter", "kind": "slice"
"pos": "testdata/src/implements-methods-json/main.go:29:6", },
"kind": "slice" "from": [
}, {
"from": [ "name": "lib.Sorter",
{ "pos": "testdata/src/lib/lib.go:16:6",
"name": "lib.Sorter", "kind": "interface"
"pos": "testdata/src/lib/lib.go:16:6", }
"kind": "interface" ],
} "method": {
], "name": "func (sorter).Len() int",
"method": { "pos": "testdata/src/implements-methods-json/main.go:31:15"
"name": "func (sorter).Len() int", },
"pos": "testdata/src/implements-methods-json/main.go:31:15" "from_method": [
}, {
"from_method": [ "name": "method (lib.Sorter) Len() int",
{ "pos": "testdata/src/lib/lib.go:17:2"
"name": "method (lib.Sorter) Len() int", }
"pos": "testdata/src/lib/lib.go:17:2" ]
}
]
}
} }
-------- @implements I.Method -------- -------- @implements I.Method --------
{ {
"mode": "implements", "type": {
"implements": { "name": "implements-methods-json.I",
"type": { "pos": "testdata/src/implements-methods-json/main.go:35:6",
"name": "implements-methods-json.I", "kind": "interface"
"pos": "testdata/src/implements-methods-json/main.go:35:6", },
"kind": "interface" "to": [
}, {
"to": [ "name": "lib.Type",
{ "pos": "testdata/src/lib/lib.go:3:6",
"name": "lib.Type", "kind": "basic"
"pos": "testdata/src/lib/lib.go:3:6", }
"kind": "basic" ],
} "method": {
], "name": "func (I).Method(*int) *int",
"method": { "pos": "testdata/src/implements-methods-json/main.go:36:2"
"name": "func (I).Method(*int) *int", },
"pos": "testdata/src/implements-methods-json/main.go:36:2" "to_method": [
}, {
"to_method": [ "name": "method (lib.Type) Method(x *int) *int",
{ "pos": "testdata/src/lib/lib.go:5:13"
"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 -------- -------- @peers peer-recv-chA --------
{ {
"mode": "peers", "pos": "testdata/src/peers-json/main.go:11:7",
"peers": { "type": "chan *int",
"pos": "testdata/src/peers-json/main.go:11:7", "allocs": [
"type": "chan *int", "testdata/src/peers-json/main.go:8:13"
"allocs": [ ],
"testdata/src/peers-json/main.go:8:13" "receives": [
], "testdata/src/peers-json/main.go:9:2",
"receives": [ "testdata/src/peers-json/main.go:11:7"
"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 -------- -------- @pointsto val-p --------
{ [
"mode": "pointsto", {
"pointsto": [ "type": "*int",
{ "labels": [
"type": "*int", {
"labels": [ "pos": "testdata/src/pointsto-json/main.go:8:6",
{ "desc": "s.x[*]"
"pos": "testdata/src/pointsto-json/main.go:8:6", }
"desc": "s.x[*]" ]
} }
] ]
}
]
}
-------- @pointsto val-i -------- -------- @pointsto val-i --------
{ [
"mode": "pointsto", {
"pointsto": [ "type": "*D",
{ "namepos": "testdata/src/pointsto-json/main.go:24:6",
"type": "*D", "labels": [
"namepos": "testdata/src/pointsto-json/main.go:24:6", {
"labels": [ "pos": "testdata/src/pointsto-json/main.go:14:10",
{ "desc": "new"
"pos": "testdata/src/pointsto-json/main.go:14:10", }
"desc": "new" ]
} },
] {
}, "type": "C",
{ "namepos": "testdata/src/pointsto-json/main.go:23:6"
"type": "C", }
"namepos": "testdata/src/pointsto-json/main.go:23:6" ]
}
]
}

View File

@ -1,64 +1,184 @@
-------- @referrers ref-package -------- -------- @referrers ref-package --------
{ {
"mode": "referrers", "desc": "package lib"
"referrers": { }
"desc": "package lib", {
"refs": [ "package": "describe",
"testdata/src/describe/main.go:91:8", "refs": [
"testdata/src/imports/main.go:18:12", {
"testdata/src/imports/main.go:19:2", "pos": "testdata/src/describe/main.go:91:8",
"testdata/src/imports/main.go:20:2", "text": "\tvar _ lib.Outer // @describe lib-outer \"Outer\""
"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", "package": "imports",
"testdata/src/referrers/int_test.go:7:7", "refs": [
"testdata/src/referrers/main.go:16:8", {
"testdata/src/referrers/main.go:16:19" "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 -------- -------- @referrers ref-method --------
{ {
"mode": "referrers", "objpos": "testdata/src/lib/lib.go:5:13",
"referrers": { "desc": "func (lib.Type).Method(x *int) *int"
"objpos": "testdata/src/lib/lib.go:5:13", }
"desc": "func (lib.Type).Method(x *int) *int", {
"refs": [ "package": "imports",
"testdata/src/imports/main.go:22:9", "refs": [
"testdata/src/referrers-json/main.go:15:8", {
"testdata/src/referrers-json/main.go:16:8", "pos": "testdata/src/imports/main.go:22:9",
"testdata/src/referrers/ext_test.go:10:17", "text": "\tp := t.Method(\u0026a) // @describe ref-method \"Method\""
"testdata/src/referrers/int_test.go:7:17", }
"testdata/src/referrers/main.go:17:8", ]
"testdata/src/referrers/main.go:18:8" }
] {
} "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 -------- -------- @referrers ref-local --------
{ {
"mode": "referrers", "objpos": "testdata/src/referrers-json/main.go:14:6",
"referrers": { "desc": "var v lib.Type"
"objpos": "testdata/src/referrers-json/main.go:14:6", }
"desc": "var v lib.Type", {
"refs": [ "package": "referrers-json",
"testdata/src/referrers-json/main.go:15:6", "refs": [
"testdata/src/referrers-json/main.go:16:6", {
"testdata/src/referrers-json/main.go:17:2", "pos": "testdata/src/referrers-json/main.go:15:6",
"testdata/src/referrers-json/main.go:18:2" "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 -------- -------- @referrers ref-field --------
{ {
"mode": "referrers", "objpos": "testdata/src/referrers-json/main.go:10:2",
"referrers": { "desc": "field f int"
"objpos": "testdata/src/referrers-json/main.go:10:2", }
"desc": "field f int", {
"refs": [ "package": "referrers-json",
"testdata/src/referrers-json/main.go:20:10", "refs": [
"testdata/src/referrers-json/main.go:23:5" {
] "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 -------- -------- @referrers package-decl --------
1 references to package main ("referrers") references to package main ("referrers")
var _ renamed.T var _ renamed.T
-------- @referrers type -------- -------- @referrers type --------
2 references to type s struct{f int} references to type s struct{f int}
_ = s{}.f // @referrers ref-field "f" _ = s{}.f // @referrers ref-field "f"
var s2 s var s2 s
-------- @referrers ref-package -------- -------- @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" var _ lib.Outer // @describe lib-outer "Outer"
const c = lib.Const // @describe ref-const "Const" const c = lib.Const // @describe ref-const "Const"
lib.Func() // @describe ref-func "Func" lib.Func() // @describe ref-func "Func"
lib.Var++ // @describe ref-var "Var" lib.Var++ // @describe ref-var "Var"
var t lib.Type // @describe ref-type "Type" var t lib.Type // @describe ref-type "Type"
var _ lib.Type // @describe ref-pkg "lib" 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 _ = (lib.Type).Method // ref from internal test package
var v lib.Type = lib.Const // @referrers ref-package "lib" _ = (lib.Type).Method // ref from external test package
var v lib.Type = lib.Const // @referrers ref-package "lib"
-------- @referrers ref-method -------- -------- @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" 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 _ = (lib.Type).Method // ref from internal test package
_ = v.Method // @referrers ref-method "Method" _ = (lib.Type).Method // ref from external test package
_ = v.Method
-------- @referrers ref-local -------- -------- @referrers ref-local --------
4 references to var v lib.Type references to var v lib.Type
_ = v.Method // @referrers ref-method "Method" _ = v.Method // @referrers ref-method "Method"
_ = v.Method _ = v.Method
v++ //@referrers ref-local "v" v++ //@referrers ref-local "v"
v++ v++
-------- @referrers ref-field -------- -------- @referrers ref-field --------
2 references to field f int references to field f int
_ = s{}.f // @referrers ref-field "f" _ = s{}.f // @referrers ref-field "f"
s2.f = 1 s2.f = 1
-------- @referrers ref-type-U -------- -------- @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) 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 -------- -------- @what call --------
{ {
"mode": "what", "enclosing": [
"what": { {
"enclosing": [ "desc": "identifier",
{ "start": 175,
"desc": "identifier", "end": 176
"start": 175, },
"end": 176 {
}, "desc": "function call (or conversion)",
{ "start": 175,
"desc": "function call (or conversion)", "end": 178
"start": 175, },
"end": 178 {
}, "desc": "expression statement",
{ "start": 175,
"desc": "expression statement", "end": 178
"start": 175, },
"end": 178 {
}, "desc": "block",
{ "start": 172,
"desc": "block", "end": 198
"start": 172, },
"end": 198 {
}, "desc": "function declaration",
{ "start": 160,
"desc": "function declaration", "end": 198
"start": 160, },
"end": 198 {
}, "desc": "source file",
{ "start": 0,
"desc": "source file", "end": 198
"start": 0, }
"end": 198 ],
} "modes": [
], "callees",
"modes": [ "callers",
"callees", "callstack",
"callers", "definition",
"callstack", "describe",
"definition", "freevars",
"describe", "implements",
"freevars", "pointsto",
"implements", "referrers"
"pointsto", ],
"referrers" "srcdir": "testdata/src",
], "importpath": "what-json"
"srcdir": "testdata/src",
"importpath": "what-json"
}
} }

View File

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

View File

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