Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
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
|
||||
}
|
Reference in New Issue
Block a user