.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / cmd / guru / what.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/cmd/guru/what.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/cmd/guru/what.go
new file mode 100644 (file)
index 0000000..82495b4
--- /dev/null
@@ -0,0 +1,283 @@
+// 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"
+       "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 = path.Join(segmentedAbsFileDir[len(segmentedAbsFileDir)-minD:]...)
+               }
+       }
+       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,
+       })
+}