.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / lsp / source / gc_annotations.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/source/gc_annotations.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/source/gc_annotations.go
new file mode 100644 (file)
index 0000000..3616bbf
--- /dev/null
@@ -0,0 +1,214 @@
+// Copyright 2020 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 (
+       "bytes"
+       "context"
+       "encoding/json"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "strings"
+
+       "golang.org/x/tools/internal/gocommand"
+       "golang.org/x/tools/internal/lsp/protocol"
+       "golang.org/x/tools/internal/span"
+)
+
+type Annotation string
+
+const (
+       // Nil controls nil checks.
+       Nil Annotation = "nil"
+
+       // Escape controls diagnostics about escape choices.
+       Escape Annotation = "escape"
+
+       // Inline controls diagnostics about inlining choices.
+       Inline Annotation = "inline"
+
+       // Bounds controls bounds checking diagnostics.
+       Bounds Annotation = "bounds"
+)
+
+func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkg Package) (map[VersionedFileIdentity][]*Diagnostic, error) {
+       if len(pkg.CompiledGoFiles()) == 0 {
+               return nil, nil
+       }
+       pkgDir := filepath.Dir(pkg.CompiledGoFiles()[0].URI.Filename())
+       outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid()))
+
+       if err := os.MkdirAll(outDir, 0700); err != nil {
+               return nil, err
+       }
+       tmpFile, err := ioutil.TempFile(os.TempDir(), "gopls-x")
+       if err != nil {
+               return nil, err
+       }
+       defer os.Remove(tmpFile.Name())
+
+       outDirURI := span.URIFromPath(outDir)
+       // GC details doesn't handle Windows URIs in the form of "file:///C:/...",
+       // so rewrite them to "file://C:/...". See golang/go#41614.
+       if !strings.HasPrefix(outDir, "/") {
+               outDirURI = span.URI(strings.Replace(string(outDirURI), "file:///", "file://", 1))
+       }
+       inv := &gocommand.Invocation{
+               Verb: "build",
+               Args: []string{
+                       fmt.Sprintf("-gcflags=-json=0,%s", outDirURI),
+                       fmt.Sprintf("-o=%s", tmpFile.Name()),
+                       ".",
+               },
+               WorkingDir: pkgDir,
+       }
+       _, err = snapshot.RunGoCommandDirect(ctx, Normal, inv)
+       if err != nil {
+               return nil, err
+       }
+       files, err := findJSONFiles(outDir)
+       if err != nil {
+               return nil, err
+       }
+       reports := make(map[VersionedFileIdentity][]*Diagnostic)
+       opts := snapshot.View().Options()
+       var parseError error
+       for _, fn := range files {
+               uri, diagnostics, err := parseDetailsFile(fn, opts)
+               if err != nil {
+                       // expect errors for all the files, save 1
+                       parseError = err
+               }
+               fh := snapshot.FindFile(uri)
+               if fh == nil {
+                       continue
+               }
+               if pkgDir != filepath.Dir(fh.URI().Filename()) {
+                       // https://github.com/golang/go/issues/42198
+                       // sometimes the detail diagnostics generated for files
+                       // outside the package can never be taken back.
+                       continue
+               }
+               reports[fh.VersionedFileIdentity()] = diagnostics
+       }
+       return reports, parseError
+}
+
+func parseDetailsFile(filename string, options *Options) (span.URI, []*Diagnostic, error) {
+       buf, err := ioutil.ReadFile(filename)
+       if err != nil {
+               return "", nil, err
+       }
+       var (
+               uri         span.URI
+               i           int
+               diagnostics []*Diagnostic
+       )
+       type metadata struct {
+               File string `json:"file,omitempty"`
+       }
+       for dec := json.NewDecoder(bytes.NewReader(buf)); dec.More(); {
+               // The first element always contains metadata.
+               if i == 0 {
+                       i++
+                       m := new(metadata)
+                       if err := dec.Decode(m); err != nil {
+                               return "", nil, err
+                       }
+                       if !strings.HasSuffix(m.File, ".go") {
+                               continue // <autogenerated>
+                       }
+                       uri = span.URIFromPath(m.File)
+                       continue
+               }
+               d := new(protocol.Diagnostic)
+               if err := dec.Decode(d); err != nil {
+                       return "", nil, err
+               }
+               msg := d.Code.(string)
+               if msg != "" {
+                       msg = fmt.Sprintf("%s(%s)", msg, d.Message)
+               }
+               if !showDiagnostic(msg, d.Source, options) {
+                       continue
+               }
+               var related []RelatedInformation
+               for _, ri := range d.RelatedInformation {
+                       related = append(related, RelatedInformation{
+                               URI:     ri.Location.URI.SpanURI(),
+                               Range:   zeroIndexedRange(ri.Location.Range),
+                               Message: ri.Message,
+                       })
+               }
+               diagnostic := &Diagnostic{
+                       URI:      uri,
+                       Range:    zeroIndexedRange(d.Range),
+                       Message:  msg,
+                       Severity: d.Severity,
+                       Source:   OptimizationDetailsError, // d.Source is always "go compiler" as of 1.16, use our own
+                       Tags:     d.Tags,
+                       Related:  related,
+               }
+               diagnostics = append(diagnostics, diagnostic)
+               i++
+       }
+       return uri, diagnostics, nil
+}
+
+// showDiagnostic reports whether a given diagnostic should be shown to the end
+// user, given the current options.
+func showDiagnostic(msg, source string, o *Options) bool {
+       if source != "go compiler" {
+               return false
+       }
+       if o.Annotations == nil {
+               return true
+       }
+       switch {
+       case strings.HasPrefix(msg, "canInline") ||
+               strings.HasPrefix(msg, "cannotInline") ||
+               strings.HasPrefix(msg, "inlineCall"):
+               return o.Annotations[Inline]
+       case strings.HasPrefix(msg, "escape") || msg == "leak":
+               return o.Annotations[Escape]
+       case strings.HasPrefix(msg, "nilcheck"):
+               return o.Annotations[Nil]
+       case strings.HasPrefix(msg, "isInBounds") ||
+               strings.HasPrefix(msg, "isSliceInBounds"):
+               return o.Annotations[Bounds]
+       }
+       return false
+}
+
+// The range produced by the compiler is 1-indexed, so subtract range by 1.
+func zeroIndexedRange(rng protocol.Range) protocol.Range {
+       return protocol.Range{
+               Start: protocol.Position{
+                       Line:      rng.Start.Line - 1,
+                       Character: rng.Start.Character - 1,
+               },
+               End: protocol.Position{
+                       Line:      rng.End.Line - 1,
+                       Character: rng.End.Character - 1,
+               },
+       }
+}
+
+func findJSONFiles(dir string) ([]string, error) {
+       ans := []string{}
+       f := func(path string, fi os.FileInfo, _ error) error {
+               if fi.IsDir() {
+                       return nil
+               }
+               if strings.HasSuffix(path, ".json") {
+                       ans = append(ans, path)
+               }
+               return nil
+       }
+       err := filepath.Walk(dir, f)
+       return ans, err
+}