Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / source / util.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/source/util.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/source/util.go
new file mode 100644 (file)
index 0000000..b3d87f9
--- /dev/null
@@ -0,0 +1,517 @@
+// Copyright 2019 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 source
+
+import (
+       "context"
+       "encoding/json"
+       "fmt"
+       "go/ast"
+       "go/printer"
+       "go/token"
+       "go/types"
+       "path/filepath"
+       "regexp"
+       "sort"
+       "strconv"
+       "strings"
+
+       "golang.org/x/tools/internal/lsp/protocol"
+       "golang.org/x/tools/internal/span"
+       errors "golang.org/x/xerrors"
+)
+
+// MappedRange provides mapped protocol.Range for a span.Range, accounting for
+// UTF-16 code points.
+type MappedRange struct {
+       spanRange span.Range
+       m         *protocol.ColumnMapper
+
+       // protocolRange is the result of converting the spanRange using the mapper.
+       // It is computed on-demand.
+       protocolRange *protocol.Range
+}
+
+// NewMappedRange returns a MappedRange for the given start and end token.Pos.
+func NewMappedRange(fset *token.FileSet, m *protocol.ColumnMapper, start, end token.Pos) MappedRange {
+       return MappedRange{
+               spanRange: span.Range{
+                       FileSet:   fset,
+                       Start:     start,
+                       End:       end,
+                       Converter: m.Converter,
+               },
+               m: m,
+       }
+}
+
+func (s MappedRange) Range() (protocol.Range, error) {
+       if s.protocolRange == nil {
+               spn, err := s.spanRange.Span()
+               if err != nil {
+                       return protocol.Range{}, err
+               }
+               prng, err := s.m.Range(spn)
+               if err != nil {
+                       return protocol.Range{}, err
+               }
+               s.protocolRange = &prng
+       }
+       return *s.protocolRange, nil
+}
+
+func (s MappedRange) Span() (span.Span, error) {
+       return s.spanRange.Span()
+}
+
+func (s MappedRange) SpanRange() span.Range {
+       return s.spanRange
+}
+
+func (s MappedRange) URI() span.URI {
+       return s.m.URI
+}
+
+// GetParsedFile is a convenience function that extracts the Package and
+// ParsedGoFile for a file in a Snapshot. pkgPolicy is one of NarrowestPackage/
+// WidestPackage.
+func GetParsedFile(ctx context.Context, snapshot Snapshot, fh FileHandle, pkgPolicy PackageFilter) (Package, *ParsedGoFile, error) {
+       pkg, err := snapshot.PackageForFile(ctx, fh.URI(), TypecheckWorkspace, pkgPolicy)
+       if err != nil {
+               return nil, nil, err
+       }
+       pgh, err := pkg.File(fh.URI())
+       return pkg, pgh, err
+}
+
+func IsGenerated(ctx context.Context, snapshot Snapshot, uri span.URI) bool {
+       fh, err := snapshot.GetFile(ctx, uri)
+       if err != nil {
+               return false
+       }
+       pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader)
+       if err != nil {
+               return false
+       }
+       tok := snapshot.FileSet().File(pgf.File.Pos())
+       if tok == nil {
+               return false
+       }
+       for _, commentGroup := range pgf.File.Comments {
+               for _, comment := range commentGroup.List {
+                       if matched := generatedRx.MatchString(comment.Text); matched {
+                               // Check if comment is at the beginning of the line in source.
+                               if pos := tok.Position(comment.Slash); pos.Column == 1 {
+                                       return true
+                               }
+                       }
+               }
+       }
+       return false
+}
+
+func nodeToProtocolRange(snapshot Snapshot, pkg Package, n ast.Node) (protocol.Range, error) {
+       mrng, err := posToMappedRange(snapshot, pkg, n.Pos(), n.End())
+       if err != nil {
+               return protocol.Range{}, err
+       }
+       return mrng.Range()
+}
+
+func objToMappedRange(snapshot Snapshot, pkg Package, obj types.Object) (MappedRange, error) {
+       if pkgName, ok := obj.(*types.PkgName); ok {
+               // An imported Go package has a package-local, unqualified name.
+               // When the name matches the imported package name, there is no
+               // identifier in the import spec with the local package name.
+               //
+               // For example:
+               //              import "go/ast"         // name "ast" matches package name
+               //              import a "go/ast"       // name "a" does not match package name
+               //
+               // When the identifier does not appear in the source, have the range
+               // of the object be the import path, including quotes.
+               if pkgName.Imported().Name() == pkgName.Name() {
+                       return posToMappedRange(snapshot, pkg, obj.Pos(), obj.Pos()+token.Pos(len(pkgName.Imported().Path())+2))
+               }
+       }
+       return nameToMappedRange(snapshot, pkg, obj.Pos(), obj.Name())
+}
+
+func nameToMappedRange(snapshot Snapshot, pkg Package, pos token.Pos, name string) (MappedRange, error) {
+       return posToMappedRange(snapshot, pkg, pos, pos+token.Pos(len(name)))
+}
+
+func posToMappedRange(snapshot Snapshot, pkg Package, pos, end token.Pos) (MappedRange, error) {
+       logicalFilename := snapshot.FileSet().File(pos).Position(pos).Filename
+       pgf, _, err := findFileInDeps(pkg, span.URIFromPath(logicalFilename))
+       if err != nil {
+               return MappedRange{}, err
+       }
+       if !pos.IsValid() {
+               return MappedRange{}, errors.Errorf("invalid position for %v", pos)
+       }
+       if !end.IsValid() {
+               return MappedRange{}, errors.Errorf("invalid position for %v", end)
+       }
+       return NewMappedRange(snapshot.FileSet(), pgf.Mapper, pos, end), nil
+}
+
+// Matches cgo generated comment as well as the proposed standard:
+//     https://golang.org/s/generatedcode
+var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)
+
+func DetectLanguage(langID, filename string) FileKind {
+       switch langID {
+       case "go":
+               return Go
+       case "go.mod":
+               return Mod
+       case "go.sum":
+               return Sum
+       }
+       // Fallback to detecting the language based on the file extension.
+       switch filepath.Ext(filename) {
+       case ".mod":
+               return Mod
+       case ".sum":
+               return Sum
+       default: // fallback to Go
+               return Go
+       }
+}
+
+func (k FileKind) String() string {
+       switch k {
+       case Mod:
+               return "go.mod"
+       case Sum:
+               return "go.sum"
+       default:
+               return "go"
+       }
+}
+
+// nodeAtPos returns the index and the node whose position is contained inside
+// the node list.
+func nodeAtPos(nodes []ast.Node, pos token.Pos) (ast.Node, int) {
+       if nodes == nil {
+               return nil, -1
+       }
+       for i, node := range nodes {
+               if node.Pos() <= pos && pos <= node.End() {
+                       return node, i
+               }
+       }
+       return nil, -1
+}
+
+// IsInterface returns if a types.Type is an interface
+func IsInterface(T types.Type) bool {
+       return T != nil && types.IsInterface(T)
+}
+
+// FormatNode returns the "pretty-print" output for an ast node.
+func FormatNode(fset *token.FileSet, n ast.Node) string {
+       var buf strings.Builder
+       if err := printer.Fprint(&buf, fset, n); err != nil {
+               return ""
+       }
+       return buf.String()
+}
+
+// Deref returns a pointer's element type, traversing as many levels as needed.
+// Otherwise it returns typ.
+func Deref(typ types.Type) types.Type {
+       for {
+               p, ok := typ.Underlying().(*types.Pointer)
+               if !ok {
+                       return typ
+               }
+               typ = p.Elem()
+       }
+}
+
+func SortDiagnostics(d []*Diagnostic) {
+       sort.Slice(d, func(i int, j int) bool {
+               return CompareDiagnostic(d[i], d[j]) < 0
+       })
+}
+
+func CompareDiagnostic(a, b *Diagnostic) int {
+       if r := protocol.CompareRange(a.Range, b.Range); r != 0 {
+               return r
+       }
+       if a.Source < b.Source {
+               return -1
+       }
+       if a.Message < b.Message {
+               return -1
+       }
+       if a.Message == b.Message {
+               return 0
+       }
+       return 1
+}
+
+// FindPosInPackage finds the parsed file for a position in a given search
+// package.
+func FindPosInPackage(snapshot Snapshot, searchpkg Package, pos token.Pos) (*ParsedGoFile, Package, error) {
+       tok := snapshot.FileSet().File(pos)
+       if tok == nil {
+               return nil, nil, errors.Errorf("no file for pos in package %s", searchpkg.ID())
+       }
+       uri := span.URIFromPath(tok.Name())
+
+       pgf, pkg, err := findFileInDeps(searchpkg, uri)
+       if err != nil {
+               return nil, nil, err
+       }
+       return pgf, pkg, nil
+}
+
+// findFileInDeps finds uri in pkg or its dependencies.
+func findFileInDeps(pkg Package, uri span.URI) (*ParsedGoFile, Package, error) {
+       queue := []Package{pkg}
+       seen := make(map[string]bool)
+
+       for len(queue) > 0 {
+               pkg := queue[0]
+               queue = queue[1:]
+               seen[pkg.ID()] = true
+
+               if pgf, err := pkg.File(uri); err == nil {
+                       return pgf, pkg, nil
+               }
+               for _, dep := range pkg.Imports() {
+                       if !seen[dep.ID()] {
+                               queue = append(queue, dep)
+                       }
+               }
+       }
+       return nil, nil, errors.Errorf("no file for %s in package %s", uri, pkg.ID())
+}
+
+// MarshalArgs encodes the given arguments to json.RawMessages. This function
+// is used to construct arguments to a protocol.Command.
+//
+// Example usage:
+//
+//   jsonArgs, err := EncodeArgs(1, "hello", true, StructuredArg{42, 12.6})
+//
+func MarshalArgs(args ...interface{}) ([]json.RawMessage, error) {
+       var out []json.RawMessage
+       for _, arg := range args {
+               argJSON, err := json.Marshal(arg)
+               if err != nil {
+                       return nil, err
+               }
+               out = append(out, argJSON)
+       }
+       return out, nil
+}
+
+// UnmarshalArgs decodes the given json.RawMessages to the variables provided
+// by args. Each element of args should be a pointer.
+//
+// Example usage:
+//
+//   var (
+//       num int
+//       str string
+//       bul bool
+//       structured StructuredArg
+//   )
+//   err := UnmarshalArgs(args, &num, &str, &bul, &structured)
+//
+func UnmarshalArgs(jsonArgs []json.RawMessage, args ...interface{}) error {
+       if len(args) != len(jsonArgs) {
+               return fmt.Errorf("DecodeArgs: expected %d input arguments, got %d JSON arguments", len(args), len(jsonArgs))
+       }
+       for i, arg := range args {
+               if err := json.Unmarshal(jsonArgs[i], arg); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+// ImportPath returns the unquoted import path of s,
+// or "" if the path is not properly quoted.
+func ImportPath(s *ast.ImportSpec) string {
+       t, err := strconv.Unquote(s.Path.Value)
+       if err != nil {
+               return ""
+       }
+       return t
+}
+
+// NodeContains returns true if a node encloses a given position pos.
+func NodeContains(n ast.Node, pos token.Pos) bool {
+       return n != nil && n.Pos() <= pos && pos <= n.End()
+}
+
+// CollectScopes returns all scopes in an ast path, ordered as innermost scope
+// first.
+func CollectScopes(info *types.Info, path []ast.Node, pos token.Pos) []*types.Scope {
+       // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
+       var scopes []*types.Scope
+       for _, n := range path {
+               // Include *FuncType scope if pos is inside the function body.
+               switch node := n.(type) {
+               case *ast.FuncDecl:
+                       if node.Body != nil && NodeContains(node.Body, pos) {
+                               n = node.Type
+                       }
+               case *ast.FuncLit:
+                       if node.Body != nil && NodeContains(node.Body, pos) {
+                               n = node.Type
+                       }
+               }
+               scopes = append(scopes, info.Scopes[n])
+       }
+       return scopes
+}
+
+// Qualifier returns a function that appropriately formats a types.PkgName
+// appearing in a *ast.File.
+func Qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier {
+       // Construct mapping of import paths to their defined or implicit names.
+       imports := make(map[*types.Package]string)
+       for _, imp := range f.Imports {
+               var obj types.Object
+               if imp.Name != nil {
+                       obj = info.Defs[imp.Name]
+               } else {
+                       obj = info.Implicits[imp]
+               }
+               if pkgname, ok := obj.(*types.PkgName); ok {
+                       imports[pkgname.Imported()] = pkgname.Name()
+               }
+       }
+       // Define qualifier to replace full package paths with names of the imports.
+       return func(p *types.Package) string {
+               if p == pkg {
+                       return ""
+               }
+               if name, ok := imports[p]; ok {
+                       return name
+               }
+               return p.Name()
+       }
+}
+
+// isDirective reports whether c is a comment directive.
+//
+// Copied and adapted from go/src/go/ast/ast.go.
+func isDirective(c string) bool {
+       if len(c) < 3 {
+               return false
+       }
+       if c[1] != '/' {
+               return false
+       }
+       //-style comment (no newline at the end)
+       c = c[2:]
+       if len(c) == 0 {
+               // empty line
+               return false
+       }
+       // "//line " is a line directive.
+       // (The // has been removed.)
+       if strings.HasPrefix(c, "line ") {
+               return true
+       }
+
+       // "//[a-z0-9]+:[a-z0-9]"
+       // (The // has been removed.)
+       colon := strings.Index(c, ":")
+       if colon <= 0 || colon+1 >= len(c) {
+               return false
+       }
+       for i := 0; i <= colon+1; i++ {
+               if i == colon {
+                       continue
+               }
+               b := c[i]
+               if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') {
+                       return false
+               }
+       }
+       return true
+}
+
+// InDir checks whether path is in the file tree rooted at dir.
+// If so, InDir returns an equivalent path relative to dir.
+// If not, InDir returns an empty string.
+// InDir makes some effort to succeed even in the presence of symbolic links.
+//
+// Copied and slightly adjusted from go/src/cmd/go/internal/search/search.go.
+func InDir(dir, path string) bool {
+       if rel := inDirLex(path, dir); rel != "" {
+               return true
+       }
+       xpath, err := filepath.EvalSymlinks(path)
+       if err != nil || xpath == path {
+               xpath = ""
+       } else {
+               if rel := inDirLex(xpath, dir); rel != "" {
+                       return true
+               }
+       }
+
+       xdir, err := filepath.EvalSymlinks(dir)
+       if err == nil && xdir != dir {
+               if rel := inDirLex(path, xdir); rel != "" {
+                       return true
+               }
+               if xpath != "" {
+                       if rel := inDirLex(xpath, xdir); rel != "" {
+                               return true
+                       }
+               }
+       }
+       return false
+}
+
+// Copied from go/src/cmd/go/internal/search/search.go.
+//
+// inDirLex is like inDir but only checks the lexical form of the file names.
+// It does not consider symbolic links.
+// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
+// return the suffix. Most uses of str.HasFilePathPrefix should probably
+// be calling InDir instead.
+func inDirLex(path, dir string) string {
+       pv := strings.ToUpper(filepath.VolumeName(path))
+       dv := strings.ToUpper(filepath.VolumeName(dir))
+       path = path[len(pv):]
+       dir = dir[len(dv):]
+       switch {
+       default:
+               return ""
+       case pv != dv:
+               return ""
+       case len(path) == len(dir):
+               if path == dir {
+                       return "."
+               }
+               return ""
+       case dir == "":
+               return path
+       case len(path) > len(dir):
+               if dir[len(dir)-1] == filepath.Separator {
+                       if path[:len(dir)] == dir {
+                               return path[len(dir):]
+                       }
+                       return ""
+               }
+               if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
+                       if len(path) == len(dir)+1 {
+                               return "."
+                       }
+                       return path[len(dir)+1:]
+               }
+               return ""
+       }
+}