Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
257
vendor/golang.org/x/tools/cmd/guru/callees.go
generated
vendored
Normal file
257
vendor/golang.org/x/tools/cmd/guru/callees.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/pointer"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
// Callees reports the possible callees of the function call site
|
||||
// identified by the specified source location.
|
||||
func callees(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
|
||||
if err := setPTAScope(&lconf, q.Scope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := loadWithSoftErrors(&lconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Determine the enclosing call for the specified position.
|
||||
var e *ast.CallExpr
|
||||
for _, n := range qpos.path {
|
||||
if e, _ = n.(*ast.CallExpr); e != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if e == nil {
|
||||
return fmt.Errorf("there is no function call here")
|
||||
}
|
||||
// TODO(adonovan): issue an error if the call is "too far
|
||||
// away" from the current selection, as this most likely is
|
||||
// not what the user intended.
|
||||
|
||||
// Reject type conversions.
|
||||
if qpos.info.Types[e.Fun].IsType() {
|
||||
return fmt.Errorf("this is a type conversion, not a function call")
|
||||
}
|
||||
|
||||
// Deal with obviously static calls before constructing SSA form.
|
||||
// Some static calls may yet require SSA construction,
|
||||
// e.g. f := func(){}; f().
|
||||
switch funexpr := unparen(e.Fun).(type) {
|
||||
case *ast.Ident:
|
||||
switch obj := qpos.info.Uses[funexpr].(type) {
|
||||
case *types.Builtin:
|
||||
// Reject calls to built-ins.
|
||||
return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
|
||||
case *types.Func:
|
||||
// This is a static function call
|
||||
q.Output(lprog.Fset, &calleesTypesResult{
|
||||
site: e,
|
||||
callee: obj,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
sel := qpos.info.Selections[funexpr]
|
||||
if sel == nil {
|
||||
// qualified identifier.
|
||||
// May refer to top level function variable
|
||||
// or to top level function.
|
||||
callee := qpos.info.Uses[funexpr.Sel]
|
||||
if obj, ok := callee.(*types.Func); ok {
|
||||
q.Output(lprog.Fset, &calleesTypesResult{
|
||||
site: e,
|
||||
callee: obj,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
} else if sel.Kind() == types.MethodVal {
|
||||
// Inspect the receiver type of the selected method.
|
||||
// If it is concrete, the call is statically dispatched.
|
||||
// (Due to implicit field selections, it is not enough to look
|
||||
// at sel.Recv(), the type of the actual receiver expression.)
|
||||
method := sel.Obj().(*types.Func)
|
||||
recvtype := method.Type().(*types.Signature).Recv().Type()
|
||||
if !types.IsInterface(recvtype) {
|
||||
// static method call
|
||||
q.Output(lprog.Fset, &calleesTypesResult{
|
||||
site: e,
|
||||
callee: method,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
|
||||
|
||||
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkg := prog.Package(qpos.info.Pkg)
|
||||
if pkg == nil {
|
||||
return fmt.Errorf("no SSA package")
|
||||
}
|
||||
|
||||
// Defer SSA construction till after errors are reported.
|
||||
prog.Build()
|
||||
|
||||
// Ascertain calling function and call site.
|
||||
callerFn := ssa.EnclosingFunction(pkg, qpos.path)
|
||||
if callerFn == nil {
|
||||
return fmt.Errorf("no SSA function built for this location (dead code?)")
|
||||
}
|
||||
|
||||
// Find the call site.
|
||||
site, err := findCallSite(callerFn, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
funcs, err := findCallees(ptaConfig, site)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.Output(lprog.Fset, &calleesSSAResult{
|
||||
site: site,
|
||||
funcs: funcs,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) {
|
||||
instr, _ := fn.ValueForExpr(call)
|
||||
callInstr, _ := instr.(ssa.CallInstruction)
|
||||
if instr == nil {
|
||||
return nil, fmt.Errorf("this call site is unreachable in this analysis")
|
||||
}
|
||||
return callInstr, nil
|
||||
}
|
||||
|
||||
func findCallees(conf *pointer.Config, site ssa.CallInstruction) ([]*ssa.Function, error) {
|
||||
// Avoid running the pointer analysis for static calls.
|
||||
if callee := site.Common().StaticCallee(); callee != nil {
|
||||
switch callee.String() {
|
||||
case "runtime.SetFinalizer", "(reflect.Value).Call":
|
||||
// The PTA treats calls to these intrinsics as dynamic.
|
||||
// TODO(adonovan): avoid reliance on PTA internals.
|
||||
|
||||
default:
|
||||
return []*ssa.Function{callee}, nil // singleton
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic call: use pointer analysis.
|
||||
conf.BuildCallGraph = true
|
||||
cg := ptrAnalysis(conf).CallGraph
|
||||
cg.DeleteSyntheticNodes()
|
||||
|
||||
// Find all call edges from the site.
|
||||
n := cg.Nodes[site.Parent()]
|
||||
if n == nil {
|
||||
return nil, fmt.Errorf("this call site is unreachable in this analysis")
|
||||
}
|
||||
calleesMap := make(map[*ssa.Function]bool)
|
||||
for _, edge := range n.Out {
|
||||
if edge.Site == site {
|
||||
calleesMap[edge.Callee.Func] = true
|
||||
}
|
||||
}
|
||||
|
||||
// De-duplicate and sort.
|
||||
funcs := make([]*ssa.Function, 0, len(calleesMap))
|
||||
for f := range calleesMap {
|
||||
funcs = append(funcs, f)
|
||||
}
|
||||
sort.Sort(byFuncPos(funcs))
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
type calleesSSAResult struct {
|
||||
site ssa.CallInstruction
|
||||
funcs []*ssa.Function
|
||||
}
|
||||
|
||||
type calleesTypesResult struct {
|
||||
site *ast.CallExpr
|
||||
callee *types.Func
|
||||
}
|
||||
|
||||
func (r *calleesSSAResult) PrintPlain(printf printfFunc) {
|
||||
if len(r.funcs) == 0 {
|
||||
// dynamic call on a provably nil func/interface
|
||||
printf(r.site, "%s on nil value", r.site.Common().Description())
|
||||
} else {
|
||||
printf(r.site, "this %s dispatches to:", r.site.Common().Description())
|
||||
for _, callee := range r.funcs {
|
||||
printf(callee, "\t%s", callee)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *calleesSSAResult) JSON(fset *token.FileSet) []byte {
|
||||
j := &serial.Callees{
|
||||
Pos: fset.Position(r.site.Pos()).String(),
|
||||
Desc: r.site.Common().Description(),
|
||||
}
|
||||
for _, callee := range r.funcs {
|
||||
j.Callees = append(j.Callees, &serial.Callee{
|
||||
Name: callee.String(),
|
||||
Pos: fset.Position(callee.Pos()).String(),
|
||||
})
|
||||
}
|
||||
return toJSON(j)
|
||||
}
|
||||
|
||||
func (r *calleesTypesResult) PrintPlain(printf printfFunc) {
|
||||
printf(r.site, "this static function call dispatches to:")
|
||||
printf(r.callee, "\t%s", r.callee.FullName())
|
||||
}
|
||||
|
||||
func (r *calleesTypesResult) JSON(fset *token.FileSet) []byte {
|
||||
j := &serial.Callees{
|
||||
Pos: fset.Position(r.site.Pos()).String(),
|
||||
Desc: "static function call",
|
||||
}
|
||||
j.Callees = []*serial.Callee{
|
||||
{
|
||||
Name: r.callee.FullName(),
|
||||
Pos: fset.Position(r.callee.Pos()).String(),
|
||||
},
|
||||
}
|
||||
return toJSON(j)
|
||||
}
|
||||
|
||||
// NB: byFuncPos is not deterministic across packages since it depends on load order.
|
||||
// Use lessPos if the tests need it.
|
||||
type byFuncPos []*ssa.Function
|
||||
|
||||
func (a byFuncPos) Len() int { return len(a) }
|
||||
func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
|
||||
func (a byFuncPos) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
195
vendor/golang.org/x/tools/cmd/guru/callers.go
generated
vendored
Normal file
195
vendor/golang.org/x/tools/cmd/guru/callers.go
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
// Callers reports the possible callers of the function
|
||||
// immediately enclosing the specified source location.
|
||||
//
|
||||
func callers(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
|
||||
if err := setPTAScope(&lconf, q.Scope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := loadWithSoftErrors(&lconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(lprog, 0)
|
||||
|
||||
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkg := prog.Package(qpos.info.Pkg)
|
||||
if pkg == nil {
|
||||
return fmt.Errorf("no SSA package")
|
||||
}
|
||||
if !ssa.HasEnclosingFunction(pkg, qpos.path) {
|
||||
return fmt.Errorf("this position is not inside a function")
|
||||
}
|
||||
|
||||
// Defer SSA construction till after errors are reported.
|
||||
prog.Build()
|
||||
|
||||
target := ssa.EnclosingFunction(pkg, qpos.path)
|
||||
if target == nil {
|
||||
return fmt.Errorf("no SSA function built for this location (dead code?)")
|
||||
}
|
||||
|
||||
// If the function is never address-taken, all calls are direct
|
||||
// and can be found quickly by inspecting the whole SSA program.
|
||||
cg := directCallsTo(target, entryPoints(ptaConfig.Mains))
|
||||
if cg == nil {
|
||||
// Run the pointer analysis, recording each
|
||||
// call found to originate from target.
|
||||
// (Pointer analysis may return fewer results than
|
||||
// directCallsTo because it ignores dead code.)
|
||||
ptaConfig.BuildCallGraph = true
|
||||
cg = ptrAnalysis(ptaConfig).CallGraph
|
||||
}
|
||||
cg.DeleteSyntheticNodes()
|
||||
edges := cg.CreateNode(target).In
|
||||
|
||||
// TODO(adonovan): sort + dedup calls to ensure test determinism.
|
||||
|
||||
q.Output(lprog.Fset, &callersResult{
|
||||
target: target,
|
||||
callgraph: cg,
|
||||
edges: edges,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// directCallsTo inspects the whole program and returns a callgraph
|
||||
// containing edges for all direct calls to the target function.
|
||||
// directCallsTo returns nil if the function is ever address-taken.
|
||||
func directCallsTo(target *ssa.Function, entrypoints []*ssa.Function) *callgraph.Graph {
|
||||
cg := callgraph.New(nil) // use nil as root *Function
|
||||
targetNode := cg.CreateNode(target)
|
||||
|
||||
// Is the function a program entry point?
|
||||
// If so, add edge from callgraph root.
|
||||
for _, f := range entrypoints {
|
||||
if f == target {
|
||||
callgraph.AddEdge(cg.Root, nil, targetNode)
|
||||
}
|
||||
}
|
||||
|
||||
// Find receiver type (for methods).
|
||||
var recvType types.Type
|
||||
if recv := target.Signature.Recv(); recv != nil {
|
||||
recvType = recv.Type()
|
||||
}
|
||||
|
||||
// Find all direct calls to function,
|
||||
// or a place where its address is taken.
|
||||
var space [32]*ssa.Value // preallocate
|
||||
for fn := range ssautil.AllFunctions(target.Prog) {
|
||||
for _, b := range fn.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
// Is this a method (T).f of a concrete type T
|
||||
// whose runtime type descriptor is address-taken?
|
||||
// (To be fully sound, we would have to check that
|
||||
// the type doesn't make it to reflection as a
|
||||
// subelement of some other address-taken type.)
|
||||
if recvType != nil {
|
||||
if mi, ok := instr.(*ssa.MakeInterface); ok {
|
||||
if types.Identical(mi.X.Type(), recvType) {
|
||||
return nil // T is address-taken
|
||||
}
|
||||
if ptr, ok := mi.X.Type().(*types.Pointer); ok &&
|
||||
types.Identical(ptr.Elem(), recvType) {
|
||||
return nil // *T is address-taken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Direct call to target?
|
||||
rands := instr.Operands(space[:0])
|
||||
if site, ok := instr.(ssa.CallInstruction); ok &&
|
||||
site.Common().Value == target {
|
||||
callgraph.AddEdge(cg.CreateNode(fn), site, targetNode)
|
||||
rands = rands[1:] // skip .Value (rands[0])
|
||||
}
|
||||
|
||||
// Address-taken?
|
||||
for _, rand := range rands {
|
||||
if rand != nil && *rand == target {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cg
|
||||
}
|
||||
|
||||
func entryPoints(mains []*ssa.Package) []*ssa.Function {
|
||||
var entrypoints []*ssa.Function
|
||||
for _, pkg := range mains {
|
||||
entrypoints = append(entrypoints, pkg.Func("init"))
|
||||
if main := pkg.Func("main"); main != nil && pkg.Pkg.Name() == "main" {
|
||||
entrypoints = append(entrypoints, main)
|
||||
}
|
||||
}
|
||||
return entrypoints
|
||||
}
|
||||
|
||||
type callersResult struct {
|
||||
target *ssa.Function
|
||||
callgraph *callgraph.Graph
|
||||
edges []*callgraph.Edge
|
||||
}
|
||||
|
||||
func (r *callersResult) PrintPlain(printf printfFunc) {
|
||||
root := r.callgraph.Root
|
||||
if r.edges == nil {
|
||||
printf(r.target, "%s is not reachable in this program.", r.target)
|
||||
} else {
|
||||
printf(r.target, "%s is called from these %d sites:", r.target, len(r.edges))
|
||||
for _, edge := range r.edges {
|
||||
if edge.Caller == root {
|
||||
printf(r.target, "the root of the call graph")
|
||||
} else {
|
||||
printf(edge, "\t%s from %s", edge.Description(), edge.Caller.Func)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *callersResult) JSON(fset *token.FileSet) []byte {
|
||||
var callers []serial.Caller
|
||||
for _, edge := range r.edges {
|
||||
callers = append(callers, serial.Caller{
|
||||
Caller: edge.Caller.Func.String(),
|
||||
Pos: fset.Position(edge.Pos()).String(),
|
||||
Desc: edge.Description(),
|
||||
})
|
||||
}
|
||||
return toJSON(callers)
|
||||
}
|
141
vendor/golang.org/x/tools/cmd/guru/callstack.go
generated
vendored
Normal file
141
vendor/golang.org/x/tools/cmd/guru/callstack.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/callgraph/static"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
// Callstack displays an arbitrary path from a root of the callgraph
|
||||
// to the function at the current position.
|
||||
//
|
||||
// The information may be misleading in a context-insensitive
|
||||
// analysis. e.g. the call path X->Y->Z might be infeasible if Y never
|
||||
// calls Z when it is called from X. TODO(adonovan): think about UI.
|
||||
//
|
||||
// TODO(adonovan): permit user to specify a starting point other than
|
||||
// the analysis root.
|
||||
//
|
||||
func callstack(q *Query) error {
|
||||
fset := token.NewFileSet()
|
||||
lconf := loader.Config{Fset: fset, Build: q.Build}
|
||||
|
||||
if err := setPTAScope(&lconf, q.Scope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := loadWithSoftErrors(&lconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(lprog, 0)
|
||||
|
||||
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkg := prog.Package(qpos.info.Pkg)
|
||||
if pkg == nil {
|
||||
return fmt.Errorf("no SSA package")
|
||||
}
|
||||
|
||||
if !ssa.HasEnclosingFunction(pkg, qpos.path) {
|
||||
return fmt.Errorf("this position is not inside a function")
|
||||
}
|
||||
|
||||
// Defer SSA construction till after errors are reported.
|
||||
prog.Build()
|
||||
|
||||
target := ssa.EnclosingFunction(pkg, qpos.path)
|
||||
if target == nil {
|
||||
return fmt.Errorf("no SSA function built for this location (dead code?)")
|
||||
}
|
||||
|
||||
var callpath []*callgraph.Edge
|
||||
isEnd := func(n *callgraph.Node) bool { return n.Func == target }
|
||||
|
||||
// First, build a callgraph containing only static call edges,
|
||||
// and search for an arbitrary path from a root to the target function.
|
||||
// This is quick, and the user wants a static path if one exists.
|
||||
cg := static.CallGraph(prog)
|
||||
cg.DeleteSyntheticNodes()
|
||||
for _, ep := range entryPoints(ptaConfig.Mains) {
|
||||
callpath = callgraph.PathSearch(cg.CreateNode(ep), isEnd)
|
||||
if callpath != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// No fully static path found.
|
||||
// Run the pointer analysis and build a complete call graph.
|
||||
if callpath == nil {
|
||||
ptaConfig.BuildCallGraph = true
|
||||
cg := ptrAnalysis(ptaConfig).CallGraph
|
||||
cg.DeleteSyntheticNodes()
|
||||
callpath = callgraph.PathSearch(cg.Root, isEnd)
|
||||
if callpath != nil {
|
||||
callpath = callpath[1:] // remove synthetic edge from <root>
|
||||
}
|
||||
}
|
||||
|
||||
q.Output(fset, &callstackResult{
|
||||
qpos: qpos,
|
||||
target: target,
|
||||
callpath: callpath,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type callstackResult struct {
|
||||
qpos *queryPos
|
||||
target *ssa.Function
|
||||
callpath []*callgraph.Edge
|
||||
}
|
||||
|
||||
func (r *callstackResult) PrintPlain(printf printfFunc) {
|
||||
if r.callpath != nil {
|
||||
printf(r.qpos, "Found a call path from root to %s", r.target)
|
||||
printf(r.target, "%s", r.target)
|
||||
for i := len(r.callpath) - 1; i >= 0; i-- {
|
||||
edge := r.callpath[i]
|
||||
printf(edge, "%s from %s", edge.Description(), edge.Caller.Func)
|
||||
}
|
||||
} else {
|
||||
printf(r.target, "%s is unreachable in this analysis scope", r.target)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *callstackResult) JSON(fset *token.FileSet) []byte {
|
||||
var callers []serial.Caller
|
||||
for i := len(r.callpath) - 1; i >= 0; i-- { // (innermost first)
|
||||
edge := r.callpath[i]
|
||||
callers = append(callers, serial.Caller{
|
||||
Pos: fset.Position(edge.Pos()).String(),
|
||||
Caller: edge.Caller.Func.String(),
|
||||
Desc: edge.Description(),
|
||||
})
|
||||
}
|
||||
return toJSON(&serial.CallStack{
|
||||
Pos: fset.Position(r.target.Pos()).String(),
|
||||
Target: r.target.String(),
|
||||
Callers: callers,
|
||||
})
|
||||
}
|
205
vendor/golang.org/x/tools/cmd/guru/definition.go
generated
vendored
Normal file
205
vendor/golang.org/x/tools/cmd/guru/definition.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
pathpkg "path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
// definition reports the location of the definition of an identifier.
|
||||
func definition(q *Query) error {
|
||||
// First try the simple resolution done by parser.
|
||||
// It only works for intra-file references but it is very fast.
|
||||
// (Extending this approach to all the files of the package,
|
||||
// resolved using ast.NewPackage, was not worth the effort.)
|
||||
{
|
||||
qpos, err := fastQueryPos(q.Build, q.Pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, _ := qpos.path[0].(*ast.Ident)
|
||||
if id == nil {
|
||||
return fmt.Errorf("no identifier here")
|
||||
}
|
||||
|
||||
// Did the parser resolve it to a local object?
|
||||
if obj := id.Obj; obj != nil && obj.Pos().IsValid() {
|
||||
q.Output(qpos.fset, &definitionResult{
|
||||
pos: obj.Pos(),
|
||||
descr: fmt.Sprintf("%s %s", obj.Kind, obj.Name),
|
||||
})
|
||||
return nil // success
|
||||
}
|
||||
|
||||
// Qualified identifier?
|
||||
if pkg := packageForQualIdent(qpos.path, id); pkg != "" {
|
||||
srcdir := filepath.Dir(qpos.fset.File(qpos.start).Name())
|
||||
tok, pos, err := findPackageMember(q.Build, qpos.fset, srcdir, pkg, id.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q.Output(qpos.fset, &definitionResult{
|
||||
pos: pos,
|
||||
descr: fmt.Sprintf("%s %s.%s", tok, pkg, id.Name),
|
||||
})
|
||||
return nil // success
|
||||
}
|
||||
|
||||
// Fall back on the type checker.
|
||||
}
|
||||
|
||||
// Run the type checker.
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := lconf.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, _ := qpos.path[0].(*ast.Ident)
|
||||
if id == nil {
|
||||
return fmt.Errorf("no identifier here")
|
||||
}
|
||||
|
||||
// Look up the declaration of this identifier.
|
||||
// If id is an anonymous field declaration,
|
||||
// it is both a use of a type and a def of a field;
|
||||
// prefer the use in that case.
|
||||
obj := qpos.info.Uses[id]
|
||||
if obj == nil {
|
||||
obj = qpos.info.Defs[id]
|
||||
if obj == nil {
|
||||
// Happens for y in "switch y := x.(type)",
|
||||
// and the package declaration,
|
||||
// but I think that's all.
|
||||
return fmt.Errorf("no object for identifier")
|
||||
}
|
||||
}
|
||||
|
||||
if !obj.Pos().IsValid() {
|
||||
return fmt.Errorf("%s is built in", obj.Name())
|
||||
}
|
||||
|
||||
q.Output(lprog.Fset, &definitionResult{
|
||||
pos: obj.Pos(),
|
||||
descr: qpos.objectString(obj),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// packageForQualIdent returns the package p if id is X in a qualified
|
||||
// identifier p.X; it returns "" otherwise.
|
||||
//
|
||||
// Precondition: id is path[0], and the parser did not resolve id to a
|
||||
// local object. For speed, packageForQualIdent assumes that p is a
|
||||
// package iff it is the basename of an import path (and not, say, a
|
||||
// package-level decl in another file or a predeclared identifier).
|
||||
func packageForQualIdent(path []ast.Node, id *ast.Ident) string {
|
||||
if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == id && ast.IsExported(id.Name) {
|
||||
if pkgid, ok := sel.X.(*ast.Ident); ok && pkgid.Obj == nil {
|
||||
f := path[len(path)-1].(*ast.File)
|
||||
for _, imp := range f.Imports {
|
||||
path, _ := strconv.Unquote(imp.Path.Value)
|
||||
if imp.Name != nil {
|
||||
if imp.Name.Name == pkgid.Name {
|
||||
return path // renaming import
|
||||
}
|
||||
} else if pathpkg.Base(path) == pkgid.Name {
|
||||
return path // ordinary import
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// findPackageMember returns the type and position of the declaration of
|
||||
// pkg.member by loading and parsing the files of that package.
|
||||
// srcdir is the directory in which the import appears.
|
||||
func findPackageMember(ctxt *build.Context, fset *token.FileSet, srcdir, pkg, member string) (token.Token, token.Pos, error) {
|
||||
bp, err := ctxt.Import(pkg, srcdir, 0)
|
||||
if err != nil {
|
||||
return 0, token.NoPos, err // no files for package
|
||||
}
|
||||
|
||||
// TODO(adonovan): opt: parallelize.
|
||||
for _, fname := range bp.GoFiles {
|
||||
filename := filepath.Join(bp.Dir, fname)
|
||||
|
||||
// Parse the file, opening it the file via the build.Context
|
||||
// so that we observe the effects of the -modified flag.
|
||||
f, _ := buildutil.ParseFile(fset, ctxt, nil, ".", filename, parser.Mode(0))
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find a package-level decl called 'member'.
|
||||
for _, decl := range f.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
for _, spec := range decl.Specs {
|
||||
switch spec := spec.(type) {
|
||||
case *ast.ValueSpec:
|
||||
// const or var
|
||||
for _, id := range spec.Names {
|
||||
if id.Name == member {
|
||||
return decl.Tok, id.Pos(), nil
|
||||
}
|
||||
}
|
||||
case *ast.TypeSpec:
|
||||
if spec.Name.Name == member {
|
||||
return token.TYPE, spec.Name.Pos(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
if decl.Recv == nil && decl.Name.Name == member {
|
||||
return token.FUNC, decl.Name.Pos(), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, token.NoPos, fmt.Errorf("couldn't find declaration of %s in %q", member, pkg)
|
||||
}
|
||||
|
||||
type definitionResult struct {
|
||||
pos token.Pos // (nonzero) location of definition
|
||||
descr string // description of object it denotes
|
||||
}
|
||||
|
||||
func (r *definitionResult) PrintPlain(printf printfFunc) {
|
||||
printf(r.pos, "defined here as %s", r.descr)
|
||||
}
|
||||
|
||||
func (r *definitionResult) JSON(fset *token.FileSet) []byte {
|
||||
return toJSON(&serial.Definition{
|
||||
Desc: r.descr,
|
||||
ObjPos: fset.Position(r.pos).String(),
|
||||
})
|
||||
}
|
899
vendor/golang.org/x/tools/cmd/guru/describe.go
generated
vendored
Normal file
899
vendor/golang.org/x/tools/cmd/guru/describe.go
generated
vendored
Normal file
@@ -0,0 +1,899 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
exact "go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// describe describes the syntax node denoted by the query position,
|
||||
// including:
|
||||
// - its syntactic category
|
||||
// - the definition of its referent (for identifiers) [now redundant]
|
||||
// - its type, fields, and methods (for an expression or type expression)
|
||||
//
|
||||
func describe(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := lconf.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if false { // debugging
|
||||
fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s",
|
||||
astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
|
||||
}
|
||||
|
||||
var qr QueryResult
|
||||
path, action := findInterestingNode(qpos.info, qpos.path)
|
||||
switch action {
|
||||
case actionExpr:
|
||||
qr, err = describeValue(qpos, path)
|
||||
|
||||
case actionType:
|
||||
qr, err = describeType(qpos, path)
|
||||
|
||||
case actionPackage:
|
||||
qr, err = describePackage(qpos, path)
|
||||
|
||||
case actionStmt:
|
||||
qr, err = describeStmt(qpos, path)
|
||||
|
||||
case actionUnknown:
|
||||
qr = &describeUnknownResult{path[0]}
|
||||
|
||||
default:
|
||||
panic(action) // unreachable
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q.Output(lprog.Fset, qr)
|
||||
return nil
|
||||
}
|
||||
|
||||
type describeUnknownResult struct {
|
||||
node ast.Node
|
||||
}
|
||||
|
||||
func (r *describeUnknownResult) PrintPlain(printf printfFunc) {
|
||||
// Nothing much to say about misc syntax.
|
||||
printf(r.node, "%s", astutil.NodeDescription(r.node))
|
||||
}
|
||||
|
||||
func (r *describeUnknownResult) JSON(fset *token.FileSet) []byte {
|
||||
return toJSON(&serial.Describe{
|
||||
Desc: astutil.NodeDescription(r.node),
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
})
|
||||
}
|
||||
|
||||
type action int
|
||||
|
||||
const (
|
||||
actionUnknown action = iota // None of the below
|
||||
actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var})
|
||||
actionType // type Expr or Ident(types.TypeName).
|
||||
actionStmt // Stmt or Ident(types.Label)
|
||||
actionPackage // Ident(types.Package) or ImportSpec
|
||||
)
|
||||
|
||||
// findInterestingNode classifies the syntax node denoted by path as one of:
|
||||
// - an expression, part of an expression or a reference to a constant
|
||||
// or variable;
|
||||
// - a type, part of a type, or a reference to a named type;
|
||||
// - a statement, part of a statement, or a label referring to a statement;
|
||||
// - part of a package declaration or import spec.
|
||||
// - none of the above.
|
||||
// and returns the most "interesting" associated node, which may be
|
||||
// the same node, an ancestor or a descendent.
|
||||
//
|
||||
func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
|
||||
// TODO(adonovan): integrate with go/types/stdlib_test.go and
|
||||
// apply this to every AST node we can find to make sure it
|
||||
// doesn't crash.
|
||||
|
||||
// TODO(adonovan): audit for ParenExpr safety, esp. since we
|
||||
// traverse up and down.
|
||||
|
||||
// TODO(adonovan): if the users selects the "." in
|
||||
// "fmt.Fprintf()", they'll get an ambiguous selection error;
|
||||
// we won't even reach here. Can we do better?
|
||||
|
||||
// TODO(adonovan): describing a field within 'type T struct {...}'
|
||||
// describes the (anonymous) struct type and concludes "no methods".
|
||||
// We should ascend to the enclosing type decl, if any.
|
||||
|
||||
for len(path) > 0 {
|
||||
switch n := path[0].(type) {
|
||||
case *ast.GenDecl:
|
||||
if len(n.Specs) == 1 {
|
||||
// Descend to sole {Import,Type,Value}Spec child.
|
||||
path = append([]ast.Node{n.Specs[0]}, path...)
|
||||
continue
|
||||
}
|
||||
return path, actionUnknown // uninteresting
|
||||
|
||||
case *ast.FuncDecl:
|
||||
// Descend to function name.
|
||||
path = append([]ast.Node{n.Name}, path...)
|
||||
continue
|
||||
|
||||
case *ast.ImportSpec:
|
||||
return path, actionPackage
|
||||
|
||||
case *ast.ValueSpec:
|
||||
if len(n.Names) == 1 {
|
||||
// Descend to sole Ident child.
|
||||
path = append([]ast.Node{n.Names[0]}, path...)
|
||||
continue
|
||||
}
|
||||
return path, actionUnknown // uninteresting
|
||||
|
||||
case *ast.TypeSpec:
|
||||
// Descend to type name.
|
||||
path = append([]ast.Node{n.Name}, path...)
|
||||
continue
|
||||
|
||||
case ast.Stmt:
|
||||
return path, actionStmt
|
||||
|
||||
case *ast.ArrayType,
|
||||
*ast.StructType,
|
||||
*ast.FuncType,
|
||||
*ast.InterfaceType,
|
||||
*ast.MapType,
|
||||
*ast.ChanType:
|
||||
return path, actionType
|
||||
|
||||
case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
|
||||
return path, actionUnknown // uninteresting
|
||||
|
||||
case *ast.Ellipsis:
|
||||
// Continue to enclosing node.
|
||||
// e.g. [...]T in ArrayType
|
||||
// f(x...) in CallExpr
|
||||
// f(x...T) in FuncType
|
||||
|
||||
case *ast.Field:
|
||||
// TODO(adonovan): this needs more thought,
|
||||
// since fields can be so many things.
|
||||
if len(n.Names) == 1 {
|
||||
// Descend to sole Ident child.
|
||||
path = append([]ast.Node{n.Names[0]}, path...)
|
||||
continue
|
||||
}
|
||||
// Zero names (e.g. anon field in struct)
|
||||
// or multiple field or param names:
|
||||
// continue to enclosing field list.
|
||||
|
||||
case *ast.FieldList:
|
||||
// Continue to enclosing node:
|
||||
// {Struct,Func,Interface}Type or FuncDecl.
|
||||
|
||||
case *ast.BasicLit:
|
||||
if _, ok := path[1].(*ast.ImportSpec); ok {
|
||||
return path[1:], actionPackage
|
||||
}
|
||||
return path, actionExpr
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
// TODO(adonovan): use Selections info directly.
|
||||
if pkginfo.Uses[n.Sel] == nil {
|
||||
// TODO(adonovan): is this reachable?
|
||||
return path, actionUnknown
|
||||
}
|
||||
// Descend to .Sel child.
|
||||
path = append([]ast.Node{n.Sel}, path...)
|
||||
continue
|
||||
|
||||
case *ast.Ident:
|
||||
switch pkginfo.ObjectOf(n).(type) {
|
||||
case *types.PkgName:
|
||||
return path, actionPackage
|
||||
|
||||
case *types.Const:
|
||||
return path, actionExpr
|
||||
|
||||
case *types.Label:
|
||||
return path, actionStmt
|
||||
|
||||
case *types.TypeName:
|
||||
return path, actionType
|
||||
|
||||
case *types.Var:
|
||||
// For x in 'struct {x T}', return struct type, for now.
|
||||
if _, ok := path[1].(*ast.Field); ok {
|
||||
_ = path[2].(*ast.FieldList) // assertion
|
||||
if _, ok := path[3].(*ast.StructType); ok {
|
||||
return path[3:], actionType
|
||||
}
|
||||
}
|
||||
return path, actionExpr
|
||||
|
||||
case *types.Func:
|
||||
return path, actionExpr
|
||||
|
||||
case *types.Builtin:
|
||||
// For reference to built-in function, return enclosing call.
|
||||
path = path[1:] // ascend to enclosing function call
|
||||
continue
|
||||
|
||||
case *types.Nil:
|
||||
return path, actionExpr
|
||||
}
|
||||
|
||||
// No object.
|
||||
switch path[1].(type) {
|
||||
case *ast.SelectorExpr:
|
||||
// Return enclosing selector expression.
|
||||
return path[1:], actionExpr
|
||||
|
||||
case *ast.Field:
|
||||
// TODO(adonovan): test this.
|
||||
// e.g. all f in:
|
||||
// struct { f, g int }
|
||||
// interface { f() }
|
||||
// func (f T) method(f, g int) (f, g bool)
|
||||
//
|
||||
// switch path[3].(type) {
|
||||
// case *ast.FuncDecl:
|
||||
// case *ast.StructType:
|
||||
// case *ast.InterfaceType:
|
||||
// }
|
||||
//
|
||||
// return path[1:], actionExpr
|
||||
//
|
||||
// Unclear what to do with these.
|
||||
// Struct.Fields -- field
|
||||
// Interface.Methods -- field
|
||||
// FuncType.{Params.Results} -- actionExpr
|
||||
// FuncDecl.Recv -- actionExpr
|
||||
|
||||
case *ast.File:
|
||||
// 'package foo'
|
||||
return path, actionPackage
|
||||
|
||||
case *ast.ImportSpec:
|
||||
return path[1:], actionPackage
|
||||
|
||||
default:
|
||||
// e.g. blank identifier
|
||||
// or y in "switch y := x.(type)"
|
||||
// or code in a _test.go file that's not part of the package.
|
||||
return path, actionUnknown
|
||||
}
|
||||
|
||||
case *ast.StarExpr:
|
||||
if pkginfo.Types[n].IsType() {
|
||||
return path, actionType
|
||||
}
|
||||
return path, actionExpr
|
||||
|
||||
case ast.Expr:
|
||||
// All Expr but {BasicLit,Ident,StarExpr} are
|
||||
// "true" expressions that evaluate to a value.
|
||||
return path, actionExpr
|
||||
}
|
||||
|
||||
// Ascend to parent.
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
return nil, actionUnknown // unreachable
|
||||
}
|
||||
|
||||
func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) {
|
||||
var expr ast.Expr
|
||||
var obj types.Object
|
||||
switch n := path[0].(type) {
|
||||
case *ast.ValueSpec:
|
||||
// ambiguous ValueSpec containing multiple names
|
||||
return nil, fmt.Errorf("multiple value specification")
|
||||
case *ast.Ident:
|
||||
obj = qpos.info.ObjectOf(n)
|
||||
expr = n
|
||||
case ast.Expr:
|
||||
expr = n
|
||||
default:
|
||||
// TODO(adonovan): is this reachable?
|
||||
return nil, fmt.Errorf("unexpected AST for expr: %T", n)
|
||||
}
|
||||
|
||||
typ := qpos.info.TypeOf(expr)
|
||||
if typ == nil {
|
||||
typ = types.Typ[types.Invalid]
|
||||
}
|
||||
constVal := qpos.info.Types[expr].Value
|
||||
if c, ok := obj.(*types.Const); ok {
|
||||
constVal = c.Val()
|
||||
}
|
||||
|
||||
return &describeValueResult{
|
||||
qpos: qpos,
|
||||
expr: expr,
|
||||
typ: typ,
|
||||
constVal: constVal,
|
||||
obj: obj,
|
||||
methods: accessibleMethods(typ, qpos.info.Pkg),
|
||||
fields: accessibleFields(typ, qpos.info.Pkg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type describeValueResult struct {
|
||||
qpos *queryPos
|
||||
expr ast.Expr // query node
|
||||
typ types.Type // type of expression
|
||||
constVal exact.Value // value of expression, if constant
|
||||
obj types.Object // var/func/const object, if expr was Ident
|
||||
methods []*types.Selection
|
||||
fields []describeField
|
||||
}
|
||||
|
||||
func (r *describeValueResult) PrintPlain(printf printfFunc) {
|
||||
var prefix, suffix string
|
||||
if r.constVal != nil {
|
||||
suffix = fmt.Sprintf(" of value %s", r.constVal)
|
||||
}
|
||||
switch obj := r.obj.(type) {
|
||||
case *types.Func:
|
||||
if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
|
||||
if _, ok := recv.Type().Underlying().(*types.Interface); ok {
|
||||
prefix = "interface method "
|
||||
} else {
|
||||
prefix = "method "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Describe the expression.
|
||||
if r.obj != nil {
|
||||
if r.obj.Pos() == r.expr.Pos() {
|
||||
// defining ident
|
||||
printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
|
||||
} else {
|
||||
// referring ident
|
||||
printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
|
||||
if def := r.obj.Pos(); def != token.NoPos {
|
||||
printf(def, "defined here")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
desc := astutil.NodeDescription(r.expr)
|
||||
if suffix != "" {
|
||||
// constant expression
|
||||
printf(r.expr, "%s%s", desc, suffix)
|
||||
} else {
|
||||
// non-constant expression
|
||||
printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ))
|
||||
}
|
||||
}
|
||||
|
||||
printMethods(printf, r.expr, r.methods)
|
||||
printFields(printf, r.expr, r.fields)
|
||||
}
|
||||
|
||||
func (r *describeValueResult) JSON(fset *token.FileSet) []byte {
|
||||
var value, objpos string
|
||||
if r.constVal != nil {
|
||||
value = r.constVal.String()
|
||||
}
|
||||
if r.obj != nil {
|
||||
objpos = fset.Position(r.obj.Pos()).String()
|
||||
}
|
||||
|
||||
return toJSON(&serial.Describe{
|
||||
Desc: astutil.NodeDescription(r.expr),
|
||||
Pos: fset.Position(r.expr.Pos()).String(),
|
||||
Detail: "value",
|
||||
Value: &serial.DescribeValue{
|
||||
Type: r.qpos.typeString(r.typ),
|
||||
Value: value,
|
||||
ObjPos: objpos,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ---- TYPE ------------------------------------------------------------
|
||||
|
||||
func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) {
|
||||
var description string
|
||||
var typ types.Type
|
||||
switch n := path[0].(type) {
|
||||
case *ast.Ident:
|
||||
obj := qpos.info.ObjectOf(n).(*types.TypeName)
|
||||
typ = obj.Type()
|
||||
if isAlias(obj) {
|
||||
description = "alias of "
|
||||
} else if obj.Pos() == n.Pos() {
|
||||
description = "definition of " // (Named type)
|
||||
} else if _, ok := typ.(*types.Basic); ok {
|
||||
description = "reference to built-in "
|
||||
} else {
|
||||
description = "reference to " // (Named type)
|
||||
}
|
||||
|
||||
case ast.Expr:
|
||||
typ = qpos.info.TypeOf(n)
|
||||
|
||||
default:
|
||||
// Unreachable?
|
||||
return nil, fmt.Errorf("unexpected AST for type: %T", n)
|
||||
}
|
||||
|
||||
description = description + "type " + qpos.typeString(typ)
|
||||
|
||||
// Show sizes for structs and named types (it's fairly obvious for others).
|
||||
switch typ.(type) {
|
||||
case *types.Named, *types.Struct:
|
||||
szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64
|
||||
description = fmt.Sprintf("%s (size %d, align %d)", description,
|
||||
szs.Sizeof(typ), szs.Alignof(typ))
|
||||
}
|
||||
|
||||
return &describeTypeResult{
|
||||
qpos: qpos,
|
||||
node: path[0],
|
||||
description: description,
|
||||
typ: typ,
|
||||
methods: accessibleMethods(typ, qpos.info.Pkg),
|
||||
fields: accessibleFields(typ, qpos.info.Pkg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type describeTypeResult struct {
|
||||
qpos *queryPos
|
||||
node ast.Node
|
||||
description string
|
||||
typ types.Type
|
||||
methods []*types.Selection
|
||||
fields []describeField
|
||||
}
|
||||
|
||||
type describeField struct {
|
||||
implicits []*types.Named
|
||||
field *types.Var
|
||||
}
|
||||
|
||||
func printMethods(printf printfFunc, node ast.Node, methods []*types.Selection) {
|
||||
if len(methods) > 0 {
|
||||
printf(node, "Methods:")
|
||||
}
|
||||
for _, meth := range methods {
|
||||
// Print the method type relative to the package
|
||||
// in which it was defined, not the query package,
|
||||
printf(meth.Obj(), "\t%s",
|
||||
types.SelectionString(meth, types.RelativeTo(meth.Obj().Pkg())))
|
||||
}
|
||||
}
|
||||
|
||||
func printFields(printf printfFunc, node ast.Node, fields []describeField) {
|
||||
if len(fields) > 0 {
|
||||
printf(node, "Fields:")
|
||||
}
|
||||
|
||||
// Align the names and the types (requires two passes).
|
||||
var width int
|
||||
var names []string
|
||||
for _, f := range fields {
|
||||
var buf bytes.Buffer
|
||||
for _, fld := range f.implicits {
|
||||
buf.WriteString(fld.Obj().Name())
|
||||
buf.WriteByte('.')
|
||||
}
|
||||
buf.WriteString(f.field.Name())
|
||||
name := buf.String()
|
||||
if n := utf8.RuneCountInString(name); n > width {
|
||||
width = n
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
for i, f := range fields {
|
||||
// Print the field type relative to the package
|
||||
// in which it was defined, not the query package,
|
||||
printf(f.field, "\t%*s %s", -width, names[i],
|
||||
types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg())))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *describeTypeResult) PrintPlain(printf printfFunc) {
|
||||
printf(r.node, "%s", r.description)
|
||||
|
||||
// Show the underlying type for a reference to a named type.
|
||||
if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
|
||||
// TODO(adonovan): improve display of complex struct/interface types.
|
||||
printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying()))
|
||||
}
|
||||
|
||||
printMethods(printf, r.node, r.methods)
|
||||
if len(r.methods) == 0 {
|
||||
// Only report null result for type kinds
|
||||
// capable of bearing methods.
|
||||
switch r.typ.(type) {
|
||||
case *types.Interface, *types.Struct, *types.Named:
|
||||
printf(r.node, "No methods.")
|
||||
}
|
||||
}
|
||||
|
||||
printFields(printf, r.node, r.fields)
|
||||
}
|
||||
|
||||
func (r *describeTypeResult) JSON(fset *token.FileSet) []byte {
|
||||
var namePos, nameDef string
|
||||
if nt, ok := r.typ.(*types.Named); ok {
|
||||
namePos = fset.Position(nt.Obj().Pos()).String()
|
||||
nameDef = nt.Underlying().String()
|
||||
}
|
||||
return toJSON(&serial.Describe{
|
||||
Desc: r.description,
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
Detail: "type",
|
||||
Type: &serial.DescribeType{
|
||||
Type: r.qpos.typeString(r.typ),
|
||||
NamePos: namePos,
|
||||
NameDef: nameDef,
|
||||
Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ---- PACKAGE ------------------------------------------------------------
|
||||
|
||||
func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) {
|
||||
var description string
|
||||
var pkg *types.Package
|
||||
switch n := path[0].(type) {
|
||||
case *ast.ImportSpec:
|
||||
var obj types.Object
|
||||
if n.Name != nil {
|
||||
obj = qpos.info.Defs[n.Name]
|
||||
} else {
|
||||
obj = qpos.info.Implicits[n]
|
||||
}
|
||||
pkgname, _ := obj.(*types.PkgName)
|
||||
if pkgname == nil {
|
||||
return nil, fmt.Errorf("can't import package %s", n.Path.Value)
|
||||
}
|
||||
pkg = pkgname.Imported()
|
||||
description = fmt.Sprintf("import of package %q", pkg.Path())
|
||||
|
||||
case *ast.Ident:
|
||||
if _, isDef := path[1].(*ast.File); isDef {
|
||||
// e.g. package id
|
||||
pkg = qpos.info.Pkg
|
||||
description = fmt.Sprintf("definition of package %q", pkg.Path())
|
||||
} else {
|
||||
// e.g. import id "..."
|
||||
// or id.F()
|
||||
pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported()
|
||||
description = fmt.Sprintf("reference to package %q", pkg.Path())
|
||||
}
|
||||
|
||||
default:
|
||||
// Unreachable?
|
||||
return nil, fmt.Errorf("unexpected AST for package: %T", n)
|
||||
}
|
||||
|
||||
var members []*describeMember
|
||||
// NB: "unsafe" has no types.Package
|
||||
if pkg != nil {
|
||||
// Enumerate the accessible package members
|
||||
// in lexicographic order.
|
||||
for _, name := range pkg.Scope().Names() {
|
||||
if pkg == qpos.info.Pkg || ast.IsExported(name) {
|
||||
mem := pkg.Scope().Lookup(name)
|
||||
var methods []*types.Selection
|
||||
if mem, ok := mem.(*types.TypeName); ok {
|
||||
methods = accessibleMethods(mem.Type(), qpos.info.Pkg)
|
||||
}
|
||||
members = append(members, &describeMember{
|
||||
mem,
|
||||
methods,
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil
|
||||
}
|
||||
|
||||
type describePackageResult struct {
|
||||
fset *token.FileSet
|
||||
node ast.Node
|
||||
description string
|
||||
pkg *types.Package
|
||||
members []*describeMember // in lexicographic name order
|
||||
}
|
||||
|
||||
type describeMember struct {
|
||||
obj types.Object
|
||||
methods []*types.Selection // in types.MethodSet order
|
||||
}
|
||||
|
||||
func (r *describePackageResult) PrintPlain(printf printfFunc) {
|
||||
printf(r.node, "%s", r.description)
|
||||
|
||||
// Compute max width of name "column".
|
||||
maxname := 0
|
||||
for _, mem := range r.members {
|
||||
if l := len(mem.obj.Name()); l > maxname {
|
||||
maxname = l
|
||||
}
|
||||
}
|
||||
|
||||
for _, mem := range r.members {
|
||||
printf(mem.obj, "\t%s", formatMember(mem.obj, maxname))
|
||||
for _, meth := range mem.methods {
|
||||
printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatMember(obj types.Object, maxname int) string {
|
||||
qualifier := types.RelativeTo(obj.Pkg())
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
|
||||
switch obj := obj.(type) {
|
||||
case *types.Const:
|
||||
fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val())
|
||||
|
||||
case *types.Func:
|
||||
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
|
||||
|
||||
case *types.TypeName:
|
||||
typ := obj.Type()
|
||||
if isAlias(obj) {
|
||||
buf.WriteString(" = ")
|
||||
} else {
|
||||
buf.WriteByte(' ')
|
||||
typ = typ.Underlying()
|
||||
}
|
||||
var typestr string
|
||||
// Abbreviate long aggregate type names.
|
||||
switch typ := typ.(type) {
|
||||
case *types.Interface:
|
||||
if typ.NumMethods() > 1 {
|
||||
typestr = "interface{...}"
|
||||
}
|
||||
case *types.Struct:
|
||||
if typ.NumFields() > 1 {
|
||||
typestr = "struct{...}"
|
||||
}
|
||||
}
|
||||
if typestr == "" {
|
||||
typestr = types.TypeString(typ, qualifier)
|
||||
}
|
||||
buf.WriteString(typestr)
|
||||
|
||||
case *types.Var:
|
||||
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (r *describePackageResult) JSON(fset *token.FileSet) []byte {
|
||||
var members []*serial.DescribeMember
|
||||
for _, mem := range r.members {
|
||||
obj := mem.obj
|
||||
typ := obj.Type()
|
||||
var val string
|
||||
var alias string
|
||||
switch obj := obj.(type) {
|
||||
case *types.Const:
|
||||
val = obj.Val().String()
|
||||
case *types.TypeName:
|
||||
if isAlias(obj) {
|
||||
alias = "= " // kludgy
|
||||
} else {
|
||||
typ = typ.Underlying()
|
||||
}
|
||||
}
|
||||
members = append(members, &serial.DescribeMember{
|
||||
Name: obj.Name(),
|
||||
Type: alias + typ.String(),
|
||||
Value: val,
|
||||
Pos: fset.Position(obj.Pos()).String(),
|
||||
Kind: tokenOf(obj),
|
||||
Methods: methodsToSerial(r.pkg, mem.methods, fset),
|
||||
})
|
||||
}
|
||||
return toJSON(&serial.Describe{
|
||||
Desc: r.description,
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
Detail: "package",
|
||||
Package: &serial.DescribePackage{
|
||||
Path: r.pkg.Path(),
|
||||
Members: members,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func tokenOf(o types.Object) string {
|
||||
switch o.(type) {
|
||||
case *types.Func:
|
||||
return "func"
|
||||
case *types.Var:
|
||||
return "var"
|
||||
case *types.TypeName:
|
||||
return "type"
|
||||
case *types.Const:
|
||||
return "const"
|
||||
case *types.PkgName:
|
||||
return "package"
|
||||
case *types.Builtin:
|
||||
return "builtin" // e.g. when describing package "unsafe"
|
||||
case *types.Nil:
|
||||
return "nil"
|
||||
case *types.Label:
|
||||
return "label"
|
||||
}
|
||||
panic(o)
|
||||
}
|
||||
|
||||
// ---- STATEMENT ------------------------------------------------------------
|
||||
|
||||
func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) {
|
||||
var description string
|
||||
switch n := path[0].(type) {
|
||||
case *ast.Ident:
|
||||
if qpos.info.Defs[n] != nil {
|
||||
description = "labelled statement"
|
||||
} else {
|
||||
description = "reference to labelled statement"
|
||||
}
|
||||
|
||||
default:
|
||||
// Nothing much to say about statements.
|
||||
description = astutil.NodeDescription(n)
|
||||
}
|
||||
return &describeStmtResult{qpos.fset, path[0], description}, nil
|
||||
}
|
||||
|
||||
type describeStmtResult struct {
|
||||
fset *token.FileSet
|
||||
node ast.Node
|
||||
description string
|
||||
}
|
||||
|
||||
func (r *describeStmtResult) PrintPlain(printf printfFunc) {
|
||||
printf(r.node, "%s", r.description)
|
||||
}
|
||||
|
||||
func (r *describeStmtResult) JSON(fset *token.FileSet) []byte {
|
||||
return toJSON(&serial.Describe{
|
||||
Desc: r.description,
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
Detail: "unknown",
|
||||
})
|
||||
}
|
||||
|
||||
// ------------------- Utilities -------------------
|
||||
|
||||
// pathToString returns a string containing the concrete types of the
|
||||
// nodes in path.
|
||||
func pathToString(path []ast.Node) string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "[")
|
||||
for i, n := range path {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&buf, " ")
|
||||
}
|
||||
fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
|
||||
}
|
||||
fmt.Fprint(&buf, "]")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
|
||||
var methods []*types.Selection
|
||||
for _, meth := range typeutil.IntuitiveMethodSet(t, nil) {
|
||||
if isAccessibleFrom(meth.Obj(), from) {
|
||||
methods = append(methods, meth)
|
||||
}
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
// accessibleFields returns the set of accessible
|
||||
// field selections on a value of type recv.
|
||||
func accessibleFields(recv types.Type, from *types.Package) []describeField {
|
||||
wantField := func(f *types.Var) bool {
|
||||
if !isAccessibleFrom(f, from) {
|
||||
return false
|
||||
}
|
||||
// Check that the field is not shadowed.
|
||||
obj, _, _ := types.LookupFieldOrMethod(recv, true, f.Pkg(), f.Name())
|
||||
return obj == f
|
||||
}
|
||||
|
||||
var fields []describeField
|
||||
var visit func(t types.Type, stack []*types.Named)
|
||||
visit = func(t types.Type, stack []*types.Named) {
|
||||
tStruct, ok := deref(t).Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
fieldloop:
|
||||
for i := 0; i < tStruct.NumFields(); i++ {
|
||||
f := tStruct.Field(i)
|
||||
|
||||
// Handle recursion through anonymous fields.
|
||||
if f.Anonymous() {
|
||||
tf := f.Type()
|
||||
if ptr, ok := tf.(*types.Pointer); ok {
|
||||
tf = ptr.Elem()
|
||||
}
|
||||
if named, ok := tf.(*types.Named); ok { // (be defensive)
|
||||
// If we've already visited this named type
|
||||
// on this path, break the cycle.
|
||||
for _, x := range stack {
|
||||
if x == named {
|
||||
continue fieldloop
|
||||
}
|
||||
}
|
||||
visit(f.Type(), append(stack, named))
|
||||
}
|
||||
}
|
||||
|
||||
// Save accessible fields.
|
||||
if wantField(f) {
|
||||
fields = append(fields, describeField{
|
||||
implicits: append([]*types.Named(nil), stack...),
|
||||
field: f,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(recv, nil)
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
|
||||
return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
|
||||
}
|
||||
|
||||
func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
|
||||
qualifier := types.RelativeTo(this)
|
||||
var jmethods []serial.DescribeMethod
|
||||
for _, meth := range methods {
|
||||
var ser serial.DescribeMethod
|
||||
if meth != nil { // may contain nils when called by implements (on a method)
|
||||
ser = serial.DescribeMethod{
|
||||
Name: types.SelectionString(meth, qualifier),
|
||||
Pos: fset.Position(meth.Obj().Pos()).String(),
|
||||
}
|
||||
}
|
||||
jmethods = append(jmethods, ser)
|
||||
}
|
||||
return jmethods
|
||||
}
|
223
vendor/golang.org/x/tools/cmd/guru/freevars.go
generated
vendored
Normal file
223
vendor/golang.org/x/tools/cmd/guru/freevars.go
generated
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
// freevars displays the lexical (not package-level) free variables of
|
||||
// the selection.
|
||||
//
|
||||
// It treats A.B.C as a separate variable from A to reveal the parts
|
||||
// of an aggregate type that are actually needed.
|
||||
// This aids refactoring.
|
||||
//
|
||||
// TODO(adonovan): optionally display the free references to
|
||||
// file/package scope objects, and to objects from other packages.
|
||||
// Depending on where the resulting function abstraction will go,
|
||||
// these might be interesting. Perhaps group the results into three
|
||||
// bands.
|
||||
//
|
||||
func freevars(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := lconf.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file := qpos.path[len(qpos.path)-1] // the enclosing file
|
||||
fileScope := qpos.info.Scopes[file]
|
||||
pkgScope := fileScope.Parent()
|
||||
|
||||
// The id and sel functions return non-nil if they denote an
|
||||
// object o or selection o.x.y that is referenced by the
|
||||
// selection but defined neither within the selection nor at
|
||||
// file scope, i.e. it is in the lexical environment.
|
||||
var id func(n *ast.Ident) types.Object
|
||||
var sel func(n *ast.SelectorExpr) types.Object
|
||||
|
||||
sel = func(n *ast.SelectorExpr) types.Object {
|
||||
switch x := unparen(n.X).(type) {
|
||||
case *ast.SelectorExpr:
|
||||
return sel(x)
|
||||
case *ast.Ident:
|
||||
return id(x)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
id = func(n *ast.Ident) types.Object {
|
||||
obj := qpos.info.Uses[n]
|
||||
if obj == nil {
|
||||
return nil // not a reference
|
||||
}
|
||||
if _, ok := obj.(*types.PkgName); ok {
|
||||
return nil // imported package
|
||||
}
|
||||
if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) {
|
||||
return nil // not defined in this file
|
||||
}
|
||||
scope := obj.Parent()
|
||||
if scope == nil {
|
||||
return nil // e.g. interface method, struct field
|
||||
}
|
||||
if scope == fileScope || scope == pkgScope {
|
||||
return nil // defined at file or package scope
|
||||
}
|
||||
if qpos.start <= obj.Pos() && obj.Pos() <= qpos.end {
|
||||
return nil // defined within selection => not free
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// Maps each reference that is free in the selection
|
||||
// to the object it refers to.
|
||||
// The map de-duplicates repeated references.
|
||||
refsMap := make(map[string]freevarsRef)
|
||||
|
||||
// Visit all the identifiers in the selected ASTs.
|
||||
ast.Inspect(qpos.path[0], func(n ast.Node) bool {
|
||||
if n == nil {
|
||||
return true // popping DFS stack
|
||||
}
|
||||
|
||||
// Is this node contained within the selection?
|
||||
// (freevars permits inexact selections,
|
||||
// like two stmts in a block.)
|
||||
if qpos.start <= n.Pos() && n.End() <= qpos.end {
|
||||
var obj types.Object
|
||||
var prune bool
|
||||
switch n := n.(type) {
|
||||
case *ast.Ident:
|
||||
obj = id(n)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
obj = sel(n)
|
||||
prune = true
|
||||
}
|
||||
|
||||
if obj != nil {
|
||||
var kind string
|
||||
switch obj.(type) {
|
||||
case *types.Var:
|
||||
kind = "var"
|
||||
case *types.Func:
|
||||
kind = "func"
|
||||
case *types.TypeName:
|
||||
kind = "type"
|
||||
case *types.Const:
|
||||
kind = "const"
|
||||
case *types.Label:
|
||||
kind = "label"
|
||||
default:
|
||||
panic(obj)
|
||||
}
|
||||
|
||||
typ := qpos.info.TypeOf(n.(ast.Expr))
|
||||
ref := freevarsRef{kind, printNode(lprog.Fset, n), typ, obj}
|
||||
refsMap[ref.ref] = ref
|
||||
|
||||
if prune {
|
||||
return false // don't descend
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true // descend
|
||||
})
|
||||
|
||||
refs := make([]freevarsRef, 0, len(refsMap))
|
||||
for _, ref := range refsMap {
|
||||
refs = append(refs, ref)
|
||||
}
|
||||
sort.Sort(byRef(refs))
|
||||
|
||||
q.Output(lprog.Fset, &freevarsResult{
|
||||
qpos: qpos,
|
||||
refs: refs,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type freevarsResult struct {
|
||||
qpos *queryPos
|
||||
refs []freevarsRef
|
||||
}
|
||||
|
||||
type freevarsRef struct {
|
||||
kind string
|
||||
ref string
|
||||
typ types.Type
|
||||
obj types.Object
|
||||
}
|
||||
|
||||
func (r *freevarsResult) PrintPlain(printf printfFunc) {
|
||||
if len(r.refs) == 0 {
|
||||
printf(r.qpos, "No free identifiers.")
|
||||
} else {
|
||||
printf(r.qpos, "Free identifiers:")
|
||||
qualifier := types.RelativeTo(r.qpos.info.Pkg)
|
||||
for _, ref := range r.refs {
|
||||
// Avoid printing "type T T".
|
||||
var typstr string
|
||||
if ref.kind != "type" && ref.kind != "label" {
|
||||
typstr = " " + types.TypeString(ref.typ, qualifier)
|
||||
}
|
||||
printf(ref.obj, "%s %s%s", ref.kind, ref.ref, typstr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *freevarsResult) JSON(fset *token.FileSet) []byte {
|
||||
var buf bytes.Buffer
|
||||
for i, ref := range r.refs {
|
||||
if i > 0 {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
buf.Write(toJSON(serial.FreeVar{
|
||||
Pos: fset.Position(ref.obj.Pos()).String(),
|
||||
Kind: ref.kind,
|
||||
Ref: ref.ref,
|
||||
Type: ref.typ.String(),
|
||||
}))
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// -------- utils --------
|
||||
|
||||
type byRef []freevarsRef
|
||||
|
||||
func (p byRef) Len() int { return len(p) }
|
||||
func (p byRef) Less(i, j int) bool { return p[i].ref < p[j].ref }
|
||||
func (p byRef) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
|
||||
// printNode returns the pretty-printed syntax of n.
|
||||
func printNode(fset *token.FileSet, n ast.Node) string {
|
||||
var buf bytes.Buffer
|
||||
printer.Fprint(&buf, fset, n)
|
||||
return buf.String()
|
||||
}
|
401
vendor/golang.org/x/tools/cmd/guru/guru.go
generated
vendored
Normal file
401
vendor/golang.org/x/tools/cmd/guru/guru.go
generated
vendored
Normal file
@@ -0,0 +1,401 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// TODO(adonovan): new queries
|
||||
// - show all statements that may update the selected lvalue
|
||||
// (local, global, field, etc).
|
||||
// - show all places where an object of type T is created
|
||||
// (&T{}, var t T, new(T), new(struct{array [3]T}), etc.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/pointer"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
type printfFunc func(pos interface{}, format string, args ...interface{})
|
||||
|
||||
// A QueryResult is an item of output. Each query produces a stream of
|
||||
// query results, calling Query.Output for each one.
|
||||
type QueryResult interface {
|
||||
// JSON returns the QueryResult in JSON form.
|
||||
JSON(fset *token.FileSet) []byte
|
||||
|
||||
// PrintPlain prints the QueryResult in plain text form.
|
||||
// The implementation calls printfFunc to print each line of output.
|
||||
PrintPlain(printf printfFunc)
|
||||
}
|
||||
|
||||
// A QueryPos represents the position provided as input to a query:
|
||||
// a textual extent in the program's source code, the AST node it
|
||||
// corresponds to, and the package to which it belongs.
|
||||
// Instances are created by parseQueryPos.
|
||||
type queryPos struct {
|
||||
fset *token.FileSet
|
||||
start, end token.Pos // source extent of query
|
||||
path []ast.Node // AST path from query node to root of ast.File
|
||||
exact bool // 2nd result of PathEnclosingInterval
|
||||
info *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
|
||||
}
|
||||
|
||||
// TypeString prints type T relative to the query position.
|
||||
func (qpos *queryPos) typeString(T types.Type) string {
|
||||
return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
|
||||
}
|
||||
|
||||
// ObjectString prints object obj relative to the query position.
|
||||
func (qpos *queryPos) objectString(obj types.Object) string {
|
||||
return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
|
||||
}
|
||||
|
||||
// A Query specifies a single guru query.
|
||||
type Query struct {
|
||||
Pos string // query position
|
||||
Build *build.Context // package loading configuration
|
||||
|
||||
// pointer analysis options
|
||||
Scope []string // main packages in (*loader.Config).FromArgs syntax
|
||||
PTALog io.Writer // (optional) pointer-analysis log file
|
||||
Reflection bool // model reflection soundly (currently slow).
|
||||
|
||||
// result-printing function, safe for concurrent use
|
||||
Output func(*token.FileSet, QueryResult)
|
||||
}
|
||||
|
||||
// Run runs an guru query and populates its Fset and Result.
|
||||
func Run(mode string, q *Query) error {
|
||||
switch mode {
|
||||
case "callees":
|
||||
return callees(q)
|
||||
case "callers":
|
||||
return callers(q)
|
||||
case "callstack":
|
||||
return callstack(q)
|
||||
case "peers":
|
||||
return peers(q)
|
||||
case "pointsto":
|
||||
return pointsto(q)
|
||||
case "whicherrs":
|
||||
return whicherrs(q)
|
||||
case "definition":
|
||||
return definition(q)
|
||||
case "describe":
|
||||
return describe(q)
|
||||
case "freevars":
|
||||
return freevars(q)
|
||||
case "implements":
|
||||
return implements(q)
|
||||
case "referrers":
|
||||
return referrers(q)
|
||||
case "what":
|
||||
return what(q)
|
||||
default:
|
||||
return fmt.Errorf("invalid mode: %q", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func setPTAScope(lconf *loader.Config, scope []string) error {
|
||||
pkgs := buildutil.ExpandPatterns(lconf.Build, scope)
|
||||
if len(pkgs) == 0 {
|
||||
return fmt.Errorf("no packages specified for pointer analysis scope")
|
||||
}
|
||||
// The value of each entry in pkgs is true,
|
||||
// giving ImportWithTests (not Import) semantics.
|
||||
lconf.ImportPkgs = pkgs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a pointer.Config whose scope is the initial packages of lprog
|
||||
// and their dependencies.
|
||||
func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
|
||||
// For each initial package (specified on the command line),
|
||||
// if it has a main function, analyze that,
|
||||
// otherwise analyze its tests, if any.
|
||||
var mains []*ssa.Package
|
||||
for _, info := range lprog.InitialPackages() {
|
||||
p := prog.Package(info.Pkg)
|
||||
|
||||
// Add package to the pointer analysis scope.
|
||||
if p.Pkg.Name() == "main" && p.Func("main") != nil {
|
||||
mains = append(mains, p)
|
||||
} else if main := prog.CreateTestMainPackage(p); main != nil {
|
||||
mains = append(mains, main)
|
||||
}
|
||||
}
|
||||
if mains == nil {
|
||||
return nil, fmt.Errorf("analysis scope has no main and no tests")
|
||||
}
|
||||
return &pointer.Config{
|
||||
Log: ptaLog,
|
||||
Reflection: reflection,
|
||||
Mains: mains,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// importQueryPackage finds the package P containing the
|
||||
// query position and tells conf to import it.
|
||||
// It returns the package's path.
|
||||
func importQueryPackage(pos string, conf *loader.Config) (string, error) {
|
||||
fqpos, err := fastQueryPos(conf.Build, pos)
|
||||
if err != nil {
|
||||
return "", err // bad query
|
||||
}
|
||||
filename := fqpos.fset.File(fqpos.start).Name()
|
||||
|
||||
_, importPath, err := guessImportPath(filename, conf.Build)
|
||||
if err != nil {
|
||||
// Can't find GOPATH dir.
|
||||
// Treat the query file as its own package.
|
||||
importPath = "command-line-arguments"
|
||||
conf.CreateFromFilenames(importPath, filename)
|
||||
} else {
|
||||
// Check that it's possible to load the queried package.
|
||||
// (e.g. guru tests contain different 'package' decls in same dir.)
|
||||
// Keep consistent with logic in loader/util.go!
|
||||
cfg2 := *conf.Build
|
||||
cfg2.CgoEnabled = false
|
||||
bp, err := cfg2.Import(importPath, "", 0)
|
||||
if err != nil {
|
||||
return "", err // no files for package
|
||||
}
|
||||
|
||||
switch pkgContainsFile(bp, filename) {
|
||||
case 'T':
|
||||
conf.ImportWithTests(importPath)
|
||||
case 'X':
|
||||
conf.ImportWithTests(importPath)
|
||||
importPath += "_test" // for TypeCheckFuncBodies
|
||||
case 'G':
|
||||
conf.Import(importPath)
|
||||
default:
|
||||
// This happens for ad-hoc packages like
|
||||
// $GOROOT/src/net/http/triv.go.
|
||||
return "", fmt.Errorf("package %q doesn't contain file %s",
|
||||
importPath, filename)
|
||||
}
|
||||
}
|
||||
|
||||
conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
|
||||
|
||||
return importPath, nil
|
||||
}
|
||||
|
||||
// pkgContainsFile reports whether file was among the packages Go
|
||||
// files, Test files, eXternal test files, or not found.
|
||||
func pkgContainsFile(bp *build.Package, filename string) byte {
|
||||
for i, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} {
|
||||
for _, file := range files {
|
||||
if sameFile(filepath.Join(bp.Dir, file), filename) {
|
||||
return "GTX"[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0 // not found
|
||||
}
|
||||
|
||||
// ParseQueryPos parses the source query position pos and returns the
|
||||
// AST node of the loaded program lprog that it identifies.
|
||||
// If needExact, it must identify a single AST subtree;
|
||||
// this is appropriate for queries that allow fairly arbitrary syntax,
|
||||
// e.g. "describe".
|
||||
//
|
||||
func parseQueryPos(lprog *loader.Program, pos string, needExact bool) (*queryPos, error) {
|
||||
filename, startOffset, endOffset, err := parsePos(pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find the named file among those in the loaded program.
|
||||
var file *token.File
|
||||
lprog.Fset.Iterate(func(f *token.File) bool {
|
||||
if sameFile(filename, f.Name()) {
|
||||
file = f
|
||||
return false // done
|
||||
}
|
||||
return true // continue
|
||||
})
|
||||
if file == nil {
|
||||
return nil, fmt.Errorf("file %s not found in loaded program", filename)
|
||||
}
|
||||
|
||||
start, end, err := fileOffsetToPos(file, startOffset, endOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info, path, exact := lprog.PathEnclosingInterval(start, end)
|
||||
if path == nil {
|
||||
return nil, fmt.Errorf("no syntax here")
|
||||
}
|
||||
if needExact && !exact {
|
||||
return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
|
||||
}
|
||||
return &queryPos{lprog.Fset, start, end, path, exact, info}, nil
|
||||
}
|
||||
|
||||
// ---------- Utilities ----------
|
||||
|
||||
// loadWithSoftErrors calls lconf.Load, suppressing "soft" errors. (See Go issue 16530.)
|
||||
// TODO(adonovan): Once the loader has an option to allow soft errors,
|
||||
// replace calls to loadWithSoftErrors with loader calls with that parameter.
|
||||
func loadWithSoftErrors(lconf *loader.Config) (*loader.Program, error) {
|
||||
lconf.AllowErrors = true
|
||||
|
||||
// Ideally we would just return conf.Load() here, but go/types
|
||||
// reports certain "soft" errors that gc does not (Go issue 14596).
|
||||
// As a workaround, we set AllowErrors=true and then duplicate
|
||||
// the loader's error checking but allow soft errors.
|
||||
// It would be nice if the loader API permitted "AllowErrors: soft".
|
||||
prog, err := lconf.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errpkgs []string
|
||||
// Report hard errors in indirectly imported packages.
|
||||
for _, info := range prog.AllPackages {
|
||||
if containsHardErrors(info.Errors) {
|
||||
errpkgs = append(errpkgs, info.Pkg.Path())
|
||||
} else {
|
||||
// Enable SSA construction for packages containing only soft errors.
|
||||
info.TransitivelyErrorFree = true
|
||||
}
|
||||
}
|
||||
if errpkgs != nil {
|
||||
var more string
|
||||
if len(errpkgs) > 3 {
|
||||
more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
|
||||
errpkgs = errpkgs[:3]
|
||||
}
|
||||
return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
|
||||
strings.Join(errpkgs, ", "), more)
|
||||
}
|
||||
return prog, err
|
||||
}
|
||||
|
||||
func containsHardErrors(errors []error) bool {
|
||||
for _, err := range errors {
|
||||
if err, ok := err.(types.Error); ok && err.Soft {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// allowErrors causes type errors to be silently ignored.
|
||||
// (Not suitable if SSA construction follows.)
|
||||
func allowErrors(lconf *loader.Config) {
|
||||
ctxt := *lconf.Build // copy
|
||||
ctxt.CgoEnabled = false
|
||||
lconf.Build = &ctxt
|
||||
lconf.AllowErrors = true
|
||||
// AllErrors makes the parser always return an AST instead of
|
||||
// bailing out after 10 errors and returning an empty ast.File.
|
||||
lconf.ParserMode = parser.AllErrors
|
||||
lconf.TypeChecker.Error = func(err error) {}
|
||||
}
|
||||
|
||||
// ptrAnalysis runs the pointer analysis and returns its result.
|
||||
func ptrAnalysis(conf *pointer.Config) *pointer.Result {
|
||||
result, err := pointer.Analyze(conf)
|
||||
if err != nil {
|
||||
panic(err) // pointer analysis internal error
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
|
||||
|
||||
// deref returns a pointer's element type; otherwise it returns typ.
|
||||
func deref(typ types.Type) types.Type {
|
||||
if p, ok := typ.Underlying().(*types.Pointer); ok {
|
||||
return p.Elem()
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// fprintf prints to w a message of the form "location: message\n"
|
||||
// where location is derived from pos.
|
||||
//
|
||||
// pos must be one of:
|
||||
// - a token.Pos, denoting a position
|
||||
// - an ast.Node, denoting an interval
|
||||
// - anything with a Pos() method:
|
||||
// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
|
||||
// - a QueryPos, denoting the extent of the user's query.
|
||||
// - nil, meaning no position at all.
|
||||
//
|
||||
// The output format is is compatible with the 'gnu'
|
||||
// compilation-error-regexp in Emacs' compilation mode.
|
||||
//
|
||||
func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
|
||||
var start, end token.Pos
|
||||
switch pos := pos.(type) {
|
||||
case ast.Node:
|
||||
start = pos.Pos()
|
||||
end = pos.End()
|
||||
case token.Pos:
|
||||
start = pos
|
||||
end = start
|
||||
case *types.PkgName:
|
||||
// The Pos of most PkgName objects does not coincide with an identifier,
|
||||
// so we suppress the usual start+len(name) heuristic for types.Objects.
|
||||
start = pos.Pos()
|
||||
end = start
|
||||
case types.Object:
|
||||
start = pos.Pos()
|
||||
end = start + token.Pos(len(pos.Name())) // heuristic
|
||||
case interface {
|
||||
Pos() token.Pos
|
||||
}:
|
||||
start = pos.Pos()
|
||||
end = start
|
||||
case *queryPos:
|
||||
start = pos.start
|
||||
end = pos.end
|
||||
case nil:
|
||||
// no-op
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid pos: %T", pos))
|
||||
}
|
||||
|
||||
if sp := fset.Position(start); start == end {
|
||||
// (prints "-: " for token.NoPos)
|
||||
fmt.Fprintf(w, "%s: ", sp)
|
||||
} else {
|
||||
ep := fset.Position(end)
|
||||
// The -1 below is a concession to Emacs's broken use of
|
||||
// inclusive (not half-open) intervals.
|
||||
// Other editors may not want it.
|
||||
// TODO(adonovan): add an -editor=vim|emacs|acme|auto
|
||||
// flag; auto uses EMACS=t / VIM=... / etc env vars.
|
||||
fmt.Fprintf(w, "%s:%d.%d-%d.%d: ",
|
||||
sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1)
|
||||
}
|
||||
fmt.Fprintf(w, format, args...)
|
||||
io.WriteString(w, "\n")
|
||||
}
|
||||
|
||||
func toJSON(x interface{}) []byte {
|
||||
b, err := json.MarshalIndent(x, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatalf("JSON error: %v", err)
|
||||
}
|
||||
return b
|
||||
}
|
343
vendor/golang.org/x/tools/cmd/guru/guru_test.go
generated
vendored
Normal file
343
vendor/golang.org/x/tools/cmd/guru/guru_test.go
generated
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main_test
|
||||
|
||||
// This file defines a test framework for guru queries.
|
||||
//
|
||||
// The files beneath testdata/src contain Go programs containing
|
||||
// query annotations of the form:
|
||||
//
|
||||
// @verb id "select"
|
||||
//
|
||||
// where verb is the query mode (e.g. "callers"), id is a unique name
|
||||
// for this query, and "select" is a regular expression matching the
|
||||
// substring of the current line that is the query's input selection.
|
||||
//
|
||||
// The expected output for each query is provided in the accompanying
|
||||
// .golden file.
|
||||
//
|
||||
// (Location information is not included because it's too fragile to
|
||||
// display as text. TODO(adonovan): think about how we can test its
|
||||
// correctness, since it is critical information.)
|
||||
//
|
||||
// Run this test with:
|
||||
// % go test golang.org/x/tools/cmd/guru -update
|
||||
// to update the golden files.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
guru "golang.org/x/tools/cmd/guru"
|
||||
)
|
||||
|
||||
var updateFlag = flag.Bool("update", false, "Update the golden files.")
|
||||
|
||||
type query struct {
|
||||
id string // unique id
|
||||
verb string // query mode, e.g. "callees"
|
||||
posn token.Position // query position
|
||||
filename string
|
||||
queryPos string // query position in command-line syntax
|
||||
}
|
||||
|
||||
func parseRegexp(text string) (*regexp.Regexp, error) {
|
||||
pattern, err := strconv.Unquote(text)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't unquote %s", text)
|
||||
}
|
||||
return regexp.Compile(pattern)
|
||||
}
|
||||
|
||||
// parseQueries parses and returns the queries in the named file.
|
||||
func parseQueries(t *testing.T, filename string) []*query {
|
||||
filedata, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Parse the file once to discover the test queries.
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, filename, filedata, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
lines := bytes.Split(filedata, []byte("\n"))
|
||||
|
||||
var queries []*query
|
||||
queriesById := make(map[string]*query)
|
||||
|
||||
// Find all annotations of these forms:
|
||||
expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp"
|
||||
for _, c := range f.Comments {
|
||||
text := strings.TrimSpace(c.Text())
|
||||
if text == "" || text[0] != '@' {
|
||||
continue
|
||||
}
|
||||
posn := fset.Position(c.Pos())
|
||||
|
||||
// @verb id "regexp"
|
||||
match := expectRe.FindStringSubmatch(text)
|
||||
if match == nil {
|
||||
t.Errorf("%s: ill-formed query: %s", posn, text)
|
||||
continue
|
||||
}
|
||||
|
||||
id := match[2]
|
||||
if prev, ok := queriesById[id]; ok {
|
||||
t.Errorf("%s: duplicate id %s", posn, id)
|
||||
t.Errorf("%s: previously used here", prev.posn)
|
||||
continue
|
||||
}
|
||||
|
||||
q := &query{
|
||||
id: id,
|
||||
verb: match[1],
|
||||
filename: filename,
|
||||
posn: posn,
|
||||
}
|
||||
|
||||
if match[3] != `"nopos"` {
|
||||
selectRe, err := parseRegexp(match[3])
|
||||
if err != nil {
|
||||
t.Errorf("%s: %s", posn, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Find text of the current line, sans query.
|
||||
// (Queries must be // not /**/ comments.)
|
||||
line := lines[posn.Line-1][:posn.Column-1]
|
||||
|
||||
// Apply regexp to current line to find input selection.
|
||||
loc := selectRe.FindIndex(line)
|
||||
if loc == nil {
|
||||
t.Errorf("%s: selection pattern %s doesn't match line %q",
|
||||
posn, match[3], string(line))
|
||||
continue
|
||||
}
|
||||
|
||||
// Assumes ASCII. TODO(adonovan): test on UTF-8.
|
||||
linestart := posn.Offset - (posn.Column - 1)
|
||||
|
||||
// Compute the file offsets.
|
||||
q.queryPos = fmt.Sprintf("%s:#%d,#%d",
|
||||
filename, linestart+loc[0], linestart+loc[1])
|
||||
}
|
||||
|
||||
queries = append(queries, q)
|
||||
queriesById[id] = q
|
||||
}
|
||||
|
||||
// Return the slice, not map, for deterministic iteration.
|
||||
return queries
|
||||
}
|
||||
|
||||
// doQuery poses query q to the guru and writes its response and
|
||||
// error (if any) to out.
|
||||
func doQuery(out io.Writer, q *query, json bool) {
|
||||
fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)
|
||||
|
||||
var buildContext = build.Default
|
||||
buildContext.GOPATH = "testdata"
|
||||
pkg := filepath.Dir(strings.TrimPrefix(q.filename, "testdata/src/"))
|
||||
|
||||
gopathAbs, _ := filepath.Abs(buildContext.GOPATH)
|
||||
|
||||
var outputMu sync.Mutex // guards outputs
|
||||
var outputs []string // JSON objects or lines of text
|
||||
outputFn := func(fset *token.FileSet, qr guru.QueryResult) {
|
||||
outputMu.Lock()
|
||||
defer outputMu.Unlock()
|
||||
if json {
|
||||
jsonstr := string(qr.JSON(fset))
|
||||
// Sanitize any absolute filenames that creep in.
|
||||
jsonstr = strings.Replace(jsonstr, gopathAbs, "$GOPATH", -1)
|
||||
outputs = append(outputs, jsonstr)
|
||||
} else {
|
||||
// suppress position information
|
||||
qr.PrintPlain(func(_ interface{}, format string, args ...interface{}) {
|
||||
outputs = append(outputs, fmt.Sprintf(format, args...))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
query := guru.Query{
|
||||
Pos: q.queryPos,
|
||||
Build: &buildContext,
|
||||
Scope: []string{pkg},
|
||||
Reflection: true,
|
||||
Output: outputFn,
|
||||
}
|
||||
|
||||
if err := guru.Run(q.verb, &query); err != nil {
|
||||
fmt.Fprintf(out, "\nError: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// In a "referrers" query, references are sorted within each
|
||||
// package but packages are visited in arbitrary order,
|
||||
// so for determinism we sort them. Line 0 is a caption.
|
||||
if q.verb == "referrers" {
|
||||
sort.Strings(outputs[1:])
|
||||
}
|
||||
|
||||
for _, output := range outputs {
|
||||
fmt.Fprintf(out, "%s\n", output)
|
||||
}
|
||||
|
||||
if !json {
|
||||
io.WriteString(out, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuru(t *testing.T) {
|
||||
if testing.Short() {
|
||||
// These tests are super slow.
|
||||
// TODO: make a lighter version of the tests for short mode?
|
||||
t.Skipf("skipping in short mode")
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
t.Skipf("skipping test on %q (no testdata dir)", runtime.GOOS)
|
||||
case "windows":
|
||||
t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
|
||||
}
|
||||
|
||||
for _, filename := range []string{
|
||||
"testdata/src/alias/alias.go", // iff guru.HasAlias (go1.9)
|
||||
"testdata/src/calls/main.go",
|
||||
"testdata/src/describe/main.go",
|
||||
"testdata/src/describe/main19.go", // iff go1.9
|
||||
"testdata/src/freevars/main.go",
|
||||
"testdata/src/implements/main.go",
|
||||
"testdata/src/implements-methods/main.go",
|
||||
"testdata/src/imports/main.go",
|
||||
"testdata/src/peers/main.go",
|
||||
"testdata/src/pointsto/main.go",
|
||||
"testdata/src/referrers/main.go",
|
||||
"testdata/src/reflection/main.go",
|
||||
"testdata/src/what/main.go",
|
||||
"testdata/src/whicherrs/main.go",
|
||||
"testdata/src/softerrs/main.go",
|
||||
// JSON:
|
||||
// TODO(adonovan): most of these are very similar; combine them.
|
||||
"testdata/src/calls-json/main.go",
|
||||
"testdata/src/peers-json/main.go",
|
||||
"testdata/src/definition-json/main.go",
|
||||
"testdata/src/definition-json/main19.go",
|
||||
"testdata/src/describe-json/main.go",
|
||||
"testdata/src/implements-json/main.go",
|
||||
"testdata/src/implements-methods-json/main.go",
|
||||
"testdata/src/pointsto-json/main.go",
|
||||
"testdata/src/referrers-json/main.go",
|
||||
"testdata/src/what-json/main.go",
|
||||
} {
|
||||
filename := filename
|
||||
name := strings.Split(filename, "/")[2]
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if filename == "testdata/src/referrers/main.go" && runtime.GOOS == "plan9" {
|
||||
// Disable this test on plan9 since it expects a particular
|
||||
// wording for a "no such file or directory" error.
|
||||
t.Skip()
|
||||
}
|
||||
if filename == "testdata/src/alias/alias.go" && !guru.HasAlias {
|
||||
t.Skip()
|
||||
}
|
||||
if strings.HasSuffix(filename, "19.go") && !contains(build.Default.ReleaseTags, "go1.9") {
|
||||
// TODO(adonovan): recombine the 'describe' and 'definition'
|
||||
// tests once we drop support for go1.8.
|
||||
t.Skip()
|
||||
}
|
||||
if filename == "testdata/src/referrers/main.go" && !contains(build.Default.ReleaseTags, "go1.11") {
|
||||
// Disabling broken test on Go 1.9 and Go 1.10. https://golang.org/issue/24421
|
||||
// TODO(gri,adonovan): fix this test.
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
json := strings.Contains(filename, "-json/")
|
||||
queries := parseQueries(t, filename)
|
||||
golden := filename + "lden"
|
||||
got := filename + "t"
|
||||
gotfh, err := os.Create(got)
|
||||
if err != nil {
|
||||
t.Fatalf("Create(%s) failed: %s", got, err)
|
||||
}
|
||||
defer os.Remove(got)
|
||||
defer gotfh.Close()
|
||||
|
||||
// Run the guru on each query, redirecting its output
|
||||
// and error (if any) to the foo.got file.
|
||||
for _, q := range queries {
|
||||
doQuery(gotfh, q, json)
|
||||
}
|
||||
|
||||
// Compare foo.got with foo.golden.
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "plan9":
|
||||
cmd = exec.Command("/bin/diff", "-c", golden, got)
|
||||
default:
|
||||
cmd = exec.Command("/usr/bin/diff", "-u", golden, got)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Errorf("Guru tests for %s failed: %s.\n%s\n",
|
||||
filename, err, buf)
|
||||
|
||||
if *updateFlag {
|
||||
t.Logf("Updating %s...", golden)
|
||||
if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
|
||||
t.Errorf("Update failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func contains(haystack []string, needle string) bool {
|
||||
for _, x := range haystack {
|
||||
if needle == x {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestIssue14684(t *testing.T) {
|
||||
var buildContext = build.Default
|
||||
buildContext.GOPATH = "testdata"
|
||||
query := guru.Query{
|
||||
Pos: "testdata/src/README.txt:#1",
|
||||
Build: &buildContext,
|
||||
}
|
||||
err := guru.Run("freevars", &query)
|
||||
if err == nil {
|
||||
t.Fatal("guru query succeeded unexpectedly")
|
||||
}
|
||||
if got, want := err.Error(), "testdata/src/README.txt is not a Go source file"; got != want {
|
||||
t.Errorf("query error was %q, want %q", got, want)
|
||||
}
|
||||
}
|
364
vendor/golang.org/x/tools/cmd/guru/implements.go
generated
vendored
Normal file
364
vendor/golang.org/x/tools/cmd/guru/implements.go
generated
vendored
Normal file
@@ -0,0 +1,364 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/refactor/importgraph"
|
||||
)
|
||||
|
||||
// Implements displays the "implements" relation as it pertains to the
|
||||
// selected type.
|
||||
// If the selection is a method, 'implements' displays
|
||||
// the corresponding methods of the types that would have been reported
|
||||
// by an implements query on the receiver type.
|
||||
//
|
||||
func implements(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
qpkg, err := importQueryPackage(q.Pos, &lconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the packages to search.
|
||||
if len(q.Scope) > 0 {
|
||||
// Inspect all packages in the analysis scope, if specified.
|
||||
if err := setPTAScope(&lconf, q.Scope); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Otherwise inspect the forward and reverse
|
||||
// transitive closure of the selected package.
|
||||
// (In theory even this is incomplete.)
|
||||
_, rev, _ := importgraph.Build(q.Build)
|
||||
for path := range rev.Search(qpkg) {
|
||||
lconf.ImportWithTests(path)
|
||||
}
|
||||
|
||||
// TODO(adonovan): for completeness, we should also
|
||||
// type-check and inspect function bodies in all
|
||||
// imported packages. This would be expensive, but we
|
||||
// could optimize by skipping functions that do not
|
||||
// contain type declarations. This would require
|
||||
// changing the loader's TypeCheckFuncBodies hook to
|
||||
// provide the []*ast.File.
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := lconf.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find the selected type.
|
||||
path, action := findInterestingNode(qpos.info, qpos.path)
|
||||
|
||||
var method *types.Func
|
||||
var T types.Type // selected type (receiver if method != nil)
|
||||
|
||||
switch action {
|
||||
case actionExpr:
|
||||
// method?
|
||||
if id, ok := path[0].(*ast.Ident); ok {
|
||||
if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
|
||||
recv := obj.Type().(*types.Signature).Recv()
|
||||
if recv == nil {
|
||||
return fmt.Errorf("this function is not a method")
|
||||
}
|
||||
method = obj
|
||||
T = recv.Type()
|
||||
}
|
||||
}
|
||||
|
||||
// If not a method, use the expression's type.
|
||||
if T == nil {
|
||||
T = qpos.info.TypeOf(path[0].(ast.Expr))
|
||||
}
|
||||
|
||||
case actionType:
|
||||
T = qpos.info.TypeOf(path[0].(ast.Expr))
|
||||
}
|
||||
if T == nil {
|
||||
return fmt.Errorf("not a type, method, or value")
|
||||
}
|
||||
|
||||
// Find all named types, even local types (which can have
|
||||
// methods due to promotion) and the built-in "error".
|
||||
// We ignore aliases 'type M = N' to avoid duplicate
|
||||
// reporting of the Named type N.
|
||||
var allNamed []*types.Named
|
||||
for _, info := range lprog.AllPackages {
|
||||
for _, obj := range info.Defs {
|
||||
if obj, ok := obj.(*types.TypeName); ok && !isAlias(obj) {
|
||||
if named, ok := obj.Type().(*types.Named); ok {
|
||||
allNamed = append(allNamed, named)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
allNamed = append(allNamed, types.Universe.Lookup("error").Type().(*types.Named))
|
||||
|
||||
var msets typeutil.MethodSetCache
|
||||
|
||||
// Test each named type.
|
||||
var to, from, fromPtr []types.Type
|
||||
for _, U := range allNamed {
|
||||
if isInterface(T) {
|
||||
if msets.MethodSet(T).Len() == 0 {
|
||||
continue // empty interface
|
||||
}
|
||||
if isInterface(U) {
|
||||
if msets.MethodSet(U).Len() == 0 {
|
||||
continue // empty interface
|
||||
}
|
||||
|
||||
// T interface, U interface
|
||||
if !types.Identical(T, U) {
|
||||
if types.AssignableTo(U, T) {
|
||||
to = append(to, U)
|
||||
}
|
||||
if types.AssignableTo(T, U) {
|
||||
from = append(from, U)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// T interface, U concrete
|
||||
if types.AssignableTo(U, T) {
|
||||
to = append(to, U)
|
||||
} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
|
||||
to = append(to, pU)
|
||||
}
|
||||
}
|
||||
} else if isInterface(U) {
|
||||
if msets.MethodSet(U).Len() == 0 {
|
||||
continue // empty interface
|
||||
}
|
||||
|
||||
// T concrete, U interface
|
||||
if types.AssignableTo(T, U) {
|
||||
from = append(from, U)
|
||||
} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
|
||||
fromPtr = append(fromPtr, U)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pos interface{} = qpos
|
||||
if nt, ok := deref(T).(*types.Named); ok {
|
||||
pos = nt.Obj()
|
||||
}
|
||||
|
||||
// Sort types (arbitrarily) to ensure test determinism.
|
||||
sort.Sort(typesByString(to))
|
||||
sort.Sort(typesByString(from))
|
||||
sort.Sort(typesByString(fromPtr))
|
||||
|
||||
var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
|
||||
if method != nil {
|
||||
for _, t := range to {
|
||||
toMethod = append(toMethod,
|
||||
types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
|
||||
}
|
||||
for _, t := range from {
|
||||
fromMethod = append(fromMethod,
|
||||
types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
|
||||
}
|
||||
for _, t := range fromPtr {
|
||||
fromPtrMethod = append(fromPtrMethod,
|
||||
types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
q.Output(lprog.Fset, &implementsResult{
|
||||
qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type implementsResult struct {
|
||||
qpos *queryPos
|
||||
|
||||
t types.Type // queried type (not necessarily named)
|
||||
pos interface{} // pos of t (*types.Name or *QueryPos)
|
||||
to []types.Type // named or ptr-to-named types assignable to interface T
|
||||
from []types.Type // named interfaces assignable from T
|
||||
fromPtr []types.Type // named interfaces assignable only from *T
|
||||
|
||||
// if a method was queried:
|
||||
method *types.Func // queried method
|
||||
toMethod []*types.Selection // method of type to[i], if any
|
||||
fromMethod []*types.Selection // method of type from[i], if any
|
||||
fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any
|
||||
}
|
||||
|
||||
func (r *implementsResult) PrintPlain(printf printfFunc) {
|
||||
relation := "is implemented by"
|
||||
|
||||
meth := func(sel *types.Selection) {
|
||||
if sel != nil {
|
||||
printf(sel.Obj(), "\t%s method (%s).%s",
|
||||
relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name())
|
||||
}
|
||||
}
|
||||
|
||||
if isInterface(r.t) {
|
||||
if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
|
||||
printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t))
|
||||
return
|
||||
}
|
||||
|
||||
if r.method == nil {
|
||||
printf(r.pos, "interface type %s", r.qpos.typeString(r.t))
|
||||
} else {
|
||||
printf(r.method, "abstract method %s", r.qpos.objectString(r.method))
|
||||
}
|
||||
|
||||
// Show concrete types (or methods) first; use two passes.
|
||||
for i, sub := range r.to {
|
||||
if !isInterface(sub) {
|
||||
if r.method == nil {
|
||||
printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
|
||||
relation, typeKind(sub), r.qpos.typeString(sub))
|
||||
} else {
|
||||
meth(r.toMethod[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i, sub := range r.to {
|
||||
if isInterface(sub) {
|
||||
if r.method == nil {
|
||||
printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
|
||||
relation, typeKind(sub), r.qpos.typeString(sub))
|
||||
} else {
|
||||
meth(r.toMethod[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
relation = "implements"
|
||||
for i, super := range r.from {
|
||||
if r.method == nil {
|
||||
printf(super.(*types.Named).Obj(), "\t%s %s",
|
||||
relation, r.qpos.typeString(super))
|
||||
} else {
|
||||
meth(r.fromMethod[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
relation = "implements"
|
||||
|
||||
if r.from != nil {
|
||||
if r.method == nil {
|
||||
printf(r.pos, "%s type %s",
|
||||
typeKind(r.t), r.qpos.typeString(r.t))
|
||||
} else {
|
||||
printf(r.method, "concrete method %s",
|
||||
r.qpos.objectString(r.method))
|
||||
}
|
||||
for i, super := range r.from {
|
||||
if r.method == nil {
|
||||
printf(super.(*types.Named).Obj(), "\t%s %s",
|
||||
relation, r.qpos.typeString(super))
|
||||
} else {
|
||||
meth(r.fromMethod[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
if r.fromPtr != nil {
|
||||
if r.method == nil {
|
||||
printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t))
|
||||
} else {
|
||||
// TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
|
||||
printf(r.method, "concrete method %s",
|
||||
r.qpos.objectString(r.method))
|
||||
}
|
||||
|
||||
for i, psuper := range r.fromPtr {
|
||||
if r.method == nil {
|
||||
printf(psuper.(*types.Named).Obj(), "\t%s %s",
|
||||
relation, r.qpos.typeString(psuper))
|
||||
} else {
|
||||
meth(r.fromPtrMethod[i])
|
||||
}
|
||||
}
|
||||
} else if r.from == nil {
|
||||
printf(r.pos, "%s type %s implements only interface{}",
|
||||
typeKind(r.t), r.qpos.typeString(r.t))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *implementsResult) JSON(fset *token.FileSet) []byte {
|
||||
var method *serial.DescribeMethod
|
||||
if r.method != nil {
|
||||
method = &serial.DescribeMethod{
|
||||
Name: r.qpos.objectString(r.method),
|
||||
Pos: fset.Position(r.method.Pos()).String(),
|
||||
}
|
||||
}
|
||||
return toJSON(&serial.Implements{
|
||||
T: makeImplementsType(r.t, fset),
|
||||
AssignableTo: makeImplementsTypes(r.to, fset),
|
||||
AssignableFrom: makeImplementsTypes(r.from, fset),
|
||||
AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset),
|
||||
AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
|
||||
AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
|
||||
AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
|
||||
Method: method,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType {
|
||||
var r []serial.ImplementsType
|
||||
for _, t := range tt {
|
||||
r = append(r, makeImplementsType(t, fset))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType {
|
||||
var pos token.Pos
|
||||
if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named
|
||||
pos = nt.Obj().Pos()
|
||||
}
|
||||
return serial.ImplementsType{
|
||||
Name: T.String(),
|
||||
Pos: fset.Position(pos).String(),
|
||||
Kind: typeKind(T),
|
||||
}
|
||||
}
|
||||
|
||||
// typeKind returns a string describing the underlying kind of type,
|
||||
// e.g. "slice", "array", "struct".
|
||||
func typeKind(T types.Type) string {
|
||||
s := reflect.TypeOf(T.Underlying()).String()
|
||||
return strings.ToLower(strings.TrimPrefix(s, "*types."))
|
||||
}
|
||||
|
||||
func isInterface(T types.Type) bool { return types.IsInterface(T) }
|
||||
|
||||
type typesByString []types.Type
|
||||
|
||||
func (p typesByString) Len() int { return len(p) }
|
||||
func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
|
||||
func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
15
vendor/golang.org/x/tools/cmd/guru/isAlias18.go
generated
vendored
Normal file
15
vendor/golang.org/x/tools/cmd/guru/isAlias18.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.9
|
||||
|
||||
package main
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return false // there are no type aliases before Go 1.9
|
||||
}
|
||||
|
||||
const HasAlias = false
|
15
vendor/golang.org/x/tools/cmd/guru/isAlias19.go
generated
vendored
Normal file
15
vendor/golang.org/x/tools/cmd/guru/isAlias19.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package main
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return obj.IsAlias()
|
||||
}
|
||||
|
||||
const HasAlias = true
|
225
vendor/golang.org/x/tools/cmd/guru/main.go
generated
vendored
Normal file
225
vendor/golang.org/x/tools/cmd/guru/main.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// guru: a tool for answering questions about Go source code.
|
||||
//
|
||||
// http://golang.org/s/using-guru
|
||||
//
|
||||
// Run with -help flag or help subcommand for usage information.
|
||||
//
|
||||
package main // import "golang.org/x/tools/cmd/guru"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
)
|
||||
|
||||
// flags
|
||||
var (
|
||||
modifiedFlag = flag.Bool("modified", false, "read archive of modified files from standard input")
|
||||
scopeFlag = flag.String("scope", "", "comma-separated list of `packages` the analysis should be limited to")
|
||||
ptalogFlag = flag.String("ptalog", "", "write points-to analysis log to `file`")
|
||||
jsonFlag = flag.Bool("json", false, "emit output in JSON format")
|
||||
reflectFlag = flag.Bool("reflect", false, "analyze reflection soundly (slow)")
|
||||
cpuprofileFlag = flag.String("cpuprofile", "", "write CPU profile to `file`")
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
|
||||
|
||||
// gccgo does not provide a GOROOT with standard library sources.
|
||||
// If we have one in the environment, force gc mode.
|
||||
if build.Default.Compiler == "gccgo" {
|
||||
if _, err := os.Stat(filepath.Join(runtime.GOROOT(), "src", "runtime", "runtime.go")); err == nil {
|
||||
build.Default.Compiler = "gc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const useHelp = "Run 'guru -help' for more information.\n"
|
||||
|
||||
const helpMessage = `Go source code guru.
|
||||
Usage: guru [flags] <mode> <position>
|
||||
|
||||
The mode argument determines the query to perform:
|
||||
|
||||
callees show possible targets of selected function call
|
||||
callers show possible callers of selected function
|
||||
callstack show path from callgraph root to selected function
|
||||
definition show declaration of selected identifier
|
||||
describe describe selected syntax: definition, methods, etc
|
||||
freevars show free variables of selection
|
||||
implements show 'implements' relation for selected type or method
|
||||
peers show send/receive corresponding to selected channel op
|
||||
pointsto show variables the selected pointer may point to
|
||||
referrers show all refs to entity denoted by selected identifier
|
||||
what show basic information about the selected syntax node
|
||||
whicherrs show possible values of the selected error variable
|
||||
|
||||
The position argument specifies the filename and byte offset (or range)
|
||||
of the syntax element to query. For example:
|
||||
|
||||
foo.go:#123,#128
|
||||
bar.go:#123
|
||||
|
||||
The -json flag causes guru to emit output in JSON format;
|
||||
golang.org/x/tools/cmd/guru/serial defines its schema.
|
||||
Otherwise, the output is in an editor-friendly format in which
|
||||
every line has the form "pos: text", where pos is "-" if unknown.
|
||||
|
||||
The -modified flag causes guru to read an archive from standard input.
|
||||
Files in this archive will be used in preference to those in
|
||||
the file system. In this way, a text editor may supply guru
|
||||
with the contents of its unsaved buffers. Each archive entry
|
||||
consists of the file name, a newline, the decimal file size,
|
||||
another newline, and the contents of the file.
|
||||
|
||||
The -scope flag restricts analysis to the specified packages.
|
||||
Its value is a comma-separated list of patterns of these forms:
|
||||
golang.org/x/tools/cmd/guru # a single package
|
||||
golang.org/x/tools/... # all packages beneath dir
|
||||
... # the entire workspace.
|
||||
A pattern preceded by '-' is negative, so the scope
|
||||
encoding/...,-encoding/xml
|
||||
matches all encoding packages except encoding/xml.
|
||||
|
||||
User manual: http://golang.org/s/using-guru
|
||||
|
||||
Example: describe syntax at offset 530 in this file (an import spec):
|
||||
|
||||
$ guru describe src/golang.org/x/tools/cmd/guru/main.go:#530
|
||||
`
|
||||
|
||||
func printHelp() {
|
||||
fmt.Fprintln(os.Stderr, helpMessage)
|
||||
fmt.Fprintln(os.Stderr, "Flags:")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetPrefix("guru: ")
|
||||
log.SetFlags(0)
|
||||
|
||||
// Don't print full help unless -help was requested.
|
||||
// Just gently remind users that it's there.
|
||||
flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) }
|
||||
flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) // hack
|
||||
if err := flag.CommandLine.Parse(os.Args[1:]); err != nil {
|
||||
// (err has already been printed)
|
||||
if err == flag.ErrHelp {
|
||||
printHelp()
|
||||
}
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) != 2 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
mode, posn := args[0], args[1]
|
||||
|
||||
if mode == "help" {
|
||||
printHelp()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// Set up points-to analysis log file.
|
||||
var ptalog io.Writer
|
||||
if *ptalogFlag != "" {
|
||||
if f, err := os.Create(*ptalogFlag); err != nil {
|
||||
log.Fatalf("Failed to create PTA log file: %s", err)
|
||||
} else {
|
||||
buf := bufio.NewWriter(f)
|
||||
ptalog = buf
|
||||
defer func() {
|
||||
if err := buf.Flush(); err != nil {
|
||||
log.Printf("flush: %s", err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
log.Printf("close: %s", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Profiling support.
|
||||
if *cpuprofileFlag != "" {
|
||||
f, err := os.Create(*cpuprofileFlag)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
ctxt := &build.Default
|
||||
|
||||
// If there were modified files,
|
||||
// read them from the standard input and
|
||||
// overlay them on the build context.
|
||||
if *modifiedFlag {
|
||||
modified, err := buildutil.ParseOverlayArchive(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// All I/O done by guru needs to consult the modified map.
|
||||
// The ReadFile done by referrers does,
|
||||
// but the loader's cgo preprocessing currently does not.
|
||||
|
||||
if len(modified) > 0 {
|
||||
ctxt = buildutil.OverlayContext(ctxt, modified)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid corner case of split("").
|
||||
var scope []string
|
||||
if *scopeFlag != "" {
|
||||
scope = strings.Split(*scopeFlag, ",")
|
||||
}
|
||||
|
||||
// Ask the guru.
|
||||
query := Query{
|
||||
Pos: posn,
|
||||
Build: ctxt,
|
||||
Scope: scope,
|
||||
PTALog: ptalog,
|
||||
Reflection: *reflectFlag,
|
||||
Output: output,
|
||||
}
|
||||
|
||||
if err := Run(mode, &query); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
252
vendor/golang.org/x/tools/cmd/guru/peers.go
generated
vendored
Normal file
252
vendor/golang.org/x/tools/cmd/guru/peers.go
generated
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
// peers enumerates, for a given channel send (or receive) operation,
|
||||
// the set of possible receives (or sends) that correspond to it.
|
||||
//
|
||||
// TODO(adonovan): support reflect.{Select,Recv,Send,Close}.
|
||||
// TODO(adonovan): permit the user to query based on a MakeChan (not send/recv),
|
||||
// or the implicit receive in "for v := range ch".
|
||||
func peers(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
|
||||
if err := setPTAScope(&lconf, q.Scope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := loadWithSoftErrors(&lconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
|
||||
|
||||
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
opPos := findOp(qpos)
|
||||
if opPos == token.NoPos {
|
||||
return fmt.Errorf("there is no channel operation here")
|
||||
}
|
||||
|
||||
// Defer SSA construction till after errors are reported.
|
||||
prog.Build()
|
||||
|
||||
var queryOp chanOp // the originating send or receive operation
|
||||
var ops []chanOp // all sends/receives of opposite direction
|
||||
|
||||
// Look at all channel operations in the whole ssa.Program.
|
||||
// Build a list of those of same type as the query.
|
||||
allFuncs := ssautil.AllFunctions(prog)
|
||||
for fn := range allFuncs {
|
||||
for _, b := range fn.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
for _, op := range chanOps(instr) {
|
||||
ops = append(ops, op)
|
||||
if op.pos == opPos {
|
||||
queryOp = op // we found the query op
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if queryOp.ch == nil {
|
||||
return fmt.Errorf("ssa.Instruction for send/receive not found")
|
||||
}
|
||||
|
||||
// Discard operations of wrong channel element type.
|
||||
// Build set of channel ssa.Values as query to pointer analysis.
|
||||
// We compare channels by element types, not channel types, to
|
||||
// ignore both directionality and type names.
|
||||
queryType := queryOp.ch.Type()
|
||||
queryElemType := queryType.Underlying().(*types.Chan).Elem()
|
||||
ptaConfig.AddQuery(queryOp.ch)
|
||||
i := 0
|
||||
for _, op := range ops {
|
||||
if types.Identical(op.ch.Type().Underlying().(*types.Chan).Elem(), queryElemType) {
|
||||
ptaConfig.AddQuery(op.ch)
|
||||
ops[i] = op
|
||||
i++
|
||||
}
|
||||
}
|
||||
ops = ops[:i]
|
||||
|
||||
// Run the pointer analysis.
|
||||
ptares := ptrAnalysis(ptaConfig)
|
||||
|
||||
// Find the points-to set.
|
||||
queryChanPtr := ptares.Queries[queryOp.ch]
|
||||
|
||||
// Ascertain which make(chan) labels the query's channel can alias.
|
||||
var makes []token.Pos
|
||||
for _, label := range queryChanPtr.PointsTo().Labels() {
|
||||
makes = append(makes, label.Pos())
|
||||
}
|
||||
sort.Sort(byPos(makes))
|
||||
|
||||
// Ascertain which channel operations can alias the same make(chan) labels.
|
||||
var sends, receives, closes []token.Pos
|
||||
for _, op := range ops {
|
||||
if ptr, ok := ptares.Queries[op.ch]; ok && ptr.MayAlias(queryChanPtr) {
|
||||
switch op.dir {
|
||||
case types.SendOnly:
|
||||
sends = append(sends, op.pos)
|
||||
case types.RecvOnly:
|
||||
receives = append(receives, op.pos)
|
||||
case types.SendRecv:
|
||||
closes = append(closes, op.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Sort(byPos(sends))
|
||||
sort.Sort(byPos(receives))
|
||||
sort.Sort(byPos(closes))
|
||||
|
||||
q.Output(lprog.Fset, &peersResult{
|
||||
queryPos: opPos,
|
||||
queryType: queryType,
|
||||
makes: makes,
|
||||
sends: sends,
|
||||
receives: receives,
|
||||
closes: closes,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// findOp returns the position of the enclosing send/receive/close op.
|
||||
// For send and receive operations, this is the position of the <- token;
|
||||
// for close operations, it's the Lparen of the function call.
|
||||
//
|
||||
// TODO(adonovan): handle implicit receive operations from 'for...range chan' statements.
|
||||
func findOp(qpos *queryPos) token.Pos {
|
||||
for _, n := range qpos.path {
|
||||
switch n := n.(type) {
|
||||
case *ast.UnaryExpr:
|
||||
if n.Op == token.ARROW {
|
||||
return n.OpPos
|
||||
}
|
||||
case *ast.SendStmt:
|
||||
return n.Arrow
|
||||
case *ast.CallExpr:
|
||||
// close function call can only exist as a direct identifier
|
||||
if close, ok := unparen(n.Fun).(*ast.Ident); ok {
|
||||
if b, ok := qpos.info.Info.Uses[close].(*types.Builtin); ok && b.Name() == "close" {
|
||||
return n.Lparen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
|
||||
type chanOp struct {
|
||||
ch ssa.Value
|
||||
dir types.ChanDir // SendOnly=send, RecvOnly=recv, SendRecv=close
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
// chanOps returns a slice of all the channel operations in the instruction.
|
||||
func chanOps(instr ssa.Instruction) []chanOp {
|
||||
// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too.
|
||||
var ops []chanOp
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.UnOp:
|
||||
if instr.Op == token.ARROW {
|
||||
ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
|
||||
}
|
||||
case *ssa.Send:
|
||||
ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
|
||||
case *ssa.Select:
|
||||
for _, st := range instr.States {
|
||||
ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
|
||||
}
|
||||
case ssa.CallInstruction:
|
||||
cc := instr.Common()
|
||||
if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
|
||||
ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
|
||||
}
|
||||
}
|
||||
return ops
|
||||
}
|
||||
|
||||
// TODO(adonovan): show the line of text for each pos, like "referrers" does.
|
||||
type peersResult struct {
|
||||
queryPos token.Pos // of queried channel op
|
||||
queryType types.Type // type of queried channel
|
||||
makes, sends, receives, closes []token.Pos // positions of aliased makechan/send/receive/close instrs
|
||||
}
|
||||
|
||||
func (r *peersResult) PrintPlain(printf printfFunc) {
|
||||
if len(r.makes) == 0 {
|
||||
printf(r.queryPos, "This channel can't point to anything.")
|
||||
return
|
||||
}
|
||||
printf(r.queryPos, "This channel of type %s may be:", r.queryType)
|
||||
for _, alloc := range r.makes {
|
||||
printf(alloc, "\tallocated here")
|
||||
}
|
||||
for _, send := range r.sends {
|
||||
printf(send, "\tsent to, here")
|
||||
}
|
||||
for _, receive := range r.receives {
|
||||
printf(receive, "\treceived from, here")
|
||||
}
|
||||
for _, clos := range r.closes {
|
||||
printf(clos, "\tclosed, here")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *peersResult) JSON(fset *token.FileSet) []byte {
|
||||
peers := &serial.Peers{
|
||||
Pos: fset.Position(r.queryPos).String(),
|
||||
Type: r.queryType.String(),
|
||||
}
|
||||
for _, alloc := range r.makes {
|
||||
peers.Allocs = append(peers.Allocs, fset.Position(alloc).String())
|
||||
}
|
||||
for _, send := range r.sends {
|
||||
peers.Sends = append(peers.Sends, fset.Position(send).String())
|
||||
}
|
||||
for _, receive := range r.receives {
|
||||
peers.Receives = append(peers.Receives, fset.Position(receive).String())
|
||||
}
|
||||
for _, clos := range r.closes {
|
||||
peers.Closes = append(peers.Closes, fset.Position(clos).String())
|
||||
}
|
||||
return toJSON(peers)
|
||||
}
|
||||
|
||||
// -------- utils --------
|
||||
|
||||
// NB: byPos is not deterministic across packages since it depends on load order.
|
||||
// Use lessPos if the tests need it.
|
||||
type byPos []token.Pos
|
||||
|
||||
func (p byPos) Len() int { return len(p) }
|
||||
func (p byPos) Less(i, j int) bool { return p[i] < p[j] }
|
||||
func (p byPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
290
vendor/golang.org/x/tools/cmd/guru/pointsto.go
generated
vendored
Normal file
290
vendor/golang.org/x/tools/cmd/guru/pointsto.go
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/pointer"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
// pointsto runs the pointer analysis on the selected expression,
|
||||
// and reports its points-to set (for a pointer-like expression)
|
||||
// or its dynamic types (for an interface, reflect.Value, or
|
||||
// reflect.Type expression) and their points-to sets.
|
||||
//
|
||||
// All printed sets are sorted to ensure determinism.
|
||||
//
|
||||
func pointsto(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
|
||||
if err := setPTAScope(&lconf, q.Scope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := loadWithSoftErrors(&lconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
|
||||
|
||||
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, action := findInterestingNode(qpos.info, qpos.path)
|
||||
if action != actionExpr {
|
||||
return fmt.Errorf("pointer analysis wants an expression; got %s",
|
||||
astutil.NodeDescription(qpos.path[0]))
|
||||
}
|
||||
|
||||
var expr ast.Expr
|
||||
var obj types.Object
|
||||
switch n := path[0].(type) {
|
||||
case *ast.ValueSpec:
|
||||
// ambiguous ValueSpec containing multiple names
|
||||
return fmt.Errorf("multiple value specification")
|
||||
case *ast.Ident:
|
||||
obj = qpos.info.ObjectOf(n)
|
||||
expr = n
|
||||
case ast.Expr:
|
||||
expr = n
|
||||
default:
|
||||
// TODO(adonovan): is this reachable?
|
||||
return fmt.Errorf("unexpected AST for expr: %T", n)
|
||||
}
|
||||
|
||||
// Reject non-pointerlike types (includes all constants---except nil).
|
||||
// TODO(adonovan): reject nil too.
|
||||
typ := qpos.info.TypeOf(expr)
|
||||
if !pointer.CanPoint(typ) {
|
||||
return fmt.Errorf("pointer analysis wants an expression of reference type; got %s", typ)
|
||||
}
|
||||
|
||||
// Determine the ssa.Value for the expression.
|
||||
var value ssa.Value
|
||||
var isAddr bool
|
||||
if obj != nil {
|
||||
// def/ref of func/var object
|
||||
value, isAddr, err = ssaValueForIdent(prog, qpos.info, obj, path)
|
||||
} else {
|
||||
value, isAddr, err = ssaValueForExpr(prog, qpos.info, path)
|
||||
}
|
||||
if err != nil {
|
||||
return err // e.g. trivially dead code
|
||||
}
|
||||
|
||||
// Defer SSA construction till after errors are reported.
|
||||
prog.Build()
|
||||
|
||||
// Run the pointer analysis.
|
||||
ptrs, err := runPTA(ptaConfig, value, isAddr)
|
||||
if err != nil {
|
||||
return err // e.g. analytically unreachable
|
||||
}
|
||||
|
||||
q.Output(lprog.Fset, &pointstoResult{
|
||||
qpos: qpos,
|
||||
typ: typ,
|
||||
ptrs: ptrs,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
|
||||
// to the root of the AST is path. isAddr reports whether the
|
||||
// ssa.Value is the address denoted by the ast.Ident, not its value.
|
||||
//
|
||||
func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
|
||||
switch obj := obj.(type) {
|
||||
case *types.Var:
|
||||
pkg := prog.Package(qinfo.Pkg)
|
||||
pkg.Build()
|
||||
if v, addr := prog.VarValue(obj, pkg, path); v != nil {
|
||||
return v, addr, nil
|
||||
}
|
||||
return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name())
|
||||
|
||||
case *types.Func:
|
||||
fn := prog.FuncValue(obj)
|
||||
if fn == nil {
|
||||
return nil, false, fmt.Errorf("%s is an interface method", obj)
|
||||
}
|
||||
// TODO(adonovan): there's no point running PTA on a *Func ident.
|
||||
// Eliminate this feature.
|
||||
return fn, false, nil
|
||||
}
|
||||
panic(obj)
|
||||
}
|
||||
|
||||
// ssaValueForExpr returns the ssa.Value of the non-ast.Ident
|
||||
// expression whose path to the root of the AST is path.
|
||||
//
|
||||
func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
|
||||
pkg := prog.Package(qinfo.Pkg)
|
||||
pkg.SetDebugMode(true)
|
||||
pkg.Build()
|
||||
|
||||
fn := ssa.EnclosingFunction(pkg, path)
|
||||
if fn == nil {
|
||||
return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)")
|
||||
}
|
||||
|
||||
if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil {
|
||||
return v, addr, nil
|
||||
}
|
||||
|
||||
return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn)
|
||||
}
|
||||
|
||||
// runPTA runs the pointer analysis of the selected SSA value or address.
|
||||
func runPTA(conf *pointer.Config, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) {
|
||||
T := v.Type()
|
||||
if isAddr {
|
||||
conf.AddIndirectQuery(v)
|
||||
T = deref(T)
|
||||
} else {
|
||||
conf.AddQuery(v)
|
||||
}
|
||||
ptares := ptrAnalysis(conf)
|
||||
|
||||
var ptr pointer.Pointer
|
||||
if isAddr {
|
||||
ptr = ptares.IndirectQueries[v]
|
||||
} else {
|
||||
ptr = ptares.Queries[v]
|
||||
}
|
||||
if ptr == (pointer.Pointer{}) {
|
||||
return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)")
|
||||
}
|
||||
pts := ptr.PointsTo()
|
||||
|
||||
if pointer.CanHaveDynamicTypes(T) {
|
||||
// Show concrete types for interface/reflect.Value expression.
|
||||
if concs := pts.DynamicTypes(); concs.Len() > 0 {
|
||||
concs.Iterate(func(conc types.Type, pta interface{}) {
|
||||
labels := pta.(pointer.PointsToSet).Labels()
|
||||
sort.Sort(byPosAndString(labels)) // to ensure determinism
|
||||
ptrs = append(ptrs, pointerResult{conc, labels})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Show labels for other expressions.
|
||||
labels := pts.Labels()
|
||||
sort.Sort(byPosAndString(labels)) // to ensure determinism
|
||||
ptrs = append(ptrs, pointerResult{T, labels})
|
||||
}
|
||||
sort.Sort(byTypeString(ptrs)) // to ensure determinism
|
||||
return ptrs, nil
|
||||
}
|
||||
|
||||
type pointerResult struct {
|
||||
typ types.Type // type of the pointer (always concrete)
|
||||
labels []*pointer.Label // set of labels
|
||||
}
|
||||
|
||||
type pointstoResult struct {
|
||||
qpos *queryPos
|
||||
typ types.Type // type of expression
|
||||
ptrs []pointerResult // pointer info (typ is concrete => len==1)
|
||||
}
|
||||
|
||||
func (r *pointstoResult) PrintPlain(printf printfFunc) {
|
||||
if pointer.CanHaveDynamicTypes(r.typ) {
|
||||
// Show concrete types for interface, reflect.Type or
|
||||
// reflect.Value expression.
|
||||
|
||||
if len(r.ptrs) > 0 {
|
||||
printf(r.qpos, "this %s may contain these dynamic types:", r.qpos.typeString(r.typ))
|
||||
for _, ptr := range r.ptrs {
|
||||
var obj types.Object
|
||||
if nt, ok := deref(ptr.typ).(*types.Named); ok {
|
||||
obj = nt.Obj()
|
||||
}
|
||||
if len(ptr.labels) > 0 {
|
||||
printf(obj, "\t%s, may point to:", r.qpos.typeString(ptr.typ))
|
||||
printLabels(printf, ptr.labels, "\t\t")
|
||||
} else {
|
||||
printf(obj, "\t%s", r.qpos.typeString(ptr.typ))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ)
|
||||
}
|
||||
} else {
|
||||
// Show labels for other expressions.
|
||||
if ptr := r.ptrs[0]; len(ptr.labels) > 0 {
|
||||
printf(r.qpos, "this %s may point to these objects:",
|
||||
r.qpos.typeString(r.typ))
|
||||
printLabels(printf, ptr.labels, "\t")
|
||||
} else {
|
||||
printf(r.qpos, "this %s may not point to anything.",
|
||||
r.qpos.typeString(r.typ))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *pointstoResult) JSON(fset *token.FileSet) []byte {
|
||||
var pts []serial.PointsTo
|
||||
for _, ptr := range r.ptrs {
|
||||
var namePos string
|
||||
if nt, ok := deref(ptr.typ).(*types.Named); ok {
|
||||
namePos = fset.Position(nt.Obj().Pos()).String()
|
||||
}
|
||||
var labels []serial.PointsToLabel
|
||||
for _, l := range ptr.labels {
|
||||
labels = append(labels, serial.PointsToLabel{
|
||||
Pos: fset.Position(l.Pos()).String(),
|
||||
Desc: l.String(),
|
||||
})
|
||||
}
|
||||
pts = append(pts, serial.PointsTo{
|
||||
Type: r.qpos.typeString(ptr.typ),
|
||||
NamePos: namePos,
|
||||
Labels: labels,
|
||||
})
|
||||
}
|
||||
return toJSON(pts)
|
||||
}
|
||||
|
||||
type byTypeString []pointerResult
|
||||
|
||||
func (a byTypeString) Len() int { return len(a) }
|
||||
func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() }
|
||||
func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type byPosAndString []*pointer.Label
|
||||
|
||||
func (a byPosAndString) Len() int { return len(a) }
|
||||
func (a byPosAndString) Less(i, j int) bool {
|
||||
cmp := a[i].Pos() - a[j].Pos()
|
||||
return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String())
|
||||
}
|
||||
func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) {
|
||||
// TODO(adonovan): due to context-sensitivity, many of these
|
||||
// labels may differ only by context, which isn't apparent.
|
||||
for _, label := range labels {
|
||||
printf(label, "%s%s", prefix, label)
|
||||
}
|
||||
}
|
142
vendor/golang.org/x/tools/cmd/guru/pos.go
generated
vendored
Normal file
142
vendor/golang.org/x/tools/cmd/guru/pos.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// This file defines utilities for working with file positions.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
)
|
||||
|
||||
// parseOctothorpDecimal returns the numeric value if s matches "#%d",
|
||||
// otherwise -1.
|
||||
func parseOctothorpDecimal(s string) int {
|
||||
if s != "" && s[0] == '#' {
|
||||
if s, err := strconv.ParseInt(s[1:], 10, 32); err == nil {
|
||||
return int(s)
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// parsePos parses a string of the form "file:pos" or
|
||||
// file:start,end" where pos, start, end match #%d and represent byte
|
||||
// offsets, and returns its components.
|
||||
//
|
||||
// (Numbers without a '#' prefix are reserved for future use,
|
||||
// e.g. to indicate line/column positions.)
|
||||
//
|
||||
func parsePos(pos string) (filename string, startOffset, endOffset int, err error) {
|
||||
if pos == "" {
|
||||
err = fmt.Errorf("no source position specified")
|
||||
return
|
||||
}
|
||||
|
||||
colon := strings.LastIndex(pos, ":")
|
||||
if colon < 0 {
|
||||
err = fmt.Errorf("bad position syntax %q", pos)
|
||||
return
|
||||
}
|
||||
filename, offset := pos[:colon], pos[colon+1:]
|
||||
startOffset = -1
|
||||
endOffset = -1
|
||||
if comma := strings.Index(offset, ","); comma < 0 {
|
||||
// e.g. "foo.go:#123"
|
||||
startOffset = parseOctothorpDecimal(offset)
|
||||
endOffset = startOffset
|
||||
} else {
|
||||
// e.g. "foo.go:#123,#456"
|
||||
startOffset = parseOctothorpDecimal(offset[:comma])
|
||||
endOffset = parseOctothorpDecimal(offset[comma+1:])
|
||||
}
|
||||
if startOffset < 0 || endOffset < 0 {
|
||||
err = fmt.Errorf("invalid offset %q in query position", offset)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fileOffsetToPos translates the specified file-relative byte offsets
|
||||
// into token.Pos form. It returns an error if the file was not found
|
||||
// or the offsets were out of bounds.
|
||||
//
|
||||
func fileOffsetToPos(file *token.File, startOffset, endOffset int) (start, end token.Pos, err error) {
|
||||
// Range check [start..end], inclusive of both end-points.
|
||||
|
||||
if 0 <= startOffset && startOffset <= file.Size() {
|
||||
start = file.Pos(int(startOffset))
|
||||
} else {
|
||||
err = fmt.Errorf("start position is beyond end of file")
|
||||
return
|
||||
}
|
||||
|
||||
if 0 <= endOffset && endOffset <= file.Size() {
|
||||
end = file.Pos(int(endOffset))
|
||||
} else {
|
||||
err = fmt.Errorf("end position is beyond end of file")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// sameFile returns true if x and y have the same basename and denote
|
||||
// the same file.
|
||||
//
|
||||
func sameFile(x, y string) bool {
|
||||
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
|
||||
if xi, err := os.Stat(x); err == nil {
|
||||
if yi, err := os.Stat(y); err == nil {
|
||||
return os.SameFile(xi, yi)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// fastQueryPos parses the position string and returns a queryPos.
|
||||
// It parses only a single file and does not run the type checker.
|
||||
func fastQueryPos(ctxt *build.Context, pos string) (*queryPos, error) {
|
||||
filename, startOffset, endOffset, err := parsePos(pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the file, opening it the file via the build.Context
|
||||
// so that we observe the effects of the -modified flag.
|
||||
fset := token.NewFileSet()
|
||||
cwd, _ := os.Getwd()
|
||||
f, err := buildutil.ParseFile(fset, ctxt, nil, cwd, filename, parser.Mode(0))
|
||||
// ParseFile usually returns a partial file along with an error.
|
||||
// Only fail if there is no file.
|
||||
if f == nil {
|
||||
return nil, err
|
||||
}
|
||||
if !f.Pos().IsValid() {
|
||||
return nil, fmt.Errorf("%s is not a Go source file", filename)
|
||||
}
|
||||
|
||||
start, end, err := fileOffsetToPos(fset.File(f.Pos()), startOffset, endOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path, exact := astutil.PathEnclosingInterval(f, start, end)
|
||||
if path == nil {
|
||||
return nil, fmt.Errorf("no syntax here")
|
||||
}
|
||||
|
||||
return &queryPos{fset, start, end, path, exact, nil}, nil
|
||||
}
|
802
vendor/golang.org/x/tools/cmd/guru/referrers.go
generated
vendored
Normal file
802
vendor/golang.org/x/tools/cmd/guru/referrers.go
generated
vendored
Normal file
@@ -0,0 +1,802 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/imports"
|
||||
"golang.org/x/tools/refactor/importgraph"
|
||||
)
|
||||
|
||||
// Referrers reports all identifiers that resolve to the same object
|
||||
// as the queried identifier, within any package in the workspace.
|
||||
func referrers(q *Query) error {
|
||||
fset := token.NewFileSet()
|
||||
lconf := loader.Config{Fset: fset, Build: q.Build}
|
||||
allowErrors(&lconf)
|
||||
|
||||
if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load tests of the query package
|
||||
// even if the query location is not in the tests.
|
||||
for path := range lconf.ImportPkgs {
|
||||
lconf.ImportPkgs[path] = true
|
||||
}
|
||||
|
||||
// Load/parse/type-check the query package.
|
||||
lprog, err := lconf.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, _ := qpos.path[0].(*ast.Ident)
|
||||
if id == nil {
|
||||
return fmt.Errorf("no identifier here")
|
||||
}
|
||||
|
||||
obj := qpos.info.ObjectOf(id)
|
||||
if obj == nil {
|
||||
// Happens for y in "switch y := x.(type)",
|
||||
// the package declaration,
|
||||
// and unresolved identifiers.
|
||||
if _, ok := qpos.path[1].(*ast.File); ok { // package decl?
|
||||
return packageReferrers(q, qpos.info.Pkg.Path())
|
||||
}
|
||||
return fmt.Errorf("no object for identifier: %T", qpos.path[1])
|
||||
}
|
||||
|
||||
// Imported package name?
|
||||
if pkgname, ok := obj.(*types.PkgName); ok {
|
||||
return packageReferrers(q, pkgname.Imported().Path())
|
||||
}
|
||||
|
||||
if obj.Pkg() == nil {
|
||||
return fmt.Errorf("references to predeclared %q are everywhere!", obj.Name())
|
||||
}
|
||||
|
||||
q.Output(fset, &referrersInitialResult{
|
||||
qinfo: qpos.info,
|
||||
obj: obj,
|
||||
})
|
||||
|
||||
// For a globally accessible object defined in package P, we
|
||||
// must load packages that depend on P. Specifically, for a
|
||||
// package-level object, we need load only direct importers
|
||||
// of P, but for a field or method, we must load
|
||||
// any package that transitively imports P.
|
||||
|
||||
if global, pkglevel := classify(obj); global {
|
||||
if pkglevel {
|
||||
return globalReferrersPkgLevel(q, obj, fset)
|
||||
}
|
||||
// We'll use the the object's position to identify it in the larger program.
|
||||
objposn := fset.Position(obj.Pos())
|
||||
defpkg := obj.Pkg().Path() // defining package
|
||||
return globalReferrers(q, qpos.info.Pkg.Path(), defpkg, objposn)
|
||||
}
|
||||
|
||||
outputUses(q, fset, usesOf(obj, qpos.info), obj.Pkg())
|
||||
|
||||
return nil // success
|
||||
}
|
||||
|
||||
// classify classifies objects by how far
|
||||
// we have to look to find references to them.
|
||||
func classify(obj types.Object) (global, pkglevel bool) {
|
||||
if obj.Exported() {
|
||||
if obj.Parent() == nil {
|
||||
// selectable object (field or method)
|
||||
return true, false
|
||||
}
|
||||
if obj.Parent() == obj.Pkg().Scope() {
|
||||
// lexical object (package-level var/const/func/type)
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
// object with unexported named or defined in local scope
|
||||
return false, false
|
||||
}
|
||||
|
||||
// packageReferrers reports all references to the specified package
|
||||
// throughout the workspace.
|
||||
func packageReferrers(q *Query, path string) error {
|
||||
// Scan the workspace and build the import graph.
|
||||
// Ignore broken packages.
|
||||
_, rev, _ := importgraph.Build(q.Build)
|
||||
|
||||
// Find the set of packages that directly import the query package.
|
||||
// Only those packages need typechecking of function bodies.
|
||||
users := rev[path]
|
||||
|
||||
// Load the larger program.
|
||||
fset := token.NewFileSet()
|
||||
lconf := loader.Config{
|
||||
Fset: fset,
|
||||
Build: q.Build,
|
||||
TypeCheckFuncBodies: func(p string) bool {
|
||||
return users[strings.TrimSuffix(p, "_test")]
|
||||
},
|
||||
}
|
||||
allowErrors(&lconf)
|
||||
|
||||
// The importgraph doesn't treat external test packages
|
||||
// as separate nodes, so we must use ImportWithTests.
|
||||
for path := range users {
|
||||
lconf.ImportWithTests(path)
|
||||
}
|
||||
|
||||
// Subtle! AfterTypeCheck needs no mutex for qpkg because the
|
||||
// topological import order gives us the necessary happens-before edges.
|
||||
// TODO(adonovan): what about import cycles?
|
||||
var qpkg *types.Package
|
||||
|
||||
// For efficiency, we scan each package for references
|
||||
// just after it has been type-checked. The loader calls
|
||||
// AfterTypeCheck (concurrently), providing us with a stream of
|
||||
// packages.
|
||||
lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
|
||||
// AfterTypeCheck may be called twice for the same package due to augmentation.
|
||||
|
||||
if info.Pkg.Path() == path && qpkg == nil {
|
||||
// Found the package of interest.
|
||||
qpkg = info.Pkg
|
||||
fakepkgname := types.NewPkgName(token.NoPos, qpkg, qpkg.Name(), qpkg)
|
||||
q.Output(fset, &referrersInitialResult{
|
||||
qinfo: info,
|
||||
obj: fakepkgname, // bogus
|
||||
})
|
||||
}
|
||||
|
||||
// Only inspect packages that directly import the
|
||||
// declaring package (and thus were type-checked).
|
||||
if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
|
||||
// Find PkgNames that refer to qpkg.
|
||||
// TODO(adonovan): perhaps more useful would be to show imports
|
||||
// of the package instead of qualified identifiers.
|
||||
var refs []*ast.Ident
|
||||
for id, obj := range info.Uses {
|
||||
if obj, ok := obj.(*types.PkgName); ok && obj.Imported() == qpkg {
|
||||
refs = append(refs, id)
|
||||
}
|
||||
}
|
||||
outputUses(q, fset, refs, info.Pkg)
|
||||
}
|
||||
|
||||
clearInfoFields(info) // save memory
|
||||
}
|
||||
|
||||
lconf.Load() // ignore error
|
||||
|
||||
if qpkg == nil {
|
||||
log.Fatalf("query package %q not found during reloading", path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 (a field or method) at the specified source position.
|
||||
// Its defining package is defpkg, and the query package is qpkg.
|
||||
func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position) error {
|
||||
// Scan the workspace and build the import graph.
|
||||
// Ignore broken packages.
|
||||
_, rev, _ := importgraph.Build(q.Build)
|
||||
|
||||
// Find the set of packages that depend on defpkg.
|
||||
// Only function bodies in those packages need type-checking.
|
||||
users := rev.Search(defpkg) // transitive importers
|
||||
|
||||
// Prepare to load the larger program.
|
||||
fset := token.NewFileSet()
|
||||
lconf := loader.Config{
|
||||
Fset: fset,
|
||||
Build: q.Build,
|
||||
TypeCheckFuncBodies: func(p string) bool {
|
||||
return users[strings.TrimSuffix(p, "_test")]
|
||||
},
|
||||
}
|
||||
allowErrors(&lconf)
|
||||
|
||||
// The importgraph doesn't treat external test packages
|
||||
// as separate nodes, so we must use ImportWithTests.
|
||||
for path := range users {
|
||||
lconf.ImportWithTests(path)
|
||||
}
|
||||
|
||||
// The remainder of this function is somewhat tricky because it
|
||||
// operates on the concurrent stream of packages observed by the
|
||||
// loader's AfterTypeCheck hook. Most of guru's helper
|
||||
// functions assume the entire program has already been loaded,
|
||||
// so we can't use them here.
|
||||
// TODO(adonovan): smooth things out once the other changes have landed.
|
||||
|
||||
// Results are reported concurrently from within the
|
||||
// AfterTypeCheck hook. The program may provide a useful stream
|
||||
// of information even if the user doesn't let the program run
|
||||
// to completion.
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
qobj types.Object
|
||||
)
|
||||
|
||||
// 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.
|
||||
|
||||
// Only inspect packages that depend on the declaring package
|
||||
// (and thus were type-checked).
|
||||
if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
|
||||
// Record the query object and its package when we see it.
|
||||
mu.Lock()
|
||||
if qobj == nil && info.Pkg.Path() == defpkg {
|
||||
// Find the object by its position (slightly ugly).
|
||||
qobj = findObject(fset, &info.Info, objposn)
|
||||
if qobj == nil {
|
||||
// It really ought to be there;
|
||||
// we found it once already.
|
||||
log.Fatalf("object at %s not found in package %s",
|
||||
objposn, defpkg)
|
||||
}
|
||||
}
|
||||
obj := qobj
|
||||
mu.Unlock()
|
||||
|
||||
// Look for references to the query object.
|
||||
if obj != nil {
|
||||
outputUses(q, fset, usesOf(obj, info), info.Pkg)
|
||||
}
|
||||
}
|
||||
|
||||
clearInfoFields(info) // save memory
|
||||
}
|
||||
|
||||
lconf.Load() // ignore error
|
||||
|
||||
if qobj == nil {
|
||||
log.Fatal("query object not found during reloading")
|
||||
}
|
||||
|
||||
return nil // success
|
||||
}
|
||||
|
||||
// globalReferrersPkgLevel reports references throughout the entire workspace to the package-level object obj.
|
||||
// It assumes that the query object itself has already been reported.
|
||||
func globalReferrersPkgLevel(q *Query, obj types.Object, fset *token.FileSet) error {
|
||||
// globalReferrersPkgLevel uses go/ast and friends instead of go/types.
|
||||
// This affords a considerable performance benefit.
|
||||
// It comes at the cost of some code complexity.
|
||||
//
|
||||
// Here's a high level summary.
|
||||
//
|
||||
// The goal is to find references to the query object p.Q.
|
||||
// There are several possible scenarios, each handled differently.
|
||||
//
|
||||
// 1. We are looking in a package other than p, and p is not dot-imported.
|
||||
// This is the simplest case. Q must be referred to as n.Q,
|
||||
// where n is the name under which p is imported.
|
||||
// We look at all imports of p to gather all names under which it is imported.
|
||||
// (In the typical case, it is imported only once, under its default name.)
|
||||
// Then we look at all selector expressions and report any matches.
|
||||
//
|
||||
// 2. We are looking in a package other than p, and p is dot-imported.
|
||||
// In this case, Q will be referred to just as Q.
|
||||
// Furthermore, go/ast's object resolution will not be able to resolve
|
||||
// Q to any other object, unlike any local (file- or function- or block-scoped) object.
|
||||
// So we look at all matching identifiers and report all unresolvable ones.
|
||||
//
|
||||
// 3. We are looking in package p.
|
||||
// (Care must be taken to separate p and p_test (an xtest package),
|
||||
// and make sure that they are treated as separate packages.)
|
||||
// In this case, we give go/ast the entire package for object resolution,
|
||||
// instead of going file by file.
|
||||
// We then iterate over all identifiers that resolve to the query object.
|
||||
// (The query object itself has already been reported, so we don't re-report it.)
|
||||
//
|
||||
// We always skip all files that don't contain the string Q, as they cannot be
|
||||
// relevant to finding references to Q.
|
||||
//
|
||||
// We parse all files leniently. In the presence of parsing errors, results are best-effort.
|
||||
|
||||
// Scan the workspace and build the import graph.
|
||||
// Ignore broken packages.
|
||||
_, rev, _ := importgraph.Build(q.Build)
|
||||
|
||||
// Find the set of packages that directly import defpkg.
|
||||
defpkg := obj.Pkg().Path()
|
||||
defpkg = strings.TrimSuffix(defpkg, "_test") // package x_test actually has package name x
|
||||
defpkg = imports.VendorlessPath(defpkg) // remove vendor goop
|
||||
|
||||
users := rev[defpkg]
|
||||
if len(users) == 0 {
|
||||
users = make(map[string]bool)
|
||||
}
|
||||
// We also need to check defpkg itself, and its xtests.
|
||||
// For the reverse graph packages, we process xtests with the main package.
|
||||
// defpkg gets special handling; we must distinguish between in-package vs out-of-package.
|
||||
// To make the control flow below simpler, add defpkg and defpkg xtest placeholders.
|
||||
// Use "!test" instead of "_test" because "!" is not a valid character in an import path.
|
||||
// (More precisely, it is not guaranteed to be a valid character in an import path,
|
||||
// so it is unlikely that it will be in use. See https://golang.org/ref/spec#Import_declarations.)
|
||||
users[defpkg] = true
|
||||
users[defpkg+"!test"] = true
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defname := obj.Pkg().Name() // name of defining package, used for imports using import path only
|
||||
isxtest := strings.HasSuffix(defname, "_test") // indicates whether the query object is defined in an xtest package
|
||||
|
||||
name := obj.Name()
|
||||
namebytes := []byte(name) // byte slice version of query object name, for early filtering
|
||||
objpos := fset.Position(obj.Pos()) // position of query object, used to prevent re-emitting original decl
|
||||
|
||||
sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for u := range users {
|
||||
u := u
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
uIsXTest := strings.HasSuffix(u, "!test") // indicates whether this package is the special defpkg xtest package
|
||||
u = strings.TrimSuffix(u, "!test")
|
||||
|
||||
// Resolve package.
|
||||
sema <- struct{}{} // acquire token
|
||||
pkg, err := q.Build.Import(u, cwd, build.IgnoreVendor)
|
||||
<-sema // release token
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If we're not in the query package,
|
||||
// the object is in another package regardless,
|
||||
// so we want to process all files.
|
||||
// If we are in the query package,
|
||||
// we want to only process the files that are
|
||||
// part of that query package;
|
||||
// that set depends on whether the query package itself is an xtest.
|
||||
inQueryPkg := u == defpkg && isxtest == uIsXTest
|
||||
var files []string
|
||||
if !inQueryPkg || !isxtest {
|
||||
files = append(files, pkg.GoFiles...)
|
||||
files = append(files, pkg.TestGoFiles...)
|
||||
files = append(files, pkg.CgoFiles...) // use raw cgo files, as we're only parsing
|
||||
}
|
||||
if !inQueryPkg || isxtest {
|
||||
files = append(files, pkg.XTestGoFiles...)
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var deffiles map[string]*ast.File
|
||||
if inQueryPkg {
|
||||
deffiles = make(map[string]*ast.File)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer) // reusable buffer for reading files
|
||||
|
||||
for _, file := range files {
|
||||
if !buildutil.IsAbsPath(q.Build, file) {
|
||||
file = buildutil.JoinPath(q.Build, pkg.Dir, file)
|
||||
}
|
||||
buf.Reset()
|
||||
sema <- struct{}{} // acquire token
|
||||
src, err := readFile(q.Build, file, buf)
|
||||
<-sema // release token
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Fast path: If the object's name isn't present anywhere in the source, ignore the file.
|
||||
if !bytes.Contains(src, namebytes) {
|
||||
continue
|
||||
}
|
||||
|
||||
if inQueryPkg {
|
||||
// If we're in the query package, we defer final processing until we have
|
||||
// parsed all of the candidate files in the package.
|
||||
// Best effort; allow errors and use what we can from what remains.
|
||||
f, _ := parser.ParseFile(fset, file, src, parser.AllErrors)
|
||||
if f != nil {
|
||||
deffiles[file] = f
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// We aren't in the query package. Go file by file.
|
||||
|
||||
// Parse out only the imports, to check whether the defining package
|
||||
// was imported, and if so, under what names.
|
||||
// Best effort; allow errors and use what we can from what remains.
|
||||
f, _ := parser.ParseFile(fset, file, src, parser.ImportsOnly|parser.AllErrors)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// pkgnames is the set of names by which defpkg is imported in this file.
|
||||
// (Multiple imports in the same file are legal but vanishingly rare.)
|
||||
pkgnames := make([]string, 0, 1)
|
||||
var isdotimport bool
|
||||
for _, imp := range f.Imports {
|
||||
path, err := strconv.Unquote(imp.Path.Value)
|
||||
if err != nil || path != defpkg {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case imp.Name == nil:
|
||||
pkgnames = append(pkgnames, defname)
|
||||
case imp.Name.Name == ".":
|
||||
isdotimport = true
|
||||
default:
|
||||
pkgnames = append(pkgnames, imp.Name.Name)
|
||||
}
|
||||
}
|
||||
if len(pkgnames) == 0 && !isdotimport {
|
||||
// Defining package not imported, bail.
|
||||
continue
|
||||
}
|
||||
|
||||
// Re-parse the entire file.
|
||||
// Parse errors are ok; we'll do the best we can with a partial AST, if we have one.
|
||||
f, _ = parser.ParseFile(fset, file, src, parser.AllErrors)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Walk the AST looking for references.
|
||||
var refs []*ast.Ident
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
// Check selector expressions.
|
||||
// If the selector matches the target name,
|
||||
// and the expression is one of the names
|
||||
// that the defining package was imported under,
|
||||
// then we have a match.
|
||||
if sel, ok := n.(*ast.SelectorExpr); ok && sel.Sel.Name == name {
|
||||
if id, ok := sel.X.(*ast.Ident); ok {
|
||||
for _, n := range pkgnames {
|
||||
if n == id.Name {
|
||||
refs = append(refs, sel.Sel)
|
||||
// Don't recurse further, to avoid duplicate entries
|
||||
// from the dot import check below.
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Dot imports are special.
|
||||
// Objects imported from the defining package are placed in the package scope.
|
||||
// go/ast does not resolve them to an object.
|
||||
// At all other scopes (file, local), go/ast can do the resolution.
|
||||
// So we're looking for object-free idents with the right name.
|
||||
// The only other way to get something with the right name at the package scope
|
||||
// is to *be* the defining package. We handle that case separately (inQueryPkg).
|
||||
if isdotimport {
|
||||
if id, ok := n.(*ast.Ident); ok && id.Obj == nil && id.Name == name {
|
||||
refs = append(refs, id)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Emit any references we found.
|
||||
if len(refs) > 0 {
|
||||
q.Output(fset, &referrersPackageResult{
|
||||
pkg: types.NewPackage(pkg.ImportPath, pkg.Name),
|
||||
build: q.Build,
|
||||
fset: fset,
|
||||
refs: refs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// If we're in the query package, we've now collected all the files in the package.
|
||||
// (Or at least the ones that might contain references to the object.)
|
||||
// Find and emit refs.
|
||||
if inQueryPkg {
|
||||
// Bundle the files together into a package.
|
||||
// This does package-level object resolution.
|
||||
qpkg, _ := ast.NewPackage(fset, deffiles, nil, nil)
|
||||
// Look up the query object; we know that it is defined in the package scope.
|
||||
pkgobj := qpkg.Scope.Objects[name]
|
||||
if pkgobj == nil {
|
||||
panic("missing defpkg object for " + defpkg + "." + name)
|
||||
}
|
||||
// Find all references to the query object.
|
||||
var refs []*ast.Ident
|
||||
ast.Inspect(qpkg, func(n ast.Node) bool {
|
||||
if id, ok := n.(*ast.Ident); ok {
|
||||
// Check both that this is a reference to the query object
|
||||
// and that it is not the query object itself;
|
||||
// the query object itself was already emitted.
|
||||
if id.Obj == pkgobj && objpos != fset.Position(id.Pos()) {
|
||||
refs = append(refs, id)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(refs) > 0 {
|
||||
q.Output(fset, &referrersPackageResult{
|
||||
pkg: types.NewPackage(pkg.ImportPath, pkg.Name),
|
||||
build: q.Build,
|
||||
fset: fset,
|
||||
refs: refs,
|
||||
})
|
||||
}
|
||||
deffiles = nil // allow GC
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// findObject returns the object defined at the specified position.
|
||||
func findObject(fset *token.FileSet, info *types.Info, objposn token.Position) types.Object {
|
||||
good := func(obj types.Object) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
posn := fset.Position(obj.Pos())
|
||||
return posn.Filename == objposn.Filename && posn.Offset == objposn.Offset
|
||||
}
|
||||
for _, obj := range info.Defs {
|
||||
if good(obj) {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
for _, obj := range info.Implicits {
|
||||
if good(obj) {
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// same reports whether x and y are identical, or both are PkgNames
|
||||
// that import the same Package.
|
||||
//
|
||||
func sameObj(x, y types.Object) bool {
|
||||
if x == y {
|
||||
return true
|
||||
}
|
||||
if x, ok := x.(*types.PkgName); ok {
|
||||
if y, ok := y.(*types.PkgName); ok {
|
||||
return x.Imported() == y.Imported()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func clearInfoFields(info *loader.PackageInfo) {
|
||||
// TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
|
||||
// (Requires go/types change for Go 1.7.)
|
||||
// info.Pkg.Scope().ClearChildren()
|
||||
|
||||
// Discard the file ASTs and their accumulated type
|
||||
// information to save memory.
|
||||
info.Files = nil
|
||||
info.Defs = make(map[*ast.Ident]types.Object)
|
||||
info.Uses = make(map[*ast.Ident]types.Object)
|
||||
info.Implicits = make(map[ast.Node]types.Object)
|
||||
|
||||
// Also, disable future collection of wholly unneeded
|
||||
// type information for the package in case there is
|
||||
// more type-checking to do (augmentation).
|
||||
info.Types = nil
|
||||
info.Scopes = nil
|
||||
info.Selections = nil
|
||||
}
|
||||
|
||||
// -------- utils --------
|
||||
|
||||
// An deterministic ordering for token.Pos that doesn't
|
||||
// depend on the order in which packages were loaded.
|
||||
func lessPos(fset *token.FileSet, x, y token.Pos) bool {
|
||||
fx := fset.File(x)
|
||||
fy := fset.File(y)
|
||||
if fx != fy {
|
||||
return fx.Name() < fy.Name()
|
||||
}
|
||||
return x < y
|
||||
}
|
||||
|
||||
type byNamePos struct {
|
||||
fset *token.FileSet
|
||||
ids []*ast.Ident
|
||||
}
|
||||
|
||||
func (p byNamePos) Len() int { return len(p.ids) }
|
||||
func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
|
||||
func (p byNamePos) Less(i, j int) bool {
|
||||
return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
|
||||
}
|
||||
|
||||
// referrersInitialResult is the initial result of a "referrers" query.
|
||||
type referrersInitialResult struct {
|
||||
qinfo *loader.PackageInfo
|
||||
obj types.Object // object it denotes
|
||||
}
|
||||
|
||||
func (r *referrersInitialResult) PrintPlain(printf printfFunc) {
|
||||
printf(r.obj, "references to %s",
|
||||
types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg)))
|
||||
}
|
||||
|
||||
func (r *referrersInitialResult) JSON(fset *token.FileSet) []byte {
|
||||
var objpos string
|
||||
if pos := r.obj.Pos(); pos.IsValid() {
|
||||
objpos = fset.Position(pos).String()
|
||||
}
|
||||
return toJSON(&serial.ReferrersInitial{
|
||||
Desc: r.obj.String(),
|
||||
ObjPos: objpos,
|
||||
})
|
||||
}
|
||||
|
||||
// referrersPackageResult is the streaming result for one package of a "referrers" query.
|
||||
type referrersPackageResult struct {
|
||||
pkg *types.Package
|
||||
build *build.Context
|
||||
fset *token.FileSet
|
||||
refs []*ast.Ident // set of all other references to it
|
||||
}
|
||||
|
||||
// forEachRef calls f(id, text) for id in r.refs, in order.
|
||||
// Text is the text of the line on which id appears.
|
||||
func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) {
|
||||
// Show referring lines, like grep.
|
||||
type fileinfo struct {
|
||||
refs []*ast.Ident
|
||||
linenums []int // line number of refs[i]
|
||||
data chan interface{} // file contents or error
|
||||
}
|
||||
var fileinfos []*fileinfo
|
||||
fileinfosByName := make(map[string]*fileinfo)
|
||||
|
||||
// First pass: start the file reads concurrently.
|
||||
sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
|
||||
for _, ref := range r.refs {
|
||||
posn := r.fset.Position(ref.Pos())
|
||||
fi := fileinfosByName[posn.Filename]
|
||||
if fi == nil {
|
||||
fi = &fileinfo{data: make(chan interface{})}
|
||||
fileinfosByName[posn.Filename] = fi
|
||||
fileinfos = append(fileinfos, fi)
|
||||
|
||||
// First request for this file:
|
||||
// start asynchronous read.
|
||||
go func() {
|
||||
sema <- struct{}{} // acquire token
|
||||
content, err := readFile(r.build, posn.Filename, nil)
|
||||
<-sema // release token
|
||||
if err != nil {
|
||||
fi.data <- err
|
||||
} else {
|
||||
fi.data <- content
|
||||
}
|
||||
}()
|
||||
}
|
||||
fi.refs = append(fi.refs, ref)
|
||||
fi.linenums = append(fi.linenums, posn.Line)
|
||||
}
|
||||
|
||||
// Second pass: print refs in original order.
|
||||
// One line may have several refs at different columns.
|
||||
for _, fi := range fileinfos {
|
||||
v := <-fi.data // wait for I/O completion
|
||||
|
||||
// Print one item for all refs in a file that could not
|
||||
// be loaded (perhaps due to //line directives).
|
||||
if err, ok := v.(error); ok {
|
||||
var suffix string
|
||||
if more := len(fi.refs) - 1; more > 0 {
|
||||
suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
|
||||
}
|
||||
f(fi.refs[0], err.Error()+suffix)
|
||||
continue
|
||||
}
|
||||
|
||||
lines := bytes.Split(v.([]byte), []byte("\n"))
|
||||
for i, ref := range fi.refs {
|
||||
f(ref, string(lines[fi.linenums[i]-1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readFile is like ioutil.ReadFile, but
|
||||
// it goes through the virtualized build.Context.
|
||||
// If non-nil, buf must have been reset.
|
||||
func readFile(ctxt *build.Context, filename string, buf *bytes.Buffer) ([]byte, error) {
|
||||
rc, err := buildutil.OpenFile(ctxt, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
if buf == nil {
|
||||
buf = new(bytes.Buffer)
|
||||
}
|
||||
if _, err := io.Copy(buf, rc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (r *referrersPackageResult) PrintPlain(printf printfFunc) {
|
||||
r.foreachRef(func(id *ast.Ident, text string) {
|
||||
printf(id, "%s", text)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *referrersPackageResult) JSON(fset *token.FileSet) []byte {
|
||||
refs := serial.ReferrersPackage{Package: r.pkg.Path()}
|
||||
r.foreachRef(func(id *ast.Ident, text string) {
|
||||
refs.Refs = append(refs.Refs, serial.Ref{
|
||||
Pos: fset.Position(id.NamePos).String(),
|
||||
Text: text,
|
||||
})
|
||||
})
|
||||
return toJSON(refs)
|
||||
}
|
259
vendor/golang.org/x/tools/cmd/guru/serial/serial.go
generated
vendored
Normal file
259
vendor/golang.org/x/tools/cmd/guru/serial/serial.go
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package serial defines the guru's schema for -json output.
|
||||
//
|
||||
// The output of a guru query is a stream of one or more JSON objects.
|
||||
// This table shows the types of objects in the result stream for each
|
||||
// query type.
|
||||
//
|
||||
// Query Result stream
|
||||
// ----- -------------
|
||||
// callees Callees
|
||||
// callers Caller ...
|
||||
// callstack CallStack
|
||||
// definition Definition
|
||||
// describe Describe
|
||||
// freevars FreeVar ...
|
||||
// implements Implements
|
||||
// peers Peers
|
||||
// pointsto PointsTo ...
|
||||
// referrers ReferrersInitial ReferrersPackage ...
|
||||
// what What
|
||||
// whicherrs WhichErrs
|
||||
//
|
||||
// All 'pos' strings in the output are of the form "file:line:col",
|
||||
// where line is the 1-based line number and col is the 1-based byte index.
|
||||
package serial
|
||||
|
||||
// A Peers is the result of a 'peers' query.
|
||||
// If Allocs is empty, the selected channel can't point to anything.
|
||||
type Peers struct {
|
||||
Pos string `json:"pos"` // location of the selected channel op (<-)
|
||||
Type string `json:"type"` // type of the selected channel
|
||||
Allocs []string `json:"allocs,omitempty"` // locations of aliased make(chan) ops
|
||||
Sends []string `json:"sends,omitempty"` // locations of aliased ch<-x ops
|
||||
Receives []string `json:"receives,omitempty"` // locations of aliased <-ch ops
|
||||
Closes []string `json:"closes,omitempty"` // locations of aliased close(ch) ops
|
||||
}
|
||||
|
||||
// A "referrers" query emits a ReferrersInitial object followed by zero or
|
||||
// more ReferrersPackage objects, one per package that contains a reference.
|
||||
type (
|
||||
ReferrersInitial struct {
|
||||
ObjPos string `json:"objpos,omitempty"` // location of the definition
|
||||
Desc string `json:"desc"` // description of the denoted object
|
||||
}
|
||||
ReferrersPackage struct {
|
||||
Package string `json:"package"`
|
||||
Refs []Ref `json:"refs"` // non-empty list of references within this package
|
||||
}
|
||||
Ref struct {
|
||||
Pos string `json:"pos"` // location of all references
|
||||
Text string `json:"text"` // text of the referring line
|
||||
}
|
||||
)
|
||||
|
||||
// A Definition is the result of a 'definition' query.
|
||||
type Definition struct {
|
||||
ObjPos string `json:"objpos,omitempty"` // location of the definition
|
||||
Desc string `json:"desc"` // description of the denoted object
|
||||
}
|
||||
|
||||
// A Callees is the result of a 'callees' query.
|
||||
//
|
||||
// Callees is nonempty unless the call was a dynamic call on a
|
||||
// provably nil func or interface value.
|
||||
type (
|
||||
Callees struct {
|
||||
Pos string `json:"pos"` // location of selected call site
|
||||
Desc string `json:"desc"` // description of call site
|
||||
Callees []*Callee `json:"callees"`
|
||||
}
|
||||
Callee struct {
|
||||
Name string `json:"name"` // full name of called function
|
||||
Pos string `json:"pos"` // location of called function
|
||||
}
|
||||
)
|
||||
|
||||
// A Caller is one element of the slice returned by a 'callers' query.
|
||||
// (Callstack also contains a similar slice.)
|
||||
//
|
||||
// The root of the callgraph has an unspecified "Caller" string.
|
||||
type Caller struct {
|
||||
Pos string `json:"pos,omitempty"` // location of the calling function
|
||||
Desc string `json:"desc"` // description of call site
|
||||
Caller string `json:"caller"` // full name of calling function
|
||||
}
|
||||
|
||||
// A CallStack is the result of a 'callstack' query.
|
||||
// It indicates an arbitrary path from the root of the callgraph to
|
||||
// the query function.
|
||||
//
|
||||
// If the Callers slice is empty, the function was unreachable in this
|
||||
// analysis scope.
|
||||
type CallStack struct {
|
||||
Pos string `json:"pos"` // location of the selected function
|
||||
Target string `json:"target"` // the selected function
|
||||
Callers []Caller `json:"callers"` // enclosing calls, innermost first.
|
||||
}
|
||||
|
||||
// A FreeVar is one element of the slice returned by a 'freevars'
|
||||
// query. Each one identifies an expression referencing a local
|
||||
// identifier defined outside the selected region.
|
||||
type FreeVar struct {
|
||||
Pos string `json:"pos"` // location of the identifier's definition
|
||||
Kind string `json:"kind"` // one of {var,func,type,const,label}
|
||||
Ref string `json:"ref"` // referring expression (e.g. "x" or "x.y.z")
|
||||
Type string `json:"type"` // type of the expression
|
||||
}
|
||||
|
||||
// An Implements contains the result of an 'implements' query.
|
||||
// It describes the queried type, the set of named non-empty interface
|
||||
// types to which it is assignable, and the set of named/*named types
|
||||
// (concrete or non-empty interface) which may be assigned to it.
|
||||
//
|
||||
type Implements struct {
|
||||
T ImplementsType `json:"type,omitempty"` // the queried type
|
||||
AssignableTo []ImplementsType `json:"to,omitempty"` // types assignable to T
|
||||
AssignableFrom []ImplementsType `json:"from,omitempty"` // interface types assignable from T
|
||||
AssignableFromPtr []ImplementsType `json:"fromptr,omitempty"` // interface types assignable only from *T
|
||||
|
||||
// The following fields are set only if the query was a method.
|
||||
// Assignable{To,From,FromPtr}Method[i] is the corresponding
|
||||
// method of type Assignable{To,From,FromPtr}[i], or blank
|
||||
// {"",""} if that type lacks the method.
|
||||
Method *DescribeMethod `json:"method,omitempty"` // the queried method
|
||||
AssignableToMethod []DescribeMethod `json:"to_method,omitempty"`
|
||||
AssignableFromMethod []DescribeMethod `json:"from_method,omitempty"`
|
||||
AssignableFromPtrMethod []DescribeMethod `json:"fromptr_method,omitempty"`
|
||||
}
|
||||
|
||||
// An ImplementsType describes a single type as part of an 'implements' query.
|
||||
type ImplementsType struct {
|
||||
Name string `json:"name"` // full name of the type
|
||||
Pos string `json:"pos"` // location of its definition
|
||||
Kind string `json:"kind"` // "basic", "array", etc
|
||||
}
|
||||
|
||||
// A SyntaxNode is one element of a stack of enclosing syntax nodes in
|
||||
// a "what" query.
|
||||
type SyntaxNode struct {
|
||||
Description string `json:"desc"` // description of syntax tree
|
||||
Start int `json:"start"` // start byte offset, 0-based
|
||||
End int `json:"end"` // end byte offset
|
||||
}
|
||||
|
||||
// A What is the result of the "what" query, which quickly identifies
|
||||
// the selection, parsing only a single file. It is intended for use
|
||||
// in low-latency GUIs.
|
||||
type What struct {
|
||||
Enclosing []SyntaxNode `json:"enclosing"` // enclosing nodes of syntax tree
|
||||
Modes []string `json:"modes"` // query modes enabled for this selection.
|
||||
SrcDir string `json:"srcdir,omitempty"` // $GOROOT src directory containing queried package
|
||||
ImportPath string `json:"importpath,omitempty"` // import path of queried package
|
||||
Object string `json:"object,omitempty"` // name of identified object, if any
|
||||
SameIDs []string `json:"sameids,omitempty"` // locations of references to same object
|
||||
}
|
||||
|
||||
// A PointsToLabel describes a pointer analysis label.
|
||||
//
|
||||
// A "label" is an object that may be pointed to by a pointer, map,
|
||||
// channel, 'func', slice or interface. Labels include:
|
||||
// - functions
|
||||
// - globals
|
||||
// - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s))
|
||||
// - stack- and heap-allocated variables (including composite literals)
|
||||
// - arrays allocated by append()
|
||||
// - channels, maps and arrays created by make()
|
||||
// - and their subelements, e.g. "alloc.y[*].z"
|
||||
//
|
||||
type PointsToLabel struct {
|
||||
Pos string `json:"pos"` // location of syntax that allocated the object
|
||||
Desc string `json:"desc"` // description of the label
|
||||
}
|
||||
|
||||
// A PointsTo is one element of the result of a 'pointsto' query on an
|
||||
// expression. It describes a single pointer: its type and the set of
|
||||
// "labels" it points to.
|
||||
//
|
||||
// If the pointer is of interface type, it will have one PTS entry
|
||||
// describing each concrete type that it may contain. For each
|
||||
// concrete type that is a pointer, the PTS entry describes the labels
|
||||
// it may point to. The same is true for reflect.Values, except the
|
||||
// dynamic types needn't be concrete.
|
||||
//
|
||||
type PointsTo struct {
|
||||
Type string `json:"type"` // (concrete) type of the pointer
|
||||
NamePos string `json:"namepos,omitempty"` // location of type defn, if Named
|
||||
Labels []PointsToLabel `json:"labels,omitempty"` // pointed-to objects
|
||||
}
|
||||
|
||||
// A DescribeValue is the additional result of a 'describe' query
|
||||
// if the selection indicates a value or expression.
|
||||
type DescribeValue struct {
|
||||
Type string `json:"type"` // type of the expression
|
||||
Value string `json:"value,omitempty"` // value of the expression, if constant
|
||||
ObjPos string `json:"objpos,omitempty"` // location of the definition, if an Ident
|
||||
}
|
||||
|
||||
type DescribeMethod struct {
|
||||
Name string `json:"name"` // method name, as defined by types.Selection.String()
|
||||
Pos string `json:"pos"` // location of the method's definition
|
||||
}
|
||||
|
||||
// A DescribeType is the additional result of a 'describe' query
|
||||
// if the selection indicates a type.
|
||||
type DescribeType struct {
|
||||
Type string `json:"type"` // the string form of the type
|
||||
NamePos string `json:"namepos,omitempty"` // location of definition of type, if named
|
||||
NameDef string `json:"namedef,omitempty"` // underlying definition of type, if named
|
||||
Methods []DescribeMethod `json:"methods,omitempty"` // methods of the type
|
||||
}
|
||||
|
||||
type DescribeMember struct {
|
||||
Name string `json:"name"` // name of member
|
||||
Type string `json:"type,omitempty"` // type of member (underlying, if 'type')
|
||||
Value string `json:"value,omitempty"` // value of member (if 'const')
|
||||
Pos string `json:"pos"` // location of definition of member
|
||||
Kind string `json:"kind"` // one of {var,const,func,type}
|
||||
Methods []DescribeMethod `json:"methods,omitempty"` // methods (if member is a type)
|
||||
}
|
||||
|
||||
// A DescribePackage is the additional result of a 'describe' if
|
||||
// the selection indicates a package.
|
||||
type DescribePackage struct {
|
||||
Path string `json:"path"` // import path of the package
|
||||
Members []*DescribeMember `json:"members,omitempty"` // accessible members of the package
|
||||
}
|
||||
|
||||
// A Describe is the result of a 'describe' query.
|
||||
// It may contain an element describing the selected semantic entity
|
||||
// in detail.
|
||||
type Describe struct {
|
||||
Desc string `json:"desc"` // description of the selected syntax node
|
||||
Pos string `json:"pos"` // location of the selected syntax node
|
||||
Detail string `json:"detail,omitempty"` // one of {package, type, value}, or "".
|
||||
|
||||
// At most one of the following fields is populated:
|
||||
// the one specified by 'detail'.
|
||||
Package *DescribePackage `json:"package,omitempty"`
|
||||
Type *DescribeType `json:"type,omitempty"`
|
||||
Value *DescribeValue `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// A WhichErrs is the result of a 'whicherrs' query.
|
||||
// It contains the position of the queried error and the possible globals,
|
||||
// constants, and types it may point to.
|
||||
type WhichErrs struct {
|
||||
ErrPos string `json:"errpos,omitempty"` // location of queried error
|
||||
Globals []string `json:"globals,omitempty"` // locations of globals
|
||||
Constants []string `json:"constants,omitempty"` // locations of constants
|
||||
Types []WhichErrsType `json:"types,omitempty"` // Types
|
||||
}
|
||||
|
||||
type WhichErrsType struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Position string `json:"position,omitempty"`
|
||||
}
|
2
vendor/golang.org/x/tools/cmd/guru/testdata/src/README.txt
generated
vendored
Normal file
2
vendor/golang.org/x/tools/cmd/guru/testdata/src/README.txt
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
This is not a Go source file.
|
||||
Used by TestIssue14684.
|
23
vendor/golang.org/x/tools/cmd/guru/testdata/src/alias/alias.go
generated
vendored
Normal file
23
vendor/golang.org/x/tools/cmd/guru/testdata/src/alias/alias.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Tests of Go 1.9 type aliases.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See alias.golden for expected query results.
|
||||
|
||||
package alias // @describe describe-pkg "alias"
|
||||
|
||||
type I interface { // @implements implements-I "I"
|
||||
f()
|
||||
}
|
||||
|
||||
type N int
|
||||
|
||||
func (N) f() {}
|
||||
|
||||
type M = N // @describe describe-def-M "M"
|
||||
var m M // @describe describe-ref-M "M"
|
||||
|
||||
type O N // @describe describe-O "O"
|
||||
|
||||
type P = struct{ N } // @describe describe-P "N"
|
||||
|
||||
type U = undefined // @describe describe-U "U"
|
||||
type _ = undefined // @describe describe-undefined "undefined"
|
47
vendor/golang.org/x/tools/cmd/guru/testdata/src/alias/alias.golden
generated
vendored
Normal file
47
vendor/golang.org/x/tools/cmd/guru/testdata/src/alias/alias.golden
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
-------- @describe describe-pkg --------
|
||||
definition of package "alias"
|
||||
type I interface{f()}
|
||||
method (I) f()
|
||||
type M = N
|
||||
method (N) f()
|
||||
type N int
|
||||
method (N) f()
|
||||
type O int
|
||||
type P = struct{N}
|
||||
method (struct{N}) f()
|
||||
type U = invalid type
|
||||
var m N
|
||||
|
||||
-------- @implements implements-I --------
|
||||
interface type I
|
||||
is implemented by basic type N
|
||||
|
||||
-------- @describe describe-def-M --------
|
||||
alias of type N (size 8, align 8)
|
||||
defined as int
|
||||
Methods:
|
||||
method (N) f()
|
||||
|
||||
-------- @describe describe-ref-M --------
|
||||
alias of type N (size 8, align 8)
|
||||
defined as int
|
||||
Methods:
|
||||
method (N) f()
|
||||
|
||||
-------- @describe describe-O --------
|
||||
definition of type O (size 8, align 8)
|
||||
No methods.
|
||||
|
||||
-------- @describe describe-P --------
|
||||
type struct{N} (size 8, align 8)
|
||||
Methods:
|
||||
method (struct{N}) f()
|
||||
Fields:
|
||||
N N
|
||||
|
||||
-------- @describe describe-U --------
|
||||
alias of type invalid type
|
||||
|
||||
-------- @describe describe-undefined --------
|
||||
identifier
|
||||
|
16
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls-json/main.go
generated
vendored
Normal file
16
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
// Tests of call-graph queries, -format=json.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See calls-json.golden for expected query results.
|
||||
|
||||
func call(f func()) {
|
||||
f() // @callees @callees-f "f"
|
||||
}
|
||||
|
||||
func main() {
|
||||
call(func() {
|
||||
// @callers callers-main.anon "^"
|
||||
// @callstack callstack-main.anon "^"
|
||||
})
|
||||
}
|
28
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls-json/main.golden
generated
vendored
Normal file
28
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
-------- @callees @callees-f --------
|
||||
{
|
||||
"pos": "testdata/src/calls-json/main.go:8:3",
|
||||
"desc": "dynamic function call",
|
||||
"callees": [
|
||||
{
|
||||
"name": "calls-json.main$1",
|
||||
"pos": "testdata/src/calls-json/main.go:12:7"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @callstack callstack-main.anon --------
|
||||
{
|
||||
"pos": "testdata/src/calls-json/main.go:12:7",
|
||||
"target": "calls-json.main$1",
|
||||
"callers": [
|
||||
{
|
||||
"pos": "testdata/src/calls-json/main.go:8:3",
|
||||
"desc": "dynamic function call",
|
||||
"caller": "calls-json.call"
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/calls-json/main.go:12:6",
|
||||
"desc": "static function call",
|
||||
"caller": "calls-json.main"
|
||||
}
|
||||
]
|
||||
}
|
129
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls/main.go
generated
vendored
Normal file
129
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls/main.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Tests of call-graph queries.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See calls.golden for expected query results.
|
||||
|
||||
func A(x *int) { // @pointsto pointsto-A-x "x"
|
||||
// @callers callers-A "^"
|
||||
// @callstack callstack-A "^"
|
||||
}
|
||||
|
||||
func B(x *int) { // @pointsto pointsto-B-x "x"
|
||||
// @callers callers-B "^"
|
||||
}
|
||||
|
||||
func foo() {
|
||||
}
|
||||
|
||||
// apply is not (yet) treated context-sensitively.
|
||||
func apply(f func(x *int), x *int) {
|
||||
f(x) // @callees callees-apply "f"
|
||||
// @callers callers-apply "^"
|
||||
}
|
||||
|
||||
// store *is* treated context-sensitively,
|
||||
// so the points-to sets for pc, pd are precise.
|
||||
func store(ptr **int, value *int) {
|
||||
*ptr = value
|
||||
// @callers callers-store "^"
|
||||
}
|
||||
|
||||
func call(f func() *int) {
|
||||
// Result points to anon function.
|
||||
f() // @pointsto pointsto-result-f "f"
|
||||
|
||||
// Target of call is anon function.
|
||||
f() // @callees callees-main.call-f "f"
|
||||
|
||||
// @callers callers-main.call "^"
|
||||
}
|
||||
|
||||
func main() {
|
||||
var a, b int
|
||||
go apply(A, &a) // @callees callees-main-apply1 "app"
|
||||
defer apply(B, &b)
|
||||
|
||||
var c, d int
|
||||
var pc, pd *int // @pointsto pointsto-pc "pc"
|
||||
store(&pc, &c)
|
||||
store(&pd, &d)
|
||||
_ = pd // @pointsto pointsto-pd "pd"
|
||||
|
||||
call(func() *int {
|
||||
// We are called twice from main.call
|
||||
// @callers callers-main.anon "^"
|
||||
return &a
|
||||
})
|
||||
|
||||
// Errors
|
||||
_ = "no function call here" // @callees callees-err-no-call "no"
|
||||
print("builtin") // @callees callees-err-builtin "builtin"
|
||||
_ = string("type conversion") // @callees callees-err-conversion "str"
|
||||
call(nil) // @callees callees-err-bad-selection "call\\(nil"
|
||||
if false {
|
||||
main() // @callees callees-err-deadcode1 "main"
|
||||
}
|
||||
var nilFunc func()
|
||||
nilFunc() // @callees callees-err-nil-func "nilFunc"
|
||||
var i interface {
|
||||
f()
|
||||
}
|
||||
i.f() // @callees callees-err-nil-interface "i.f"
|
||||
|
||||
i = new(myint)
|
||||
i.f() // @callees callees-not-a-wrapper "f"
|
||||
|
||||
// statically dispatched calls. Handled specially by callees, so test that they work.
|
||||
foo() // @callees callees-static-call "foo"
|
||||
fmt.Println() // @callees callees-qualified-call "Println"
|
||||
m := new(method)
|
||||
m.f() // @callees callees-static-method-call "f"
|
||||
g := new(embeddedIface)
|
||||
g.iface = m
|
||||
g.f() // @callees callees-implicit-selection-method-call "f"
|
||||
}
|
||||
|
||||
type myint int
|
||||
|
||||
func (myint) f() {
|
||||
// @callers callers-not-a-wrapper "^"
|
||||
}
|
||||
|
||||
type method int
|
||||
|
||||
func (method) f() {
|
||||
}
|
||||
|
||||
type embeddedIface struct {
|
||||
iface
|
||||
}
|
||||
|
||||
type iface interface {
|
||||
f()
|
||||
}
|
||||
|
||||
var dynamic = func() {}
|
||||
|
||||
func deadcode() {
|
||||
main() // @callees callees-err-deadcode2 "main"
|
||||
// @callers callers-err-deadcode "^"
|
||||
// @callstack callstack-err-deadcode "^"
|
||||
|
||||
// Within dead code, dynamic calls have no callees.
|
||||
dynamic() // @callees callees-err-deadcode3 "dynamic"
|
||||
}
|
||||
|
||||
// This code belongs to init.
|
||||
var global = 123 // @callers callers-global "global"
|
||||
|
||||
// The package initializer may be called by other packages' inits, or
|
||||
// in this case, the root of the callgraph. The source-level init functions
|
||||
// are in turn called by it.
|
||||
func init() {
|
||||
// @callstack callstack-init "^"
|
||||
}
|
125
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls/main.golden
generated
vendored
Normal file
125
vendor/golang.org/x/tools/cmd/guru/testdata/src/calls/main.golden
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
-------- @pointsto pointsto-A-x --------
|
||||
this *int may point to these objects:
|
||||
a
|
||||
b
|
||||
|
||||
-------- @callstack callstack-A --------
|
||||
Found a call path from root to calls.A
|
||||
calls.A
|
||||
dynamic function call from calls.apply
|
||||
concurrent static function call from calls.main
|
||||
|
||||
-------- @pointsto pointsto-B-x --------
|
||||
this *int may point to these objects:
|
||||
a
|
||||
b
|
||||
|
||||
-------- @callers callers-B --------
|
||||
calls.B is called from these 1 sites:
|
||||
dynamic function call from calls.apply
|
||||
|
||||
-------- @callees callees-apply --------
|
||||
this dynamic function call dispatches to:
|
||||
calls.A
|
||||
calls.B
|
||||
|
||||
-------- @callers callers-apply --------
|
||||
calls.apply is called from these 2 sites:
|
||||
concurrent static function call from calls.main
|
||||
deferred static function call from calls.main
|
||||
|
||||
-------- @callers callers-store --------
|
||||
calls.store is called from these 2 sites:
|
||||
static function call from calls.main
|
||||
static function call from calls.main
|
||||
|
||||
-------- @pointsto pointsto-result-f --------
|
||||
this func() *int may point to these objects:
|
||||
calls.main$1
|
||||
|
||||
-------- @callees callees-main.call-f --------
|
||||
this dynamic function call dispatches to:
|
||||
calls.main$1
|
||||
|
||||
-------- @callers callers-main.call --------
|
||||
calls.call is called from these 2 sites:
|
||||
static function call from calls.main
|
||||
static function call from calls.main
|
||||
|
||||
-------- @callees callees-main-apply1 --------
|
||||
this static function call dispatches to:
|
||||
calls.apply
|
||||
|
||||
-------- @pointsto pointsto-pc --------
|
||||
this *int may point to these objects:
|
||||
c
|
||||
|
||||
-------- @pointsto pointsto-pd --------
|
||||
this *int may point to these objects:
|
||||
d
|
||||
|
||||
-------- @callees callees-err-no-call --------
|
||||
|
||||
Error: there is no function call here
|
||||
-------- @callees callees-err-builtin --------
|
||||
|
||||
Error: this is a call to the built-in 'print' operator
|
||||
-------- @callees callees-err-conversion --------
|
||||
|
||||
Error: this is a type conversion, not a function call
|
||||
-------- @callees callees-err-bad-selection --------
|
||||
|
||||
Error: ambiguous selection within function call (or conversion)
|
||||
-------- @callees callees-err-deadcode1 --------
|
||||
this static function call dispatches to:
|
||||
calls.main
|
||||
|
||||
-------- @callees callees-err-nil-func --------
|
||||
dynamic function call on nil value
|
||||
|
||||
-------- @callees callees-err-nil-interface --------
|
||||
dynamic method call on nil value
|
||||
|
||||
-------- @callees callees-not-a-wrapper --------
|
||||
this dynamic method call dispatches to:
|
||||
(calls.myint).f
|
||||
|
||||
-------- @callees callees-static-call --------
|
||||
this static function call dispatches to:
|
||||
calls.foo
|
||||
|
||||
-------- @callees callees-qualified-call --------
|
||||
this static function call dispatches to:
|
||||
fmt.Println
|
||||
|
||||
-------- @callees callees-static-method-call --------
|
||||
this static function call dispatches to:
|
||||
(calls.method).f
|
||||
|
||||
-------- @callees callees-implicit-selection-method-call --------
|
||||
this dynamic method call dispatches to:
|
||||
(calls.method).f
|
||||
|
||||
-------- @callers callers-not-a-wrapper --------
|
||||
(calls.myint).f is called from these 1 sites:
|
||||
dynamic method call from calls.main
|
||||
|
||||
-------- @callees callees-err-deadcode2 --------
|
||||
this static function call dispatches to:
|
||||
calls.main
|
||||
|
||||
-------- @callstack callstack-err-deadcode --------
|
||||
calls.deadcode is unreachable in this analysis scope
|
||||
|
||||
-------- @callees callees-err-deadcode3 --------
|
||||
|
||||
Error: this call site is unreachable in this analysis
|
||||
-------- @callers callers-global --------
|
||||
calls.init is called from these 1 sites:
|
||||
the root of the call graph
|
||||
|
||||
-------- @callstack callstack-init --------
|
||||
Found a call path from root to calls.init#1
|
||||
calls.init#1
|
||||
static function call from calls.init
|
||||
|
66
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main.go
generated
vendored
Normal file
66
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package definition
|
||||
|
||||
// Tests of 'definition' query, -json output.
|
||||
// See golang.org/x/tools/cmd/guru/guru_test.go for explanation.
|
||||
// See main.golden for expected query results.
|
||||
|
||||
// TODO(adonovan): test: selection of member of same package defined in another file.
|
||||
|
||||
import (
|
||||
"lib"
|
||||
lib2 "lib"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var _ int // @definition builtin "int"
|
||||
|
||||
var _ undef // @definition lexical-undef "undef"
|
||||
var x lib.T // @definition lexical-pkgname "lib"
|
||||
f() // @definition lexical-func "f"
|
||||
print(x) // @definition lexical-var "x"
|
||||
if x := ""; x == "" { // @definition lexical-shadowing "x"
|
||||
}
|
||||
|
||||
var _ lib.Type // @definition qualified-type "Type"
|
||||
var _ lib.Func // @definition qualified-func "Func"
|
||||
var _ lib.Var // @definition qualified-var "Var"
|
||||
var _ lib.Const // @definition qualified-const "Const"
|
||||
var _ lib2.Type // @definition qualified-type-renaming "Type"
|
||||
var _ lib.Nonesuch // @definition qualified-nomember "Nonesuch"
|
||||
|
||||
var u U
|
||||
print(u.field) // @definition select-field "field"
|
||||
u.method() // @definition select-method "method"
|
||||
}
|
||||
|
||||
func f()
|
||||
|
||||
type T struct{ field int }
|
||||
|
||||
func (T) method()
|
||||
|
||||
type U struct{ T }
|
||||
|
||||
type V1 struct {
|
||||
W // @definition embedded-other-file "W"
|
||||
}
|
||||
|
||||
type V2 struct {
|
||||
*W // @definition embedded-other-file-pointer "W"
|
||||
}
|
||||
|
||||
type V3 struct {
|
||||
int // @definition embedded-basic "int"
|
||||
}
|
||||
|
||||
type V4 struct {
|
||||
*int // @definition embedded-basic-pointer "int"
|
||||
}
|
||||
|
||||
type V5 struct {
|
||||
lib.Type // @definition embedded-other-pkg "Type"
|
||||
}
|
||||
|
||||
type V6 struct {
|
||||
T // @definition embedded-same-file "T"
|
||||
}
|
90
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main.golden
generated
vendored
Normal file
90
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
-------- @definition builtin --------
|
||||
|
||||
Error: int is built in
|
||||
-------- @definition lexical-undef --------
|
||||
|
||||
Error: no object for identifier
|
||||
-------- @definition lexical-pkgname --------
|
||||
{
|
||||
"objpos": "testdata/src/definition-json/main.go:10:2",
|
||||
"desc": "package lib"
|
||||
}
|
||||
-------- @definition lexical-func --------
|
||||
{
|
||||
"objpos": "$GOPATH/src/definition-json/main.go:36:6",
|
||||
"desc": "func f"
|
||||
}
|
||||
-------- @definition lexical-var --------
|
||||
{
|
||||
"objpos": "$GOPATH/src/definition-json/main.go:18:6",
|
||||
"desc": "var x"
|
||||
}
|
||||
-------- @definition lexical-shadowing --------
|
||||
{
|
||||
"objpos": "$GOPATH/src/definition-json/main.go:21:5",
|
||||
"desc": "var x"
|
||||
}
|
||||
-------- @definition qualified-type --------
|
||||
{
|
||||
"objpos": "testdata/src/lib/lib.go:3:6",
|
||||
"desc": "type lib.Type"
|
||||
}
|
||||
-------- @definition qualified-func --------
|
||||
{
|
||||
"objpos": "testdata/src/lib/lib.go:9:6",
|
||||
"desc": "func lib.Func"
|
||||
}
|
||||
-------- @definition qualified-var --------
|
||||
{
|
||||
"objpos": "testdata/src/lib/lib.go:14:5",
|
||||
"desc": "var lib.Var"
|
||||
}
|
||||
-------- @definition qualified-const --------
|
||||
{
|
||||
"objpos": "testdata/src/lib/lib.go:12:7",
|
||||
"desc": "const lib.Const"
|
||||
}
|
||||
-------- @definition qualified-type-renaming --------
|
||||
{
|
||||
"objpos": "testdata/src/lib/lib.go:3:6",
|
||||
"desc": "type lib.Type"
|
||||
}
|
||||
-------- @definition qualified-nomember --------
|
||||
|
||||
Error: couldn't find declaration of Nonesuch in "lib"
|
||||
-------- @definition select-field --------
|
||||
{
|
||||
"objpos": "testdata/src/definition-json/main.go:38:16",
|
||||
"desc": "field field int"
|
||||
}
|
||||
-------- @definition select-method --------
|
||||
{
|
||||
"objpos": "testdata/src/definition-json/main.go:40:10",
|
||||
"desc": "func (T).method()"
|
||||
}
|
||||
-------- @definition embedded-other-file --------
|
||||
{
|
||||
"objpos": "testdata/src/definition-json/type.go:3:6",
|
||||
"desc": "type W int"
|
||||
}
|
||||
-------- @definition embedded-other-file-pointer --------
|
||||
{
|
||||
"objpos": "testdata/src/definition-json/type.go:3:6",
|
||||
"desc": "type W int"
|
||||
}
|
||||
-------- @definition embedded-basic --------
|
||||
|
||||
Error: int is built in
|
||||
-------- @definition embedded-basic-pointer --------
|
||||
|
||||
Error: int is built in
|
||||
-------- @definition embedded-other-pkg --------
|
||||
{
|
||||
"objpos": "testdata/src/lib/lib.go:3:6",
|
||||
"desc": "type lib.Type"
|
||||
}
|
||||
-------- @definition embedded-same-file --------
|
||||
{
|
||||
"objpos": "$GOPATH/src/definition-json/main.go:38:6",
|
||||
"desc": "type T"
|
||||
}
|
5
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main19.go
generated
vendored
Normal file
5
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main19.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package definition
|
||||
|
||||
import "nosuchpkg"
|
||||
|
||||
var _ nosuchpkg.T // @definition qualified-nopkg "nosuchpkg"
|
5
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main19.golden
generated
vendored
Normal file
5
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/main19.golden
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
-------- @definition qualified-nopkg --------
|
||||
{
|
||||
"objpos": "testdata/src/definition-json/main19.go:3:8",
|
||||
"desc": "package nosuchpkg"
|
||||
}
|
3
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/type.go
generated
vendored
Normal file
3
vendor/golang.org/x/tools/cmd/guru/testdata/src/definition-json/type.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package definition
|
||||
|
||||
type W int
|
29
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe-json/main.go
generated
vendored
Normal file
29
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package describe // @describe pkgdecl "describe"
|
||||
|
||||
// Tests of 'describe' query, -format=json.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See describe-json.golden for expected query results.
|
||||
|
||||
func main() {
|
||||
var s struct{ x [3]int }
|
||||
p := &s.x[0] // @describe desc-val-p "p"
|
||||
_ = p
|
||||
|
||||
var i I = C(0)
|
||||
if i == nil {
|
||||
i = new(D)
|
||||
}
|
||||
print(i) // @describe desc-val-i "\\bi\\b"
|
||||
|
||||
go main() // @describe desc-stmt "go"
|
||||
}
|
||||
|
||||
type I interface {
|
||||
f()
|
||||
}
|
||||
|
||||
type C int // @describe desc-type-C "C"
|
||||
type D struct{}
|
||||
|
||||
func (c C) f() {}
|
||||
func (d *D) f() {}
|
96
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe-json/main.golden
generated
vendored
Normal file
96
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
-------- @describe pkgdecl --------
|
||||
{
|
||||
"desc": "definition of package \"describe-json\"",
|
||||
"pos": "testdata/src/describe-json/main.go:1:9",
|
||||
"detail": "package",
|
||||
"package": {
|
||||
"path": "describe-json",
|
||||
"members": [
|
||||
{
|
||||
"name": "C",
|
||||
"type": "int",
|
||||
"pos": "testdata/src/describe-json/main.go:25:6",
|
||||
"kind": "type",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (C) f()",
|
||||
"pos": "testdata/src/describe-json/main.go:28:12"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "D",
|
||||
"type": "struct{}",
|
||||
"pos": "testdata/src/describe-json/main.go:26:6",
|
||||
"kind": "type",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (*D) f()",
|
||||
"pos": "testdata/src/describe-json/main.go:29:13"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "I",
|
||||
"type": "interface{f()}",
|
||||
"pos": "testdata/src/describe-json/main.go:21:6",
|
||||
"kind": "type",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (I) f()",
|
||||
"pos": "testdata/src/describe-json/main.go:22:2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "main",
|
||||
"type": "func()",
|
||||
"pos": "testdata/src/describe-json/main.go:7:6",
|
||||
"kind": "func"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
-------- @describe desc-val-p --------
|
||||
{
|
||||
"desc": "identifier",
|
||||
"pos": "testdata/src/describe-json/main.go:9:2",
|
||||
"detail": "value",
|
||||
"value": {
|
||||
"type": "*int",
|
||||
"objpos": "testdata/src/describe-json/main.go:9:2"
|
||||
}
|
||||
}
|
||||
-------- @describe desc-val-i --------
|
||||
{
|
||||
"desc": "identifier",
|
||||
"pos": "testdata/src/describe-json/main.go:16:8",
|
||||
"detail": "value",
|
||||
"value": {
|
||||
"type": "I",
|
||||
"objpos": "testdata/src/describe-json/main.go:12:6"
|
||||
}
|
||||
}
|
||||
-------- @describe desc-stmt --------
|
||||
{
|
||||
"desc": "go statement",
|
||||
"pos": "testdata/src/describe-json/main.go:18:2",
|
||||
"detail": "unknown"
|
||||
}
|
||||
-------- @describe desc-type-C --------
|
||||
{
|
||||
"desc": "definition of type C (size 8, align 8)",
|
||||
"pos": "testdata/src/describe-json/main.go:25:6",
|
||||
"detail": "type",
|
||||
"type": {
|
||||
"type": "C",
|
||||
"namepos": "testdata/src/describe-json/main.go:25:6",
|
||||
"namedef": "int",
|
||||
"methods": [
|
||||
{
|
||||
"name": "method (C) f()",
|
||||
"pos": "testdata/src/describe-json/main.go:28:12"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
102
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main.go
generated
vendored
Normal file
102
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package describe // @describe pkgdecl "describe"
|
||||
|
||||
// Tests of 'describe' query.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See describe.golden for expected query results.
|
||||
|
||||
// TODO(adonovan): more coverage of the (extensive) logic.
|
||||
|
||||
import (
|
||||
"lib"
|
||||
_ "unsafe" // @describe unsafe "unsafe"
|
||||
)
|
||||
|
||||
type cake float64 // @describe type-ref-builtin "float64"
|
||||
|
||||
const c = iota // @describe const-ref-iota "iota"
|
||||
|
||||
const pi = 3.141 // @describe const-def-pi "pi"
|
||||
const pie = cake(pi) // @describe const-def-pie "pie"
|
||||
const _ = pi // @describe const-ref-pi "pi"
|
||||
|
||||
var global = new(string) // NB: ssa.Global is indirect, i.e. **string
|
||||
|
||||
func main() { // @describe func-def-main "main"
|
||||
// func objects
|
||||
_ = main // @describe func-ref-main "main"
|
||||
_ = (*C).f // @describe func-ref-*C.f "..C..f"
|
||||
_ = D.f // @describe func-ref-D.f "D.f"
|
||||
_ = I.f // @describe func-ref-I.f "I.f"
|
||||
var d D // @describe type-D "D"
|
||||
var i I // @describe type-I "I"
|
||||
_ = d.f // @describe func-ref-d.f "d.f"
|
||||
_ = i.f // @describe func-ref-i.f "i.f"
|
||||
|
||||
var dptr *D // @describe ptr-with-nonptr-methods "dptr"
|
||||
_ = dptr
|
||||
|
||||
// var objects
|
||||
anon := func() {
|
||||
_ = d // @describe ref-lexical-d "d"
|
||||
}
|
||||
_ = anon // @describe ref-anon "anon"
|
||||
_ = global // @describe ref-global "global"
|
||||
|
||||
// SSA affords some local flow sensitivity.
|
||||
var a, b int
|
||||
var x = &a // @describe var-def-x-1 "x"
|
||||
_ = x // @describe var-ref-x-1 "x"
|
||||
x = &b // @describe var-def-x-2 "x"
|
||||
_ = x // @describe var-ref-x-2 "x"
|
||||
|
||||
i = new(C) // @describe var-ref-i-C "i"
|
||||
if i != nil {
|
||||
i = D{} // @describe var-ref-i-D "i"
|
||||
}
|
||||
print(i) // @describe var-ref-i "\\bi\\b"
|
||||
|
||||
// const objects
|
||||
const localpi = 3.141 // @describe const-local-pi "localpi"
|
||||
const localpie = cake(pi) // @describe const-local-pie "localpie"
|
||||
const _ = localpi // @describe const-ref-localpi "localpi"
|
||||
|
||||
// type objects
|
||||
type T int // @describe type-def-T "T"
|
||||
var three T = 3 // @describe type-ref-T "T"
|
||||
_ = three
|
||||
|
||||
print(1 + 2*3) // @describe const-expr " 2.3"
|
||||
print(real(1+2i) - 3) // @describe const-expr2 "real.*3"
|
||||
|
||||
m := map[string]*int{"a": &a}
|
||||
mapval, _ := m["a"] // @describe map-lookup,ok "m..a.."
|
||||
_ = mapval // @describe mapval "mapval"
|
||||
_ = m // @describe m "m"
|
||||
|
||||
defer main() // @describe defer-stmt "defer"
|
||||
go main() // @describe go-stmt "go"
|
||||
|
||||
panic(3) // @describe builtin-ref-panic "panic"
|
||||
|
||||
var a2 int // @describe var-decl-stmt "var a2 int"
|
||||
_ = a2
|
||||
var _ int // @describe var-decl-stmt2 "var _ int"
|
||||
var _ int // @describe var-def-blank "_"
|
||||
|
||||
var _ lib.Outer // @describe lib-outer "Outer"
|
||||
|
||||
unknown() // @describe call-unknown "\\("
|
||||
}
|
||||
|
||||
type I interface { // @describe def-iface-I "I"
|
||||
f() // @describe def-imethod-I.f "f"
|
||||
}
|
||||
|
||||
type C int
|
||||
type D struct {
|
||||
Field int
|
||||
AnotherField string
|
||||
}
|
||||
|
||||
func (c *C) f() {}
|
||||
func (d D) f() {}
|
212
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main.golden
generated
vendored
Normal file
212
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main.golden
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
-------- @describe pkgdecl --------
|
||||
definition of package "describe"
|
||||
type C int
|
||||
method (*C) f()
|
||||
type D struct{...}
|
||||
method (D) f()
|
||||
type I interface{f()}
|
||||
method (I) f()
|
||||
const c untyped int = 0
|
||||
type cake float64
|
||||
var global *string
|
||||
func main func()
|
||||
const pi untyped float = 3.141
|
||||
const pie cake = 3.141
|
||||
|
||||
-------- @describe unsafe --------
|
||||
import of package "unsafe"
|
||||
builtin Alignof
|
||||
builtin Offsetof
|
||||
type Pointer unsafe.Pointer
|
||||
builtin Sizeof
|
||||
|
||||
-------- @describe type-ref-builtin --------
|
||||
reference to built-in type float64
|
||||
|
||||
-------- @describe const-ref-iota --------
|
||||
reference to const iota untyped int of value 0
|
||||
|
||||
-------- @describe const-def-pi --------
|
||||
definition of const pi untyped float of value 3.141
|
||||
|
||||
-------- @describe const-def-pie --------
|
||||
definition of const pie cake of value 3.141
|
||||
|
||||
-------- @describe const-ref-pi --------
|
||||
reference to const pi untyped float of value 3.141
|
||||
defined here
|
||||
|
||||
-------- @describe func-def-main --------
|
||||
definition of func main()
|
||||
|
||||
-------- @describe func-ref-main --------
|
||||
reference to func main()
|
||||
defined here
|
||||
|
||||
-------- @describe func-ref-*C.f --------
|
||||
reference to method func (*C).f()
|
||||
defined here
|
||||
|
||||
-------- @describe func-ref-D.f --------
|
||||
reference to method func (D).f()
|
||||
defined here
|
||||
|
||||
-------- @describe func-ref-I.f --------
|
||||
reference to interface method func (I).f()
|
||||
defined here
|
||||
|
||||
-------- @describe type-D --------
|
||||
reference to type D (size 24, align 8)
|
||||
defined as struct{Field int; AnotherField string}
|
||||
Methods:
|
||||
method (D) f()
|
||||
Fields:
|
||||
Field int
|
||||
AnotherField string
|
||||
|
||||
-------- @describe type-I --------
|
||||
reference to type I (size 16, align 8)
|
||||
defined as interface{f()}
|
||||
Methods:
|
||||
method (I) f()
|
||||
|
||||
-------- @describe func-ref-d.f --------
|
||||
reference to method func (D).f()
|
||||
defined here
|
||||
|
||||
-------- @describe func-ref-i.f --------
|
||||
reference to interface method func (I).f()
|
||||
defined here
|
||||
|
||||
-------- @describe ptr-with-nonptr-methods --------
|
||||
definition of var dptr *D
|
||||
Methods:
|
||||
method (*D) f()
|
||||
Fields:
|
||||
Field int
|
||||
AnotherField string
|
||||
|
||||
-------- @describe ref-lexical-d --------
|
||||
reference to var d D
|
||||
defined here
|
||||
Methods:
|
||||
method (D) f()
|
||||
Fields:
|
||||
Field int
|
||||
AnotherField string
|
||||
|
||||
-------- @describe ref-anon --------
|
||||
reference to var anon func()
|
||||
defined here
|
||||
|
||||
-------- @describe ref-global --------
|
||||
reference to var global *string
|
||||
defined here
|
||||
|
||||
-------- @describe var-def-x-1 --------
|
||||
definition of var x *int
|
||||
|
||||
-------- @describe var-ref-x-1 --------
|
||||
reference to var x *int
|
||||
defined here
|
||||
|
||||
-------- @describe var-def-x-2 --------
|
||||
reference to var x *int
|
||||
defined here
|
||||
|
||||
-------- @describe var-ref-x-2 --------
|
||||
reference to var x *int
|
||||
defined here
|
||||
|
||||
-------- @describe var-ref-i-C --------
|
||||
reference to var i I
|
||||
defined here
|
||||
Methods:
|
||||
method (I) f()
|
||||
|
||||
-------- @describe var-ref-i-D --------
|
||||
reference to var i I
|
||||
defined here
|
||||
Methods:
|
||||
method (I) f()
|
||||
|
||||
-------- @describe var-ref-i --------
|
||||
reference to var i I
|
||||
defined here
|
||||
Methods:
|
||||
method (I) f()
|
||||
|
||||
-------- @describe const-local-pi --------
|
||||
definition of const localpi untyped float of value 3.141
|
||||
|
||||
-------- @describe const-local-pie --------
|
||||
definition of const localpie cake of value 3.141
|
||||
|
||||
-------- @describe const-ref-localpi --------
|
||||
reference to const localpi untyped float of value 3.141
|
||||
defined here
|
||||
|
||||
-------- @describe type-def-T --------
|
||||
definition of type T (size 8, align 8)
|
||||
No methods.
|
||||
|
||||
-------- @describe type-ref-T --------
|
||||
reference to type T (size 8, align 8)
|
||||
defined as int
|
||||
No methods.
|
||||
|
||||
-------- @describe const-expr --------
|
||||
binary * operation of value 6
|
||||
|
||||
-------- @describe const-expr2 --------
|
||||
binary - operation of value -2
|
||||
|
||||
-------- @describe map-lookup,ok --------
|
||||
index expression of type (*int, bool)
|
||||
|
||||
-------- @describe mapval --------
|
||||
reference to var mapval *int
|
||||
defined here
|
||||
|
||||
-------- @describe m --------
|
||||
reference to var m map[string]*int
|
||||
defined here
|
||||
|
||||
-------- @describe defer-stmt --------
|
||||
defer statement
|
||||
|
||||
-------- @describe go-stmt --------
|
||||
go statement
|
||||
|
||||
-------- @describe builtin-ref-panic --------
|
||||
function call (or conversion) of type ()
|
||||
|
||||
-------- @describe var-decl-stmt --------
|
||||
definition of var a2 int
|
||||
|
||||
-------- @describe var-decl-stmt2 --------
|
||||
definition of var _ int
|
||||
|
||||
-------- @describe var-def-blank --------
|
||||
definition of var _ int
|
||||
|
||||
-------- @describe lib-outer --------
|
||||
reference to type lib.Outer (size 56, align 8)
|
||||
defined as struct{A int; b int; lib.inner}
|
||||
No methods.
|
||||
Fields:
|
||||
A int
|
||||
inner.C bool
|
||||
inner.recursive.E bool
|
||||
|
||||
-------- @describe call-unknown --------
|
||||
function call of type invalid type
|
||||
|
||||
-------- @describe def-iface-I --------
|
||||
definition of type I (size 16, align 8)
|
||||
Methods:
|
||||
method (I) f()
|
||||
|
||||
-------- @describe def-imethod-I.f --------
|
||||
definition of interface method func (I).f()
|
||||
|
13
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main19.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main19.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package describe
|
||||
|
||||
// The behavior of "describe" on a non-existent import changed
|
||||
// when go/types started returning fake packages, so this test
|
||||
// is executed only under go1.9.
|
||||
|
||||
import (
|
||||
"nosuchpkg" // @describe badimport1 "nosuchpkg"
|
||||
nosuchpkg2 "nosuchpkg" // @describe badimport2 "nosuchpkg2"
|
||||
)
|
||||
|
||||
var _ nosuchpkg.T
|
||||
var _ nosuchpkg2.T
|
6
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main19.golden
generated
vendored
Normal file
6
vendor/golang.org/x/tools/cmd/guru/testdata/src/describe/main19.golden
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
-------- @describe badimport1 --------
|
||||
import of package "nosuchpkg"
|
||||
|
||||
-------- @describe badimport2 --------
|
||||
reference to package "nosuchpkg"
|
||||
|
40
vendor/golang.org/x/tools/cmd/guru/testdata/src/freevars/main.go
generated
vendored
Normal file
40
vendor/golang.org/x/tools/cmd/guru/testdata/src/freevars/main.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
// Tests of 'freevars' query.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See freevars.golden for expected query results.
|
||||
|
||||
// TODO(adonovan): it's hard to test this query in a single line of gofmt'd code.
|
||||
|
||||
type T struct {
|
||||
a, b int
|
||||
}
|
||||
|
||||
type S struct {
|
||||
x int
|
||||
t T
|
||||
}
|
||||
|
||||
func f(int) {}
|
||||
|
||||
func main() {
|
||||
type C int
|
||||
x := 1
|
||||
const exp = 6
|
||||
if y := 2; x+y+int(C(3)) != exp { // @freevars fv1 "if.*{"
|
||||
panic("expected 6")
|
||||
}
|
||||
|
||||
var s S
|
||||
|
||||
for x, y := range "foo" {
|
||||
println(s.x + s.t.a + s.t.b + x + int(y)) // @freevars fv2 "print.*y."
|
||||
}
|
||||
|
||||
f(x) // @freevars fv3 "f.x."
|
||||
|
||||
loop: // @freevars fv-def-label "loop:"
|
||||
for {
|
||||
break loop // @freevars fv-ref-label "break loop"
|
||||
}
|
||||
}
|
25
vendor/golang.org/x/tools/cmd/guru/testdata/src/freevars/main.golden
generated
vendored
Normal file
25
vendor/golang.org/x/tools/cmd/guru/testdata/src/freevars/main.golden
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
-------- @freevars fv1 --------
|
||||
Free identifiers:
|
||||
type C
|
||||
const exp int
|
||||
var x int
|
||||
|
||||
-------- @freevars fv2 --------
|
||||
Free identifiers:
|
||||
var s.t.a int
|
||||
var s.t.b int
|
||||
var s.x int
|
||||
var x int
|
||||
var y rune
|
||||
|
||||
-------- @freevars fv3 --------
|
||||
Free identifiers:
|
||||
var x int
|
||||
|
||||
-------- @freevars fv-def-label --------
|
||||
No free identifiers.
|
||||
|
||||
-------- @freevars fv-ref-label --------
|
||||
Free identifiers:
|
||||
label loop
|
||||
|
27
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-json/main.go
generated
vendored
Normal file
27
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
// Tests of 'implements' query, -output=json.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See implements.golden for expected query results.
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
type E interface{} // @implements E "E"
|
||||
|
||||
type F interface { // @implements F "F"
|
||||
f()
|
||||
}
|
||||
|
||||
type FG interface { // @implements FG "FG"
|
||||
f()
|
||||
g() []int // @implements slice "..int"
|
||||
}
|
||||
|
||||
type C int // @implements C "C"
|
||||
type D struct{}
|
||||
|
||||
func (c *C) f() {} // @implements starC ".C"
|
||||
func (d D) f() {} // @implements D "D"
|
||||
|
||||
func (d *D) g() []int { return nil } // @implements starD ".D"
|
135
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-json/main.golden
generated
vendored
Normal file
135
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
-------- @implements E --------
|
||||
{
|
||||
"type": {
|
||||
"name": "implements-json.E",
|
||||
"pos": "testdata/src/implements-json/main.go:10:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
}
|
||||
-------- @implements F --------
|
||||
{
|
||||
"type": {
|
||||
"name": "implements-json.F",
|
||||
"pos": "testdata/src/implements-json/main.go:12:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
"to": [
|
||||
{
|
||||
"name": "*implements-json.C",
|
||||
"pos": "testdata/src/implements-json/main.go:21:6",
|
||||
"kind": "pointer"
|
||||
},
|
||||
{
|
||||
"name": "implements-json.D",
|
||||
"pos": "testdata/src/implements-json/main.go:22:6",
|
||||
"kind": "struct"
|
||||
},
|
||||
{
|
||||
"name": "implements-json.FG",
|
||||
"pos": "testdata/src/implements-json/main.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @implements FG --------
|
||||
{
|
||||
"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 --------
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods-json/main.go
generated
vendored
Normal file
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
// Tests of 'implements' query applied to methods, -output=json.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See implements-methods.golden for expected query results.
|
||||
|
||||
import _ "lib"
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
type F interface {
|
||||
f() // @implements F.f "f"
|
||||
}
|
||||
|
||||
type FG interface {
|
||||
f() // @implements FG.f "f"
|
||||
g() []int // @implements FG.g "g"
|
||||
}
|
||||
|
||||
type C int
|
||||
type D struct{}
|
||||
|
||||
func (c *C) f() {} // @implements *C.f "f"
|
||||
func (d D) f() {} // @implements D.f "f"
|
||||
|
||||
func (d *D) g() []int { return nil } // @implements *D.g "g"
|
||||
|
||||
type sorter []int
|
||||
|
||||
func (sorter) Len() int { return 0 } // @implements Len "Len"
|
||||
func (sorter) Less(i, j int) bool { return false }
|
||||
func (sorter) Swap(i, j int) {}
|
||||
|
||||
type I interface {
|
||||
Method(*int) *int // @implements I.Method "Method"
|
||||
}
|
266
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods-json/main.golden
generated
vendored
Normal file
266
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
-------- @implements F.f --------
|
||||
{
|
||||
"type": {
|
||||
"name": "implements-methods-json.F",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:12:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
"to": [
|
||||
{
|
||||
"name": "*implements-methods-json.C",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:21:6",
|
||||
"kind": "pointer"
|
||||
},
|
||||
{
|
||||
"name": "implements-methods-json.D",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:22:6",
|
||||
"kind": "struct"
|
||||
},
|
||||
{
|
||||
"name": "implements-methods-json.FG",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"method": {
|
||||
"name": "func (F).f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:13:2"
|
||||
},
|
||||
"to_method": [
|
||||
{
|
||||
"name": "method (*C) f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:24:13"
|
||||
},
|
||||
{
|
||||
"name": "method (D) f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:25:12"
|
||||
},
|
||||
{
|
||||
"name": "method (FG) f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:17:2"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @implements FG.f --------
|
||||
{
|
||||
"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 --------
|
||||
{
|
||||
"type": {
|
||||
"name": "implements-methods-json.FG",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:16:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
"to": [
|
||||
{
|
||||
"name": "*implements-methods-json.D",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:22:6",
|
||||
"kind": "pointer"
|
||||
}
|
||||
],
|
||||
"from": [
|
||||
{
|
||||
"name": "implements-methods-json.F",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:12:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"method": {
|
||||
"name": "func (FG).g() []int",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:18:2"
|
||||
},
|
||||
"to_method": [
|
||||
{
|
||||
"name": "method (*D) g() []int",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:27:13"
|
||||
}
|
||||
],
|
||||
"from_method": [
|
||||
{
|
||||
"name": "",
|
||||
"pos": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @implements *C.f --------
|
||||
{
|
||||
"type": {
|
||||
"name": "*implements-methods-json.C",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:21:6",
|
||||
"kind": "pointer"
|
||||
},
|
||||
"from": [
|
||||
{
|
||||
"name": "implements-methods-json.F",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:12:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"method": {
|
||||
"name": "func (*C).f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:24:13"
|
||||
},
|
||||
"from_method": [
|
||||
{
|
||||
"name": "method (F) f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:13:2"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @implements D.f --------
|
||||
{
|
||||
"type": {
|
||||
"name": "implements-methods-json.D",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:22:6",
|
||||
"kind": "struct"
|
||||
},
|
||||
"from": [
|
||||
{
|
||||
"name": "implements-methods-json.F",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:12:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"fromptr": [
|
||||
{
|
||||
"name": "implements-methods-json.FG",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"method": {
|
||||
"name": "func (D).f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:25:12"
|
||||
},
|
||||
"from_method": [
|
||||
{
|
||||
"name": "method (F) f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:13:2"
|
||||
}
|
||||
],
|
||||
"fromptr_method": [
|
||||
{
|
||||
"name": "method (FG) f()",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:17:2"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @implements *D.g --------
|
||||
{
|
||||
"type": {
|
||||
"name": "*implements-methods-json.D",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:22:6",
|
||||
"kind": "pointer"
|
||||
},
|
||||
"from": [
|
||||
{
|
||||
"name": "implements-methods-json.F",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:12:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
{
|
||||
"name": "implements-methods-json.FG",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"method": {
|
||||
"name": "func (*D).g() []int",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:27:13"
|
||||
},
|
||||
"from_method": [
|
||||
{
|
||||
"name": "",
|
||||
"pos": ""
|
||||
},
|
||||
{
|
||||
"name": "method (FG) g() []int",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:18:2"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @implements Len --------
|
||||
{
|
||||
"type": {
|
||||
"name": "implements-methods-json.sorter",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:29:6",
|
||||
"kind": "slice"
|
||||
},
|
||||
"from": [
|
||||
{
|
||||
"name": "lib.Sorter",
|
||||
"pos": "testdata/src/lib/lib.go:16:6",
|
||||
"kind": "interface"
|
||||
}
|
||||
],
|
||||
"method": {
|
||||
"name": "func (sorter).Len() int",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:31:15"
|
||||
},
|
||||
"from_method": [
|
||||
{
|
||||
"name": "method (lib.Sorter) Len() int",
|
||||
"pos": "testdata/src/lib/lib.go:17:2"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @implements I.Method --------
|
||||
{
|
||||
"type": {
|
||||
"name": "implements-methods-json.I",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:35:6",
|
||||
"kind": "interface"
|
||||
},
|
||||
"to": [
|
||||
{
|
||||
"name": "lib.Type",
|
||||
"pos": "testdata/src/lib/lib.go:3:6",
|
||||
"kind": "basic"
|
||||
}
|
||||
],
|
||||
"method": {
|
||||
"name": "func (I).Method(*int) *int",
|
||||
"pos": "testdata/src/implements-methods-json/main.go:36:2"
|
||||
},
|
||||
"to_method": [
|
||||
{
|
||||
"name": "method (lib.Type) Method(x *int) *int",
|
||||
"pos": "testdata/src/lib/lib.go:5:13"
|
||||
}
|
||||
]
|
||||
}
|
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods/main.go
generated
vendored
Normal file
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods/main.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
// Tests of 'implements' query applied to methods.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See implements-methods.golden for expected query results.
|
||||
|
||||
import _ "lib"
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
type F interface {
|
||||
f() // @implements F.f "f"
|
||||
}
|
||||
|
||||
type FG interface {
|
||||
f() // @implements FG.f "f"
|
||||
g() []int // @implements FG.g "g"
|
||||
}
|
||||
|
||||
type C int
|
||||
type D struct{}
|
||||
|
||||
func (c *C) f() {} // @implements *C.f "f"
|
||||
func (d D) f() {} // @implements D.f "f"
|
||||
|
||||
func (d *D) g() []int { return nil } // @implements *D.g "g"
|
||||
|
||||
type sorter []int
|
||||
|
||||
func (sorter) Len() int { return 0 } // @implements Len "Len"
|
||||
func (sorter) Less(i, j int) bool { return false }
|
||||
func (sorter) Swap(i, j int) {}
|
||||
|
||||
type I interface {
|
||||
Method(*int) *int // @implements I.Method "Method"
|
||||
}
|
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods/main.golden
generated
vendored
Normal file
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements-methods/main.golden
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
-------- @implements F.f --------
|
||||
abstract method func (F).f()
|
||||
is implemented by method (*C).f
|
||||
is implemented by method (D).f
|
||||
is implemented by method (FG).f
|
||||
|
||||
-------- @implements FG.f --------
|
||||
abstract method func (FG).f()
|
||||
is implemented by method (*D).f
|
||||
implements method (F).f
|
||||
|
||||
-------- @implements FG.g --------
|
||||
abstract method func (FG).g() []int
|
||||
is implemented by method (*D).g
|
||||
|
||||
-------- @implements *C.f --------
|
||||
concrete method func (*C).f()
|
||||
implements method (F).f
|
||||
|
||||
-------- @implements D.f --------
|
||||
concrete method func (D).f()
|
||||
implements method (F).f
|
||||
concrete method func (D).f()
|
||||
implements method (FG).f
|
||||
|
||||
-------- @implements *D.g --------
|
||||
concrete method func (*D).g() []int
|
||||
implements method (FG).g
|
||||
|
||||
-------- @implements Len --------
|
||||
concrete method func (sorter).Len() int
|
||||
implements method (lib.Sorter).Len
|
||||
|
||||
-------- @implements I.Method --------
|
||||
abstract method func (I).Method(*int) *int
|
||||
is implemented by method (lib.Type).Method
|
||||
|
44
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements/main.go
generated
vendored
Normal file
44
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements/main.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
// Tests of 'implements' query.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See implements.golden for expected query results.
|
||||
|
||||
import _ "lib"
|
||||
|
||||
func main() {
|
||||
}
|
||||
|
||||
type E interface{} // @implements E "E"
|
||||
|
||||
type F interface { // @implements F "F"
|
||||
f()
|
||||
}
|
||||
|
||||
type FG interface { // @implements FG "FG"
|
||||
f()
|
||||
g() []int // @implements slice "..int"
|
||||
}
|
||||
|
||||
type C int // @implements C "C"
|
||||
type D struct{}
|
||||
|
||||
func (c *C) f() {} // @implements starC ".C"
|
||||
func (d D) f() {} // @implements D "D"
|
||||
|
||||
func (d *D) g() []int { return nil } // @implements starD ".D"
|
||||
|
||||
type sorter []int // @implements sorter "sorter"
|
||||
|
||||
func (sorter) Len() int { return 0 }
|
||||
func (sorter) Less(i, j int) bool { return false }
|
||||
func (sorter) Swap(i, j int) {}
|
||||
|
||||
type I interface { // @implements I "I"
|
||||
Method(*int) *int
|
||||
}
|
||||
|
||||
func _() {
|
||||
var d D
|
||||
_ = d // @implements var_d "d"
|
||||
}
|
50
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements/main.golden
generated
vendored
Normal file
50
vendor/golang.org/x/tools/cmd/guru/testdata/src/implements/main.golden
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
-------- @implements E --------
|
||||
empty interface type E
|
||||
|
||||
-------- @implements F --------
|
||||
interface type F
|
||||
is implemented by pointer type *C
|
||||
is implemented by struct type D
|
||||
is implemented by interface type FG
|
||||
|
||||
-------- @implements FG --------
|
||||
interface type FG
|
||||
is implemented by pointer type *D
|
||||
implements F
|
||||
|
||||
-------- @implements slice --------
|
||||
slice type []int implements only interface{}
|
||||
|
||||
-------- @implements C --------
|
||||
pointer type *C
|
||||
implements F
|
||||
|
||||
-------- @implements starC --------
|
||||
pointer type *C
|
||||
implements F
|
||||
|
||||
-------- @implements D --------
|
||||
struct type D
|
||||
implements F
|
||||
pointer type *D
|
||||
implements FG
|
||||
|
||||
-------- @implements starD --------
|
||||
pointer type *D
|
||||
implements F
|
||||
implements FG
|
||||
|
||||
-------- @implements sorter --------
|
||||
slice type sorter
|
||||
implements lib.Sorter
|
||||
|
||||
-------- @implements I --------
|
||||
interface type I
|
||||
is implemented by basic type lib.Type
|
||||
|
||||
-------- @implements var_d --------
|
||||
struct type D
|
||||
implements F
|
||||
pointer type *D
|
||||
implements FG
|
||||
|
29
vendor/golang.org/x/tools/cmd/guru/testdata/src/imports/main.go
generated
vendored
Normal file
29
vendor/golang.org/x/tools/cmd/guru/testdata/src/imports/main.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"lib" // @describe ref-pkg-import "lib"
|
||||
"lib/sublib" // @describe ref-pkg-import2 "sublib"
|
||||
)
|
||||
|
||||
// Tests that import another package. (To make the tests run quickly,
|
||||
// we avoid using imports in all the other tests. Remember, each
|
||||
// query causes parsing and typechecking of the whole program.)
|
||||
//
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See imports.golden for expected query results.
|
||||
|
||||
var a int
|
||||
|
||||
func main() {
|
||||
const c = lib.Const // @describe ref-const "Const"
|
||||
lib.Func() // @describe ref-func "Func"
|
||||
lib.Var++ // @describe ref-var "Var"
|
||||
var t lib.Type // @describe ref-type "Type"
|
||||
p := t.Method(&a) // @describe ref-method "Method"
|
||||
|
||||
print(*p + 1) // @pointsto p "p "
|
||||
|
||||
var _ lib.Type // @describe ref-pkg "lib"
|
||||
|
||||
_ = sublib.C
|
||||
}
|
56
vendor/golang.org/x/tools/cmd/guru/testdata/src/imports/main.golden
generated
vendored
Normal file
56
vendor/golang.org/x/tools/cmd/guru/testdata/src/imports/main.golden
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
-------- @describe ref-pkg-import --------
|
||||
import of package "lib"
|
||||
const Const untyped int = 3
|
||||
func Func func()
|
||||
type Outer struct{...}
|
||||
type Sorter interface{...}
|
||||
method (Sorter) Len() int
|
||||
method (Sorter) Less(i int, j int) bool
|
||||
method (Sorter) Swap(i int, j int)
|
||||
type Type int
|
||||
method (Type) Method(x *int) *int
|
||||
var Var int
|
||||
|
||||
-------- @describe ref-pkg-import2 --------
|
||||
import of package "lib/sublib"
|
||||
const C untyped int = 0
|
||||
|
||||
-------- @describe ref-const --------
|
||||
reference to const lib.Const untyped int of value 3
|
||||
defined here
|
||||
|
||||
-------- @describe ref-func --------
|
||||
reference to func lib.Func()
|
||||
defined here
|
||||
|
||||
-------- @describe ref-var --------
|
||||
reference to var lib.Var int
|
||||
defined here
|
||||
|
||||
-------- @describe ref-type --------
|
||||
reference to type lib.Type (size 8, align 8)
|
||||
defined as int
|
||||
Methods:
|
||||
method (Type) Method(x *int) *int
|
||||
|
||||
-------- @describe ref-method --------
|
||||
reference to method func (lib.Type).Method(x *int) *int
|
||||
defined here
|
||||
|
||||
-------- @pointsto p --------
|
||||
this *int may point to these objects:
|
||||
imports.a
|
||||
|
||||
-------- @describe ref-pkg --------
|
||||
reference to package "lib"
|
||||
const Const untyped int = 3
|
||||
func Func func()
|
||||
type Outer struct{...}
|
||||
type Sorter interface{...}
|
||||
method (Sorter) Len() int
|
||||
method (Sorter) Less(i int, j int) bool
|
||||
method (Sorter) Swap(i int, j int)
|
||||
type Type int
|
||||
method (Type) Method(x *int) *int
|
||||
var Var int
|
||||
|
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/lib/lib.go
generated
vendored
Normal file
37
vendor/golang.org/x/tools/cmd/guru/testdata/src/lib/lib.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package lib
|
||||
|
||||
type Type int
|
||||
|
||||
func (Type) Method(x *int) *int {
|
||||
return x
|
||||
}
|
||||
|
||||
func Func() {
|
||||
}
|
||||
|
||||
const Const = 3
|
||||
|
||||
var Var = 0
|
||||
|
||||
type Sorter interface {
|
||||
Len() int
|
||||
Less(i, j int) bool
|
||||
Swap(i, j int)
|
||||
}
|
||||
|
||||
type Outer struct {
|
||||
A int
|
||||
b int
|
||||
inner
|
||||
}
|
||||
|
||||
type inner struct {
|
||||
C bool
|
||||
d string
|
||||
recursive
|
||||
}
|
||||
|
||||
type recursive struct {
|
||||
E bool
|
||||
*inner
|
||||
}
|
3
vendor/golang.org/x/tools/cmd/guru/testdata/src/lib/sublib/sublib.go
generated
vendored
Normal file
3
vendor/golang.org/x/tools/cmd/guru/testdata/src/lib/sublib/sublib.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package sublib
|
||||
|
||||
const C = 0
|
13
vendor/golang.org/x/tools/cmd/guru/testdata/src/main/multi.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/cmd/guru/testdata/src/main/multi.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
func g(x int) {
|
||||
}
|
||||
|
||||
func f() {
|
||||
x := 1
|
||||
g(x) // "g(x)" is the selection for multiple queries
|
||||
}
|
||||
|
||||
func main() {
|
||||
f()
|
||||
}
|
13
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers-json/main.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
// Tests of channel 'peers' query, -format=json.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See peers-json.golden for expected query results.
|
||||
|
||||
func main() {
|
||||
chA := make(chan *int)
|
||||
<-chA
|
||||
select {
|
||||
case <-chA: // @peers peer-recv-chA "<-"
|
||||
}
|
||||
}
|
12
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers-json/main.golden
generated
vendored
Normal file
12
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
-------- @peers peer-recv-chA --------
|
||||
{
|
||||
"pos": "testdata/src/peers-json/main.go:11:7",
|
||||
"type": "chan *int",
|
||||
"allocs": [
|
||||
"testdata/src/peers-json/main.go:8:13"
|
||||
],
|
||||
"receives": [
|
||||
"testdata/src/peers-json/main.go:9:2",
|
||||
"testdata/src/peers-json/main.go:11:7"
|
||||
]
|
||||
}
|
52
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers/main.go
generated
vendored
Normal file
52
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers/main.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
// Tests of channel 'peers' query.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See peers.golden for expected query results.
|
||||
|
||||
var a2 int
|
||||
|
||||
func main() {
|
||||
chA := make(chan *int)
|
||||
a1 := 1
|
||||
chA <- &a1
|
||||
|
||||
chA2 := make(chan *int, 2)
|
||||
if a2 == 0 {
|
||||
chA = chA2
|
||||
}
|
||||
|
||||
chB := make(chan *int)
|
||||
b := 3
|
||||
chB <- &b
|
||||
|
||||
<-chA // @pointsto pointsto-chA "chA"
|
||||
<-chA2 // @pointsto pointsto-chA2 "chA2"
|
||||
<-chB // @pointsto pointsto-chB "chB"
|
||||
|
||||
select {
|
||||
case rA := <-chA: // @peers peer-recv-chA "<-"
|
||||
_ = rA // @pointsto pointsto-rA "rA"
|
||||
case rB := <-chB: // @peers peer-recv-chB "<-"
|
||||
_ = rB // @pointsto pointsto-rB "rB"
|
||||
|
||||
case <-chA: // @peers peer-recv-chA' "<-"
|
||||
|
||||
case chA2 <- &a2: // @peers peer-send-chA' "<-"
|
||||
}
|
||||
|
||||
for range chA {
|
||||
}
|
||||
|
||||
close(chA) // @peers peer-close-chA "chA"
|
||||
|
||||
chC := make(chan *int)
|
||||
(close)(chC) // @peers peer-close-chC "chC"
|
||||
|
||||
close := func(ch chan *int) chan *int {
|
||||
return ch
|
||||
}
|
||||
|
||||
close(chC) <- &b // @peers peer-send-chC "chC"
|
||||
<-close(chC) // @peers peer-recv-chC "chC"
|
||||
}
|
100
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers/main.golden
generated
vendored
Normal file
100
vendor/golang.org/x/tools/cmd/guru/testdata/src/peers/main.golden
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
-------- @pointsto pointsto-chA --------
|
||||
this chan *int may point to these objects:
|
||||
makechan
|
||||
makechan
|
||||
|
||||
-------- @pointsto pointsto-chA2 --------
|
||||
this chan *int may point to these objects:
|
||||
makechan
|
||||
|
||||
-------- @pointsto pointsto-chB --------
|
||||
this chan *int may point to these objects:
|
||||
makechan
|
||||
|
||||
-------- @peers peer-recv-chA --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
allocated here
|
||||
sent to, here
|
||||
sent to, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
closed, here
|
||||
|
||||
-------- @pointsto pointsto-rA --------
|
||||
this *int may point to these objects:
|
||||
peers.a2
|
||||
a1
|
||||
|
||||
-------- @peers peer-recv-chB --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
sent to, here
|
||||
received from, here
|
||||
received from, here
|
||||
|
||||
-------- @pointsto pointsto-rB --------
|
||||
this *int may point to these objects:
|
||||
b
|
||||
|
||||
-------- @peers peer-recv-chA' --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
allocated here
|
||||
sent to, here
|
||||
sent to, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
closed, here
|
||||
|
||||
-------- @peers peer-send-chA' --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
sent to, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
closed, here
|
||||
|
||||
-------- @peers peer-close-chA --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
allocated here
|
||||
sent to, here
|
||||
sent to, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
received from, here
|
||||
closed, here
|
||||
|
||||
-------- @peers peer-close-chC --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
sent to, here
|
||||
received from, here
|
||||
closed, here
|
||||
|
||||
-------- @peers peer-send-chC --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
sent to, here
|
||||
received from, here
|
||||
closed, here
|
||||
|
||||
-------- @peers peer-recv-chC --------
|
||||
This channel of type chan *int may be:
|
||||
allocated here
|
||||
sent to, here
|
||||
received from, here
|
||||
closed, here
|
||||
|
27
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto-json/main.go
generated
vendored
Normal file
27
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
// Tests of 'pointsto' queries, -format=json.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See pointsto-json.golden for expected query results.
|
||||
|
||||
func main() { //
|
||||
var s struct{ x [3]int }
|
||||
p := &s.x[0] // @pointsto val-p "p"
|
||||
_ = p
|
||||
|
||||
var i I = C(0)
|
||||
if i == nil {
|
||||
i = new(D)
|
||||
}
|
||||
print(i) // @pointsto val-i "\\bi\\b"
|
||||
}
|
||||
|
||||
type I interface {
|
||||
f()
|
||||
}
|
||||
|
||||
type C int
|
||||
type D struct{}
|
||||
|
||||
func (c C) f() {}
|
||||
func (d *D) f() {}
|
29
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto-json/main.golden
generated
vendored
Normal file
29
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
-------- @pointsto val-p --------
|
||||
[
|
||||
{
|
||||
"type": "*int",
|
||||
"labels": [
|
||||
{
|
||||
"pos": "testdata/src/pointsto-json/main.go:8:6",
|
||||
"desc": "s.x[*]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
-------- @pointsto val-i --------
|
||||
[
|
||||
{
|
||||
"type": "*D",
|
||||
"namepos": "testdata/src/pointsto-json/main.go:24:6",
|
||||
"labels": [
|
||||
{
|
||||
"pos": "testdata/src/pointsto-json/main.go:14:10",
|
||||
"desc": "new"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "C",
|
||||
"namepos": "testdata/src/pointsto-json/main.go:23:6"
|
||||
}
|
||||
]
|
75
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto/main.go
generated
vendored
Normal file
75
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto/main.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
// Tests of 'pointsto' query.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See pointsto.golden for expected query results.
|
||||
|
||||
const pi = 3.141 // @pointsto const "pi"
|
||||
|
||||
var global = new(string) // NB: ssa.Global is indirect, i.e. **string
|
||||
|
||||
func main() {
|
||||
livecode()
|
||||
|
||||
// func objects
|
||||
_ = main // @pointsto func-ref-main "main"
|
||||
_ = (*C).f // @pointsto func-ref-*C.f "..C..f"
|
||||
_ = D.f // @pointsto func-ref-D.f "D.f"
|
||||
_ = I.f // @pointsto func-ref-I.f "I.f"
|
||||
var d D
|
||||
var i I
|
||||
_ = d.f // @pointsto func-ref-d.f "d.f"
|
||||
_ = i.f // @pointsto func-ref-i.f "i.f"
|
||||
|
||||
// var objects
|
||||
anon := func() {
|
||||
_ = d.f // @pointsto ref-lexical-d.f "d.f"
|
||||
}
|
||||
_ = anon // @pointsto ref-anon "anon"
|
||||
_ = global // @pointsto ref-global "global"
|
||||
|
||||
// SSA affords some local flow sensitivity.
|
||||
var a, b int
|
||||
var x = &a // @pointsto var-def-x-1 "x"
|
||||
_ = x // @pointsto var-ref-x-1 "x"
|
||||
x = &b // @pointsto var-def-x-2 "x"
|
||||
_ = x // @pointsto var-ref-x-2 "x"
|
||||
|
||||
i = new(C) // @pointsto var-ref-i-C "i"
|
||||
if i != nil {
|
||||
i = D{} // @pointsto var-ref-i-D "i"
|
||||
}
|
||||
print(i) // @pointsto var-ref-i "\\bi\\b"
|
||||
|
||||
m := map[string]*int{"a": &a}
|
||||
mapval, _ := m["a"] // @pointsto map-lookup,ok "m..a.."
|
||||
_ = mapval // @pointsto mapval "mapval"
|
||||
_ = m // @pointsto m "m"
|
||||
|
||||
if false {
|
||||
panic(3) // @pointsto builtin-panic "panic"
|
||||
}
|
||||
|
||||
// NB: s.f is addressable per (*ssa.Program).VarValue,
|
||||
// but our query concerns the object, not its address.
|
||||
s := struct{ f interface{} }{f: make(chan bool)}
|
||||
print(s.f) // @pointsto var-ref-s-f "s.f"
|
||||
}
|
||||
|
||||
func livecode() {} // @pointsto func-live "livecode"
|
||||
|
||||
func deadcode() { // @pointsto func-dead "deadcode"
|
||||
// Pointer analysis can't run on dead code.
|
||||
var b = new(int) // @pointsto b "b"
|
||||
_ = b
|
||||
}
|
||||
|
||||
type I interface {
|
||||
f()
|
||||
}
|
||||
|
||||
type C int
|
||||
type D struct{}
|
||||
|
||||
func (c *C) f() {}
|
||||
func (d D) f() {}
|
96
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto/main.golden
generated
vendored
Normal file
96
vendor/golang.org/x/tools/cmd/guru/testdata/src/pointsto/main.golden
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
-------- @pointsto const --------
|
||||
|
||||
Error: pointer analysis wants an expression of reference type; got untyped float
|
||||
-------- @pointsto func-ref-main --------
|
||||
this func() may point to these objects:
|
||||
pointsto.main
|
||||
|
||||
-------- @pointsto func-ref-*C.f --------
|
||||
this func() may point to these objects:
|
||||
(*pointsto.C).f
|
||||
|
||||
-------- @pointsto func-ref-D.f --------
|
||||
this func() may point to these objects:
|
||||
(pointsto.D).f
|
||||
|
||||
-------- @pointsto func-ref-I.f --------
|
||||
|
||||
Error: func (pointsto.I).f() is an interface method
|
||||
-------- @pointsto func-ref-d.f --------
|
||||
this func() may point to these objects:
|
||||
(pointsto.D).f
|
||||
|
||||
-------- @pointsto func-ref-i.f --------
|
||||
|
||||
Error: func (pointsto.I).f() is an interface method
|
||||
-------- @pointsto ref-lexical-d.f --------
|
||||
this func() may point to these objects:
|
||||
(pointsto.D).f
|
||||
|
||||
-------- @pointsto ref-anon --------
|
||||
this func() may point to these objects:
|
||||
pointsto.main$1
|
||||
|
||||
-------- @pointsto ref-global --------
|
||||
this *string may point to these objects:
|
||||
new
|
||||
|
||||
-------- @pointsto var-def-x-1 --------
|
||||
this *int may point to these objects:
|
||||
a
|
||||
|
||||
-------- @pointsto var-ref-x-1 --------
|
||||
this *int may point to these objects:
|
||||
a
|
||||
|
||||
-------- @pointsto var-def-x-2 --------
|
||||
this *int may point to these objects:
|
||||
b
|
||||
|
||||
-------- @pointsto var-ref-x-2 --------
|
||||
this *int may point to these objects:
|
||||
b
|
||||
|
||||
-------- @pointsto var-ref-i-C --------
|
||||
this I may contain these dynamic types:
|
||||
*C, may point to:
|
||||
new
|
||||
|
||||
-------- @pointsto var-ref-i-D --------
|
||||
this I may contain these dynamic types:
|
||||
D
|
||||
|
||||
-------- @pointsto var-ref-i --------
|
||||
this I may contain these dynamic types:
|
||||
*C, may point to:
|
||||
new
|
||||
D
|
||||
|
||||
-------- @pointsto map-lookup,ok --------
|
||||
|
||||
Error: pointer analysis wants an expression of reference type; got (*int, bool)
|
||||
-------- @pointsto mapval --------
|
||||
this *int may point to these objects:
|
||||
a
|
||||
|
||||
-------- @pointsto m --------
|
||||
this map[string]*int may point to these objects:
|
||||
makemap
|
||||
|
||||
-------- @pointsto builtin-panic --------
|
||||
|
||||
Error: pointer analysis wants an expression of reference type; got ()
|
||||
-------- @pointsto var-ref-s-f --------
|
||||
this interface{} may contain these dynamic types:
|
||||
chan bool, may point to:
|
||||
makechan
|
||||
|
||||
-------- @pointsto func-live --------
|
||||
|
||||
Error: pointer analysis did not find expression (dead code?)
|
||||
-------- @pointsto func-dead --------
|
||||
|
||||
Error: pointer analysis did not find expression (dead code?)
|
||||
-------- @pointsto b --------
|
||||
|
||||
Error: pointer analysis did not find expression (dead code?)
|
24
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers-json/main.go
generated
vendored
Normal file
24
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
// Tests of 'referrers' query.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See referrers.golden for expected query results.
|
||||
|
||||
import "lib"
|
||||
|
||||
type s struct {
|
||||
f int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var v lib.Type = lib.Const // @referrers ref-package "lib"
|
||||
_ = v.Method // @referrers ref-method "Method"
|
||||
_ = v.Method
|
||||
v++ //@referrers ref-local "v"
|
||||
v++
|
||||
|
||||
_ = s{}.f // @referrers ref-field "f"
|
||||
|
||||
var s2 s
|
||||
s2.f = 1
|
||||
}
|
234
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers-json/main.golden
generated
vendored
Normal file
234
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
-------- @referrers ref-package --------
|
||||
{
|
||||
"desc": "package lib"
|
||||
}
|
||||
{
|
||||
"package": "definition-json",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:18:8",
|
||||
"text": "\tvar x lib.T // @definition lexical-pkgname \"lib\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:24:8",
|
||||
"text": "\tvar _ lib.Type // @definition qualified-type \"Type\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:25:8",
|
||||
"text": "\tvar _ lib.Func // @definition qualified-func \"Func\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:26:8",
|
||||
"text": "\tvar _ lib.Var // @definition qualified-var \"Var\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:27:8",
|
||||
"text": "\tvar _ lib.Const // @definition qualified-const \"Const\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:28:8",
|
||||
"text": "\tvar _ lib2.Type // @definition qualified-type-renaming \"Type\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:29:8",
|
||||
"text": "\tvar _ lib.Nonesuch // @definition qualified-nomember \"Nonesuch\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/definition-json/main.go:61:2",
|
||||
"text": "\tlib.Type // @definition embedded-other-pkg \"Type\""
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "describe",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/describe/main.go:86:8",
|
||||
"text": "\tvar _ lib.Outer // @describe lib-outer \"Outer\""
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "imports",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/imports/main.go:18:12",
|
||||
"text": "\tconst c = lib.Const // @describe ref-const \"Const\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/imports/main.go:19:2",
|
||||
"text": "\tlib.Func() // @describe ref-func \"Func\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/imports/main.go:20:2",
|
||||
"text": "\tlib.Var++ // @describe ref-var \"Var\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/imports/main.go:21:8",
|
||||
"text": "\tvar t lib.Type // @describe ref-type \"Type\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/imports/main.go:26:8",
|
||||
"text": "\tvar _ lib.Type // @describe ref-pkg \"lib\""
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers/int_test.go:7:7",
|
||||
"text": "\t_ = (lib.Type).Method // ref from internal test package"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers/main.go:16:8",
|
||||
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers/main.go:16:19",
|
||||
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers-json",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:14:8",
|
||||
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:14:19",
|
||||
"text": "\tvar v lib.Type = lib.Const // @referrers ref-package \"lib\""
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers_test",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers/ext_test.go:10:7",
|
||||
"text": "\t_ = (lib.Type).Method // ref from external test package"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "what-json",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/what-json/main.go:13:7",
|
||||
"text": "var _ lib.Var // @what pkg \"lib\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/what-json/main.go:14:8",
|
||||
"text": "type _ lib.T"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @referrers ref-method --------
|
||||
{
|
||||
"objpos": "testdata/src/lib/lib.go:5:13",
|
||||
"desc": "func (lib.Type).Method(x *int) *int"
|
||||
}
|
||||
{
|
||||
"package": "imports",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/imports/main.go:22:9",
|
||||
"text": "\tp := t.Method(\u0026a) // @describe ref-method \"Method\""
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers/int_test.go:7:17",
|
||||
"text": "\t_ = (lib.Type).Method // ref from internal test package"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers/main.go:17:8",
|
||||
"text": "\t_ = v.Method // @referrers ref-method \"Method\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers/main.go:18:8",
|
||||
"text": "\t_ = v.Method"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers-json",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:15:8",
|
||||
"text": "\t_ = v.Method // @referrers ref-method \"Method\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:16:8",
|
||||
"text": "\t_ = v.Method"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"package": "referrers_test",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers/ext_test.go:10:17",
|
||||
"text": "\t_ = (lib.Type).Method // ref from external test package"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @referrers ref-local --------
|
||||
{
|
||||
"objpos": "testdata/src/referrers-json/main.go:14:6",
|
||||
"desc": "var v lib.Type"
|
||||
}
|
||||
{
|
||||
"package": "referrers-json",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:15:6",
|
||||
"text": "\t_ = v.Method // @referrers ref-method \"Method\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:16:6",
|
||||
"text": "\t_ = v.Method"
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:17:2",
|
||||
"text": "\tv++ //@referrers ref-local \"v\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:18:2",
|
||||
"text": "\tv++"
|
||||
}
|
||||
]
|
||||
}
|
||||
-------- @referrers ref-field --------
|
||||
{
|
||||
"objpos": "testdata/src/referrers-json/main.go:10:2",
|
||||
"desc": "field f int"
|
||||
}
|
||||
{
|
||||
"package": "referrers-json",
|
||||
"refs": [
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:20:10",
|
||||
"text": "\t_ = s{}.f // @referrers ref-field \"f\""
|
||||
},
|
||||
{
|
||||
"pos": "testdata/src/referrers-json/main.go:23:5",
|
||||
"text": "\ts2.f = 1"
|
||||
}
|
||||
]
|
||||
}
|
12
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/ext_test.go
generated
vendored
Normal file
12
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/ext_test.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"lib"
|
||||
renamed "referrers" // package has name "main", path "referrers", local name "renamed"
|
||||
)
|
||||
|
||||
func _() {
|
||||
// This reference should be found by the ref-method query.
|
||||
_ = (lib.Type).Method // ref from external test package
|
||||
var _ renamed.T
|
||||
}
|
10
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/int_test.go
generated
vendored
Normal file
10
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/int_test.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "lib"
|
||||
|
||||
func _() {
|
||||
// This reference should be found by the ref-method query.
|
||||
_ = (lib.Type).Method // ref from internal test package
|
||||
|
||||
_ = notexported
|
||||
}
|
36
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/main.go
generated
vendored
Normal file
36
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/main.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package main // @referrers package-decl "main"
|
||||
|
||||
// Tests of 'referrers' query.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See referrers.golden for expected query results.
|
||||
|
||||
import "lib"
|
||||
|
||||
type s struct { // @referrers type " s "
|
||||
f int
|
||||
}
|
||||
|
||||
type T int
|
||||
|
||||
func main() {
|
||||
var v lib.Type = lib.Const // @referrers ref-package "lib"
|
||||
_ = v.Method // @referrers ref-method "Method"
|
||||
_ = v.Method
|
||||
v++ //@referrers ref-local "v"
|
||||
v++
|
||||
|
||||
_ = s{}.f // @referrers ref-field "f"
|
||||
|
||||
var s2 s
|
||||
s2.f = 1
|
||||
}
|
||||
|
||||
var notexported int // @referrers unexported-from-test "notexported"
|
||||
|
||||
// Test //line directives:
|
||||
|
||||
type U int // @referrers ref-type-U "U"
|
||||
|
||||
//line nosuchfile.y:123
|
||||
var u1 U
|
||||
var u2 U
|
64
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/main.golden
generated
vendored
Normal file
64
vendor/golang.org/x/tools/cmd/guru/testdata/src/referrers/main.golden
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
-------- @referrers package-decl --------
|
||||
references to package main ("referrers")
|
||||
var _ renamed.T
|
||||
|
||||
-------- @referrers type --------
|
||||
references to type s struct{f int}
|
||||
_ = s{}.f // @referrers ref-field "f"
|
||||
var s2 s
|
||||
|
||||
-------- @referrers ref-package --------
|
||||
references to package lib
|
||||
_ = (lib.Type).Method // ref from external test package
|
||||
_ = (lib.Type).Method // ref from internal test package
|
||||
const c = lib.Const // @describe ref-const "Const"
|
||||
lib.Func() // @describe ref-func "Func"
|
||||
lib.Type // @definition embedded-other-pkg "Type"
|
||||
lib.Var++ // @describe ref-var "Var"
|
||||
var _ lib.Const // @definition qualified-const "Const"
|
||||
var _ lib.Func // @definition qualified-func "Func"
|
||||
var _ lib.Nonesuch // @definition qualified-nomember "Nonesuch"
|
||||
var _ lib.Outer // @describe lib-outer "Outer"
|
||||
var _ lib.Type // @definition qualified-type "Type"
|
||||
var _ lib.Type // @describe ref-pkg "lib"
|
||||
var _ lib.Var // @definition qualified-var "Var"
|
||||
var _ lib2.Type // @definition qualified-type-renaming "Type"
|
||||
var t lib.Type // @describe ref-type "Type"
|
||||
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 x lib.T // @definition lexical-pkgname "lib"
|
||||
type _ lib.T
|
||||
var _ lib.Var // @what pkg "lib"
|
||||
|
||||
-------- @referrers ref-method --------
|
||||
references to func (lib.Type).Method(x *int) *int
|
||||
_ = (lib.Type).Method // ref from external test package
|
||||
_ = (lib.Type).Method // ref from internal test package
|
||||
_ = v.Method
|
||||
_ = v.Method
|
||||
_ = v.Method // @referrers ref-method "Method"
|
||||
_ = v.Method // @referrers ref-method "Method"
|
||||
p := t.Method(&a) // @describe ref-method "Method"
|
||||
|
||||
-------- @referrers ref-local --------
|
||||
references to var v lib.Type
|
||||
_ = v.Method
|
||||
_ = v.Method // @referrers ref-method "Method"
|
||||
v++
|
||||
v++ //@referrers ref-local "v"
|
||||
|
||||
-------- @referrers ref-field --------
|
||||
references to field f int
|
||||
_ = s{}.f // @referrers ref-field "f"
|
||||
s2.f = 1
|
||||
|
||||
-------- @referrers unexported-from-test --------
|
||||
references to var notexported int
|
||||
_ = notexported
|
||||
|
||||
-------- @referrers ref-type-U --------
|
||||
references to type U int
|
||||
open nosuchfile.y: no such file or directory (+ 1 more refs in this file)
|
||||
|
30
vendor/golang.org/x/tools/cmd/guru/testdata/src/reflection/main.go
generated
vendored
Normal file
30
vendor/golang.org/x/tools/cmd/guru/testdata/src/reflection/main.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
// This is a test of 'pointsto', but we split it into a separate file
|
||||
// so that pointsto.go doesn't have to import "reflect" each time.
|
||||
|
||||
import "reflect"
|
||||
|
||||
var a int
|
||||
var b bool
|
||||
|
||||
func main() {
|
||||
m := make(map[*int]*bool)
|
||||
m[&a] = &b
|
||||
|
||||
mrv := reflect.ValueOf(m)
|
||||
if a > 0 {
|
||||
mrv = reflect.ValueOf(&b)
|
||||
}
|
||||
if a > 0 {
|
||||
mrv = reflect.ValueOf(&a)
|
||||
}
|
||||
|
||||
_ = mrv // @pointsto mrv "mrv"
|
||||
p1 := mrv.Interface() // @pointsto p1 "p1"
|
||||
p2 := mrv.MapKeys() // @pointsto p2 "p2"
|
||||
p3 := p2[0] // @pointsto p3 "p3"
|
||||
p4 := reflect.TypeOf(p1) // @pointsto p4 "p4"
|
||||
|
||||
_, _, _, _ = p1, p2, p3, p4
|
||||
}
|
34
vendor/golang.org/x/tools/cmd/guru/testdata/src/reflection/main.golden
generated
vendored
Normal file
34
vendor/golang.org/x/tools/cmd/guru/testdata/src/reflection/main.golden
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
-------- @pointsto mrv --------
|
||||
this reflect.Value may contain these dynamic types:
|
||||
*bool, may point to:
|
||||
reflection.b
|
||||
*int, may point to:
|
||||
reflection.a
|
||||
map[*int]*bool, may point to:
|
||||
makemap
|
||||
|
||||
-------- @pointsto p1 --------
|
||||
this interface{} may contain these dynamic types:
|
||||
*bool, may point to:
|
||||
reflection.b
|
||||
*int, may point to:
|
||||
reflection.a
|
||||
map[*int]*bool, may point to:
|
||||
makemap
|
||||
|
||||
-------- @pointsto p2 --------
|
||||
this []reflect.Value may point to these objects:
|
||||
<alloc in (reflect.Value).MapKeys>
|
||||
|
||||
-------- @pointsto p3 --------
|
||||
this reflect.Value may contain these dynamic types:
|
||||
*int, may point to:
|
||||
reflection.a
|
||||
|
||||
-------- @pointsto p4 --------
|
||||
this reflect.Type may contain these dynamic types:
|
||||
*reflect.rtype, may point to:
|
||||
*bool
|
||||
*int
|
||||
map[*int]*bool
|
||||
|
15
vendor/golang.org/x/tools/cmd/guru/testdata/src/softerrs/main.go
generated
vendored
Normal file
15
vendor/golang.org/x/tools/cmd/guru/testdata/src/softerrs/main.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
// Tests of various queries on a program containing only "soft" errors.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See main.golden for expected query results.
|
||||
|
||||
func _() {
|
||||
var i int // "unused var" is a soft error
|
||||
}
|
||||
|
||||
func f() {} // @callers softerrs-callers-f "f"
|
||||
|
||||
func main() {
|
||||
f() // @describe softerrs-describe-f "f"
|
||||
}
|
8
vendor/golang.org/x/tools/cmd/guru/testdata/src/softerrs/main.golden
generated
vendored
Normal file
8
vendor/golang.org/x/tools/cmd/guru/testdata/src/softerrs/main.golden
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
-------- @callers softerrs-callers-f --------
|
||||
softerrs.f is called from these 1 sites:
|
||||
static function call from softerrs.main
|
||||
|
||||
-------- @describe softerrs-describe-f --------
|
||||
reference to func f()
|
||||
defined here
|
||||
|
14
vendor/golang.org/x/tools/cmd/guru/testdata/src/what-json/main.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/cmd/guru/testdata/src/what-json/main.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import "lib"
|
||||
|
||||
// Tests of 'what' queries, -format=json.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See what-json.golden for expected query results.
|
||||
|
||||
func main() {
|
||||
f() // @what call "f"
|
||||
}
|
||||
|
||||
var _ lib.Var // @what pkg "lib"
|
||||
type _ lib.T
|
95
vendor/golang.org/x/tools/cmd/guru/testdata/src/what-json/main.golden
generated
vendored
Normal file
95
vendor/golang.org/x/tools/cmd/guru/testdata/src/what-json/main.golden
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
-------- @what call --------
|
||||
{
|
||||
"enclosing": [
|
||||
{
|
||||
"desc": "identifier",
|
||||
"start": 189,
|
||||
"end": 190
|
||||
},
|
||||
{
|
||||
"desc": "function call",
|
||||
"start": 189,
|
||||
"end": 192
|
||||
},
|
||||
{
|
||||
"desc": "expression statement",
|
||||
"start": 189,
|
||||
"end": 192
|
||||
},
|
||||
{
|
||||
"desc": "block",
|
||||
"start": 186,
|
||||
"end": 212
|
||||
},
|
||||
{
|
||||
"desc": "function declaration",
|
||||
"start": 174,
|
||||
"end": 212
|
||||
},
|
||||
{
|
||||
"desc": "source file",
|
||||
"start": 0,
|
||||
"end": 259
|
||||
}
|
||||
],
|
||||
"modes": [
|
||||
"callees",
|
||||
"callers",
|
||||
"callstack",
|
||||
"definition",
|
||||
"describe",
|
||||
"freevars",
|
||||
"implements",
|
||||
"pointsto",
|
||||
"referrers",
|
||||
"whicherrs"
|
||||
],
|
||||
"srcdir": "testdata/src",
|
||||
"importpath": "what-json"
|
||||
}
|
||||
-------- @what pkg --------
|
||||
{
|
||||
"enclosing": [
|
||||
{
|
||||
"desc": "identifier",
|
||||
"start": 220,
|
||||
"end": 223
|
||||
},
|
||||
{
|
||||
"desc": "selector",
|
||||
"start": 220,
|
||||
"end": 227
|
||||
},
|
||||
{
|
||||
"desc": "value specification",
|
||||
"start": 218,
|
||||
"end": 227
|
||||
},
|
||||
{
|
||||
"desc": "variable declaration",
|
||||
"start": 214,
|
||||
"end": 227
|
||||
},
|
||||
{
|
||||
"desc": "source file",
|
||||
"start": 0,
|
||||
"end": 259
|
||||
}
|
||||
],
|
||||
"modes": [
|
||||
"definition",
|
||||
"describe",
|
||||
"freevars",
|
||||
"implements",
|
||||
"pointsto",
|
||||
"referrers",
|
||||
"whicherrs"
|
||||
],
|
||||
"srcdir": "testdata/src",
|
||||
"importpath": "what-json",
|
||||
"object": "lib",
|
||||
"sameids": [
|
||||
"$GOPATH/src/what-json/main.go:13:7",
|
||||
"$GOPATH/src/what-json/main.go:14:8"
|
||||
]
|
||||
}
|
11
vendor/golang.org/x/tools/cmd/guru/testdata/src/what/main.go
generated
vendored
Normal file
11
vendor/golang.org/x/tools/cmd/guru/testdata/src/what/main.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package main // @what pkgdecl "main"
|
||||
|
||||
// Tests of 'what' queries.
|
||||
// See go.tools/guru/guru_test.go for explanation.
|
||||
// See what.golden for expected query results.
|
||||
|
||||
func main() {
|
||||
f() // @what call "f"
|
||||
var ch chan int // @what var "var"
|
||||
<-ch // @what recv "ch"
|
||||
}
|
41
vendor/golang.org/x/tools/cmd/guru/testdata/src/what/main.golden
generated
vendored
Normal file
41
vendor/golang.org/x/tools/cmd/guru/testdata/src/what/main.golden
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
-------- @what pkgdecl --------
|
||||
identifier
|
||||
source file
|
||||
modes: [definition describe freevars implements pointsto referrers whicherrs]
|
||||
srcdir: testdata/src
|
||||
import path: what
|
||||
|
||||
-------- @what call --------
|
||||
identifier
|
||||
function call
|
||||
expression statement
|
||||
block
|
||||
function declaration
|
||||
source file
|
||||
modes: [callees callers callstack definition describe freevars implements pointsto referrers whicherrs]
|
||||
srcdir: testdata/src
|
||||
import path: what
|
||||
|
||||
-------- @what var --------
|
||||
variable declaration
|
||||
variable declaration statement
|
||||
block
|
||||
function declaration
|
||||
source file
|
||||
modes: [callers callstack describe freevars pointsto whicherrs]
|
||||
srcdir: testdata/src
|
||||
import path: what
|
||||
|
||||
-------- @what recv --------
|
||||
identifier
|
||||
unary <- operation
|
||||
expression statement
|
||||
block
|
||||
function declaration
|
||||
source file
|
||||
modes: [callers callstack definition describe freevars implements peers pointsto referrers whicherrs]
|
||||
srcdir: testdata/src
|
||||
import path: what
|
||||
ch
|
||||
ch
|
||||
|
32
vendor/golang.org/x/tools/cmd/guru/testdata/src/whicherrs/main.go
generated
vendored
Normal file
32
vendor/golang.org/x/tools/cmd/guru/testdata/src/whicherrs/main.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
type errType string
|
||||
|
||||
const constErr errType = "blah"
|
||||
|
||||
func (et errType) Error() string {
|
||||
return string(et)
|
||||
}
|
||||
|
||||
var errVar error = errType("foo")
|
||||
|
||||
func genErr(i int) error {
|
||||
switch i {
|
||||
case 0:
|
||||
return constErr
|
||||
case 1:
|
||||
return errVar
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func unreachable() {
|
||||
err := errVar // @whicherrs func-dead "err"
|
||||
_ = err
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := genErr(0) // @whicherrs localerrs "err"
|
||||
_ = err
|
||||
}
|
11
vendor/golang.org/x/tools/cmd/guru/testdata/src/whicherrs/main.golden
generated
vendored
Normal file
11
vendor/golang.org/x/tools/cmd/guru/testdata/src/whicherrs/main.golden
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
-------- @whicherrs func-dead --------
|
||||
|
||||
Error: pointer analysis did not find expression (dead code?)
|
||||
-------- @whicherrs localerrs --------
|
||||
this error may point to these globals:
|
||||
errVar
|
||||
this error may contain these constants:
|
||||
constErr
|
||||
this error may contain these dynamic types:
|
||||
errType
|
||||
|
105
vendor/golang.org/x/tools/cmd/guru/unit_test.go
generated
vendored
Normal file
105
vendor/golang.org/x/tools/cmd/guru/unit_test.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Unit tests for internal guru functions
|
||||
|
||||
func TestIssue17515(t *testing.T) {
|
||||
// Tests handling of symlinks in function guessImportPath
|
||||
// If we have Go code inside $HOME/go/src and create a symlink $HOME/src to it
|
||||
// there are 4 possible cases that need to be tested:
|
||||
// (1) absolute & absolute: GOPATH=$HOME/go/src file=$HOME/go/src/test/test.go
|
||||
// (2) absolute & symlink: GOPATH=$HOME/go/src file=$HOME/src/test/test.go
|
||||
// (3) symlink & symlink: GOPATH=$HOME/src file=$HOME/src/test/test.go
|
||||
// (4) symlink & absolute: GOPATH=$HOME/src file= $HOME/go/src/test/test.go
|
||||
|
||||
// Create a temporary home directory under /tmp
|
||||
home, err := ioutil.TempDir(os.TempDir(), "home")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to create a temporary directory in %s", os.TempDir())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(home)
|
||||
|
||||
// create filepath /tmp/home/go/src/test/test.go
|
||||
if err = os.MkdirAll(home+"/go/src/test", 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buildContext = build.Default
|
||||
|
||||
// Success test cases
|
||||
type SuccessTest struct {
|
||||
gopath, filename, wantSrcdir string
|
||||
}
|
||||
|
||||
successTests := []SuccessTest{
|
||||
{home + "/go", home + "/go/src/test/test.go", filepath.FromSlash(home + "/go/src")},
|
||||
}
|
||||
|
||||
// Add symlink cases if not on Windows, Plan 9
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
|
||||
// symlink between /tmp/home/go/src and /tmp/home/src
|
||||
if err := os.Symlink(home+"/go/src", home+"/src"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
successTests = append(successTests, []SuccessTest{
|
||||
{home + "/go", home + "/src/test/test.go", filepath.FromSlash(home + "/go/src")},
|
||||
{home, home + "/go/src/test/test.go", filepath.FromSlash(home + "/src")},
|
||||
{home, home + "/src/test/test.go", filepath.FromSlash(home + "/src")},
|
||||
}...)
|
||||
}
|
||||
|
||||
for _, test := range successTests {
|
||||
buildContext.GOPATH = test.gopath
|
||||
srcdir, importPath, err := guessImportPath(test.filename, &buildContext)
|
||||
if srcdir != test.wantSrcdir || importPath != "test" || err != nil {
|
||||
t.Errorf("guessImportPath(%q, %q) = %q, %q, %q; want %q, %q, %q",
|
||||
test.filename, test.gopath, srcdir, importPath, err, test.wantSrcdir, "test", "nil")
|
||||
}
|
||||
}
|
||||
// Function to format expected error message
|
||||
errFormat := func(fpath string) string {
|
||||
return fmt.Sprintf("can't evaluate symlinks of %s", fpath)
|
||||
}
|
||||
|
||||
// Failure test cases
|
||||
type FailTest struct {
|
||||
gopath, filename, wantErr string
|
||||
}
|
||||
|
||||
failTests := []FailTest{
|
||||
{home + "/go", home + "/go/src/fake/test.go", errFormat(filepath.FromSlash(home + "/go/src/fake"))},
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
|
||||
failTests = append(failTests, []FailTest{
|
||||
{home + "/go", home + "/src/fake/test.go", errFormat(filepath.FromSlash(home + "/src/fake"))},
|
||||
{home, home + "/src/fake/test.go", errFormat(filepath.FromSlash(home + "/src/fake"))},
|
||||
{home, home + "/go/src/fake/test.go", errFormat(filepath.FromSlash(home + "/go/src/fake"))},
|
||||
}...)
|
||||
}
|
||||
|
||||
for _, test := range failTests {
|
||||
buildContext.GOPATH = test.gopath
|
||||
srcdir, importPath, err := guessImportPath(test.filename, &buildContext)
|
||||
if !strings.HasPrefix(fmt.Sprint(err), test.wantErr) {
|
||||
t.Errorf("guessImportPath(%q, %q) = %q, %q, %q; want %q, %q, %q",
|
||||
test.filename, test.gopath, srcdir, importPath, err, "", "", test.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
282
vendor/golang.org/x/tools/cmd/guru/what.go
generated
vendored
Normal file
282
vendor/golang.org/x/tools/cmd/guru/what.go
generated
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// what reports all the information about the query selection that can be
|
||||
// obtained from parsing only its containing source file.
|
||||
// It is intended to be a very low-latency query callable from GUI
|
||||
// tools, e.g. to populate a menu of options of slower queries about
|
||||
// the selected location.
|
||||
//
|
||||
func what(q *Query) error {
|
||||
qpos, err := fastQueryPos(q.Build, q.Pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// (ignore errors)
|
||||
srcdir, importPath, _ := guessImportPath(qpos.fset.File(qpos.start).Name(), q.Build)
|
||||
|
||||
// Determine which query modes are applicable to the selection.
|
||||
enable := map[string]bool{
|
||||
"describe": true, // any syntax; always enabled
|
||||
}
|
||||
|
||||
if qpos.end > qpos.start {
|
||||
enable["freevars"] = true // nonempty selection?
|
||||
}
|
||||
|
||||
for _, n := range qpos.path {
|
||||
switch n := n.(type) {
|
||||
case *ast.Ident:
|
||||
enable["definition"] = true
|
||||
enable["referrers"] = true
|
||||
enable["implements"] = true
|
||||
case *ast.CallExpr:
|
||||
enable["callees"] = true
|
||||
case *ast.FuncDecl:
|
||||
enable["callers"] = true
|
||||
enable["callstack"] = true
|
||||
case *ast.SendStmt:
|
||||
enable["peers"] = true
|
||||
case *ast.UnaryExpr:
|
||||
if n.Op == token.ARROW {
|
||||
enable["peers"] = true
|
||||
}
|
||||
}
|
||||
|
||||
// For implements, we approximate findInterestingNode.
|
||||
if _, ok := enable["implements"]; !ok {
|
||||
switch n.(type) {
|
||||
case *ast.ArrayType,
|
||||
*ast.StructType,
|
||||
*ast.FuncType,
|
||||
*ast.InterfaceType,
|
||||
*ast.MapType,
|
||||
*ast.ChanType:
|
||||
enable["implements"] = true
|
||||
}
|
||||
}
|
||||
|
||||
// For pointsto and whicherrs, we approximate findInterestingNode.
|
||||
if _, ok := enable["pointsto"]; !ok {
|
||||
switch n.(type) {
|
||||
case ast.Stmt,
|
||||
*ast.ArrayType,
|
||||
*ast.StructType,
|
||||
*ast.FuncType,
|
||||
*ast.InterfaceType,
|
||||
*ast.MapType,
|
||||
*ast.ChanType:
|
||||
// not an expression
|
||||
enable["pointsto"] = false
|
||||
enable["whicherrs"] = false
|
||||
|
||||
case ast.Expr, ast.Decl, *ast.ValueSpec:
|
||||
// an expression, maybe
|
||||
enable["pointsto"] = true
|
||||
enable["whicherrs"] = true
|
||||
|
||||
default:
|
||||
// Comment, Field, KeyValueExpr, etc: ascend.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have an exact selection, disable modes that need one.
|
||||
if !qpos.exact {
|
||||
enable["callees"] = false
|
||||
enable["pointsto"] = false
|
||||
enable["whicherrs"] = false
|
||||
enable["describe"] = false
|
||||
}
|
||||
|
||||
var modes []string
|
||||
for mode := range enable {
|
||||
modes = append(modes, mode)
|
||||
}
|
||||
sort.Strings(modes)
|
||||
|
||||
// Find the object referred to by the selection (if it's an
|
||||
// identifier) and report the position of each identifier
|
||||
// that refers to the same object.
|
||||
//
|
||||
// This may return spurious matches (e.g. struct fields) because
|
||||
// it uses the best-effort name resolution done by go/parser.
|
||||
var sameids []token.Pos
|
||||
var object string
|
||||
if id, ok := qpos.path[0].(*ast.Ident); ok {
|
||||
if id.Obj == nil {
|
||||
// An unresolved identifier is potentially a package name.
|
||||
// Resolve them with a simple importer (adds ~100µs).
|
||||
importer := func(imports map[string]*ast.Object, path string) (*ast.Object, error) {
|
||||
pkg, ok := imports[path]
|
||||
if !ok {
|
||||
pkg = &ast.Object{
|
||||
Kind: ast.Pkg,
|
||||
Name: filepath.Base(path), // a guess
|
||||
}
|
||||
imports[path] = pkg
|
||||
}
|
||||
return pkg, nil
|
||||
}
|
||||
f := qpos.path[len(qpos.path)-1].(*ast.File)
|
||||
ast.NewPackage(qpos.fset, map[string]*ast.File{"": f}, importer, nil)
|
||||
}
|
||||
|
||||
if id.Obj != nil {
|
||||
object = id.Obj.Name
|
||||
decl := qpos.path[len(qpos.path)-1]
|
||||
ast.Inspect(decl, func(n ast.Node) bool {
|
||||
if n, ok := n.(*ast.Ident); ok && n.Obj == id.Obj {
|
||||
sameids = append(sameids, n.Pos())
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
q.Output(qpos.fset, &whatResult{
|
||||
path: qpos.path,
|
||||
srcdir: srcdir,
|
||||
importPath: importPath,
|
||||
modes: modes,
|
||||
object: object,
|
||||
sameids: sameids,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// guessImportPath finds the package containing filename, and returns
|
||||
// its source directory (an element of $GOPATH) and its import path
|
||||
// relative to it.
|
||||
//
|
||||
// TODO(adonovan): what about _test.go files that are not part of the
|
||||
// package?
|
||||
//
|
||||
func guessImportPath(filename string, buildContext *build.Context) (srcdir, importPath string, err error) {
|
||||
absFile, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("can't form absolute path of %s: %v", filename, err)
|
||||
}
|
||||
|
||||
absFileDir := filepath.Dir(absFile)
|
||||
resolvedAbsFileDir, err := filepath.EvalSymlinks(absFileDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("can't evaluate symlinks of %s: %v", absFileDir, err)
|
||||
}
|
||||
|
||||
segmentedAbsFileDir := segments(resolvedAbsFileDir)
|
||||
// Find the innermost directory in $GOPATH that encloses filename.
|
||||
minD := 1024
|
||||
for _, gopathDir := range buildContext.SrcDirs() {
|
||||
absDir, err := filepath.Abs(gopathDir)
|
||||
if err != nil {
|
||||
continue // e.g. non-existent dir on $GOPATH
|
||||
}
|
||||
resolvedAbsDir, err := filepath.EvalSymlinks(absDir)
|
||||
if err != nil {
|
||||
continue // e.g. non-existent dir on $GOPATH
|
||||
}
|
||||
|
||||
d := prefixLen(segments(resolvedAbsDir), segmentedAbsFileDir)
|
||||
// If there are multiple matches,
|
||||
// prefer the innermost enclosing directory
|
||||
// (smallest d).
|
||||
if d >= 0 && d < minD {
|
||||
minD = d
|
||||
srcdir = gopathDir
|
||||
importPath = strings.Join(segmentedAbsFileDir[len(segmentedAbsFileDir)-minD:], string(os.PathSeparator))
|
||||
}
|
||||
}
|
||||
if srcdir == "" {
|
||||
return "", "", fmt.Errorf("directory %s is not beneath any of these GOROOT/GOPATH directories: %s",
|
||||
filepath.Dir(absFile), strings.Join(buildContext.SrcDirs(), ", "))
|
||||
}
|
||||
if importPath == "" {
|
||||
// This happens for e.g. $GOPATH/src/a.go, but
|
||||
// "" is not a valid path for (*go/build).Import.
|
||||
return "", "", fmt.Errorf("cannot load package in root of source directory %s", srcdir)
|
||||
}
|
||||
return srcdir, importPath, nil
|
||||
}
|
||||
|
||||
func segments(path string) []string {
|
||||
return strings.Split(path, string(os.PathSeparator))
|
||||
}
|
||||
|
||||
// prefixLen returns the length of the remainder of y if x is a prefix
|
||||
// of y, a negative number otherwise.
|
||||
func prefixLen(x, y []string) int {
|
||||
d := len(y) - len(x)
|
||||
if d >= 0 {
|
||||
for i := range x {
|
||||
if y[i] != x[i] {
|
||||
return -1 // not a prefix
|
||||
}
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
type whatResult struct {
|
||||
path []ast.Node
|
||||
modes []string
|
||||
srcdir string
|
||||
importPath string
|
||||
object string
|
||||
sameids []token.Pos
|
||||
}
|
||||
|
||||
func (r *whatResult) PrintPlain(printf printfFunc) {
|
||||
for _, n := range r.path {
|
||||
printf(n, "%s", astutil.NodeDescription(n))
|
||||
}
|
||||
printf(nil, "modes: %s", r.modes)
|
||||
printf(nil, "srcdir: %s", r.srcdir)
|
||||
printf(nil, "import path: %s", r.importPath)
|
||||
for _, pos := range r.sameids {
|
||||
printf(pos, "%s", r.object)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *whatResult) JSON(fset *token.FileSet) []byte {
|
||||
var enclosing []serial.SyntaxNode
|
||||
for _, n := range r.path {
|
||||
enclosing = append(enclosing, serial.SyntaxNode{
|
||||
Description: astutil.NodeDescription(n),
|
||||
Start: fset.Position(n.Pos()).Offset,
|
||||
End: fset.Position(n.End()).Offset,
|
||||
})
|
||||
}
|
||||
|
||||
var sameids []string
|
||||
for _, pos := range r.sameids {
|
||||
sameids = append(sameids, fset.Position(pos).String())
|
||||
}
|
||||
|
||||
return toJSON(&serial.What{
|
||||
Modes: r.modes,
|
||||
SrcDir: r.srcdir,
|
||||
ImportPath: r.importPath,
|
||||
Enclosing: enclosing,
|
||||
Object: r.object,
|
||||
SameIDs: sameids,
|
||||
})
|
||||
}
|
327
vendor/golang.org/x/tools/cmd/guru/whicherrs.go
generated
vendored
Normal file
327
vendor/golang.org/x/tools/cmd/guru/whicherrs.go
generated
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cmd/guru/serial"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/pointer"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
var builtinErrorType = types.Universe.Lookup("error").Type()
|
||||
|
||||
// whicherrs takes an position to an error and tries to find all types, constants
|
||||
// and global value which a given error can point to and which can be checked from the
|
||||
// scope where the error lives.
|
||||
// In short, it returns a list of things that can be checked against in order to handle
|
||||
// an error properly.
|
||||
//
|
||||
// TODO(dmorsing): figure out if fields in errors like *os.PathError.Err
|
||||
// can be queried recursively somehow.
|
||||
func whicherrs(q *Query) error {
|
||||
lconf := loader.Config{Build: q.Build}
|
||||
|
||||
if err := setPTAScope(&lconf, q.Scope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load/parse/type-check the program.
|
||||
lprog, err := loadWithSoftErrors(&lconf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
|
||||
|
||||
ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, action := findInterestingNode(qpos.info, qpos.path)
|
||||
if action != actionExpr {
|
||||
return fmt.Errorf("whicherrs wants an expression; got %s",
|
||||
astutil.NodeDescription(qpos.path[0]))
|
||||
}
|
||||
var expr ast.Expr
|
||||
var obj types.Object
|
||||
switch n := path[0].(type) {
|
||||
case *ast.ValueSpec:
|
||||
// ambiguous ValueSpec containing multiple names
|
||||
return fmt.Errorf("multiple value specification")
|
||||
case *ast.Ident:
|
||||
obj = qpos.info.ObjectOf(n)
|
||||
expr = n
|
||||
case ast.Expr:
|
||||
expr = n
|
||||
default:
|
||||
return fmt.Errorf("unexpected AST for expr: %T", n)
|
||||
}
|
||||
|
||||
typ := qpos.info.TypeOf(expr)
|
||||
if !types.Identical(typ, builtinErrorType) {
|
||||
return fmt.Errorf("selection is not an expression of type 'error'")
|
||||
}
|
||||
// Determine the ssa.Value for the expression.
|
||||
var value ssa.Value
|
||||
if obj != nil {
|
||||
// def/ref of func/var object
|
||||
value, _, err = ssaValueForIdent(prog, qpos.info, obj, path)
|
||||
} else {
|
||||
value, _, err = ssaValueForExpr(prog, qpos.info, path)
|
||||
}
|
||||
if err != nil {
|
||||
return err // e.g. trivially dead code
|
||||
}
|
||||
|
||||
// Defer SSA construction till after errors are reported.
|
||||
prog.Build()
|
||||
|
||||
globals := findVisibleErrs(prog, qpos)
|
||||
constants := findVisibleConsts(prog, qpos)
|
||||
|
||||
res := &whicherrsResult{
|
||||
qpos: qpos,
|
||||
errpos: expr.Pos(),
|
||||
}
|
||||
|
||||
// TODO(adonovan): the following code is heavily duplicated
|
||||
// w.r.t. "pointsto". Refactor?
|
||||
|
||||
// Find the instruction which initialized the
|
||||
// global error. If more than one instruction has stored to the global
|
||||
// remove the global from the set of values that we want to query.
|
||||
allFuncs := ssautil.AllFunctions(prog)
|
||||
for fn := range allFuncs {
|
||||
for _, b := range fn.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
store, ok := instr.(*ssa.Store)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
gval, ok := store.Addr.(*ssa.Global)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
gbl, ok := globals[gval]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// we already found a store to this global
|
||||
// The normal error define is just one store in the init
|
||||
// so we just remove this global from the set we want to query
|
||||
if gbl != nil {
|
||||
delete(globals, gval)
|
||||
}
|
||||
globals[gval] = store.Val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptaConfig.AddQuery(value)
|
||||
for _, v := range globals {
|
||||
ptaConfig.AddQuery(v)
|
||||
}
|
||||
|
||||
ptares := ptrAnalysis(ptaConfig)
|
||||
valueptr := ptares.Queries[value]
|
||||
if valueptr == (pointer.Pointer{}) {
|
||||
return fmt.Errorf("pointer analysis did not find expression (dead code?)")
|
||||
}
|
||||
for g, v := range globals {
|
||||
ptr, ok := ptares.Queries[v]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if !ptr.MayAlias(valueptr) {
|
||||
continue
|
||||
}
|
||||
res.globals = append(res.globals, g)
|
||||
}
|
||||
pts := valueptr.PointsTo()
|
||||
dedup := make(map[*ssa.NamedConst]bool)
|
||||
for _, label := range pts.Labels() {
|
||||
// These values are either MakeInterfaces or reflect
|
||||
// generated interfaces. For the purposes of this
|
||||
// analysis, we don't care about reflect generated ones
|
||||
makeiface, ok := label.Value().(*ssa.MakeInterface)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
constval, ok := makeiface.X.(*ssa.Const)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
c := constants[*constval]
|
||||
if c != nil && !dedup[c] {
|
||||
dedup[c] = true
|
||||
res.consts = append(res.consts, c)
|
||||
}
|
||||
}
|
||||
concs := pts.DynamicTypes()
|
||||
concs.Iterate(func(conc types.Type, _ interface{}) {
|
||||
// go/types is a bit annoying here.
|
||||
// We want to find all the types that we can
|
||||
// typeswitch or assert to. This means finding out
|
||||
// if the type pointed to can be seen by us.
|
||||
//
|
||||
// For the purposes of this analysis, we care only about
|
||||
// TypeNames of Named or pointer-to-Named types.
|
||||
// We ignore other types (e.g. structs) that implement error.
|
||||
var name *types.TypeName
|
||||
switch t := conc.(type) {
|
||||
case *types.Pointer:
|
||||
named, ok := t.Elem().(*types.Named)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
name = named.Obj()
|
||||
case *types.Named:
|
||||
name = t.Obj()
|
||||
default:
|
||||
return
|
||||
}
|
||||
if !isAccessibleFrom(name, qpos.info.Pkg) {
|
||||
return
|
||||
}
|
||||
res.types = append(res.types, &errorType{conc, name})
|
||||
})
|
||||
sort.Sort(membersByPosAndString(res.globals))
|
||||
sort.Sort(membersByPosAndString(res.consts))
|
||||
sort.Sort(sorterrorType(res.types))
|
||||
|
||||
q.Output(lprog.Fset, res)
|
||||
return nil
|
||||
}
|
||||
|
||||
// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil.
|
||||
func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value {
|
||||
globals := make(map[*ssa.Global]ssa.Value)
|
||||
for _, pkg := range prog.AllPackages() {
|
||||
for _, mem := range pkg.Members {
|
||||
gbl, ok := mem.(*ssa.Global)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
gbltype := gbl.Type()
|
||||
// globals are always pointers
|
||||
if !types.Identical(deref(gbltype), builtinErrorType) {
|
||||
continue
|
||||
}
|
||||
if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) {
|
||||
continue
|
||||
}
|
||||
globals[gbl] = nil
|
||||
}
|
||||
}
|
||||
return globals
|
||||
}
|
||||
|
||||
// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil.
|
||||
func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst {
|
||||
constants := make(map[ssa.Const]*ssa.NamedConst)
|
||||
for _, pkg := range prog.AllPackages() {
|
||||
for _, mem := range pkg.Members {
|
||||
obj, ok := mem.(*ssa.NamedConst)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
consttype := obj.Type()
|
||||
if !types.AssignableTo(consttype, builtinErrorType) {
|
||||
continue
|
||||
}
|
||||
if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) {
|
||||
continue
|
||||
}
|
||||
constants[*obj.Value] = obj
|
||||
}
|
||||
}
|
||||
|
||||
return constants
|
||||
}
|
||||
|
||||
type membersByPosAndString []ssa.Member
|
||||
|
||||
func (a membersByPosAndString) Len() int { return len(a) }
|
||||
func (a membersByPosAndString) Less(i, j int) bool {
|
||||
cmp := a[i].Pos() - a[j].Pos()
|
||||
return cmp < 0 || cmp == 0 && a[i].String() < a[j].String()
|
||||
}
|
||||
func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type sorterrorType []*errorType
|
||||
|
||||
func (a sorterrorType) Len() int { return len(a) }
|
||||
func (a sorterrorType) Less(i, j int) bool {
|
||||
cmp := a[i].obj.Pos() - a[j].obj.Pos()
|
||||
return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String()
|
||||
}
|
||||
func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type errorType struct {
|
||||
typ types.Type // concrete type N or *N that implements error
|
||||
obj *types.TypeName // the named type N
|
||||
}
|
||||
|
||||
type whicherrsResult struct {
|
||||
qpos *queryPos
|
||||
errpos token.Pos
|
||||
globals []ssa.Member
|
||||
consts []ssa.Member
|
||||
types []*errorType
|
||||
}
|
||||
|
||||
func (r *whicherrsResult) PrintPlain(printf printfFunc) {
|
||||
if len(r.globals) > 0 {
|
||||
printf(r.qpos, "this error may point to these globals:")
|
||||
for _, g := range r.globals {
|
||||
printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg))
|
||||
}
|
||||
}
|
||||
if len(r.consts) > 0 {
|
||||
printf(r.qpos, "this error may contain these constants:")
|
||||
for _, c := range r.consts {
|
||||
printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg))
|
||||
}
|
||||
}
|
||||
if len(r.types) > 0 {
|
||||
printf(r.qpos, "this error may contain these dynamic types:")
|
||||
for _, t := range r.types {
|
||||
printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *whicherrsResult) JSON(fset *token.FileSet) []byte {
|
||||
we := &serial.WhichErrs{}
|
||||
we.ErrPos = fset.Position(r.errpos).String()
|
||||
for _, g := range r.globals {
|
||||
we.Globals = append(we.Globals, fset.Position(g.Pos()).String())
|
||||
}
|
||||
for _, c := range r.consts {
|
||||
we.Constants = append(we.Constants, fset.Position(c.Pos()).String())
|
||||
}
|
||||
for _, t := range r.types {
|
||||
var et serial.WhichErrsType
|
||||
et.Type = r.qpos.typeString(t.typ)
|
||||
et.Position = fset.Position(t.obj.Pos()).String()
|
||||
we.Types = append(we.Types, et)
|
||||
}
|
||||
return toJSON(we)
|
||||
}
|
Reference in New Issue
Block a user