Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / source / gc_annotations.go
1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package source
6
7 import (
8         "bytes"
9         "context"
10         "encoding/json"
11         "fmt"
12         "io/ioutil"
13         "os"
14         "path/filepath"
15         "strings"
16
17         "golang.org/x/tools/internal/lsp/protocol"
18         "golang.org/x/tools/internal/span"
19 )
20
21 func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.URI) (map[VersionedFileIdentity][]*Diagnostic, error) {
22         outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid()))
23
24         if err := os.MkdirAll(outDir, 0700); err != nil {
25                 return nil, err
26         }
27         tmpFile, err := ioutil.TempFile(os.TempDir(), "gopls-x")
28         if err != nil {
29                 return nil, err
30         }
31         defer os.Remove(tmpFile.Name())
32
33         outDirURI := span.URIFromPath(outDir)
34         // GC details doesn't handle Windows URIs in the form of "file:///C:/...",
35         // so rewrite them to "file://C:/...". See golang/go#41614.
36         if !strings.HasPrefix(outDir, "/") {
37                 outDirURI = span.URI(strings.Replace(string(outDirURI), "file:///", "file://", 1))
38         }
39         args := []string{
40                 fmt.Sprintf("-gcflags=-json=0,%s", outDirURI),
41                 fmt.Sprintf("-o=%s", tmpFile.Name()),
42                 ".",
43         }
44         err = snapshot.RunGoCommandDirect(ctx, pkgDir.Filename(), "build", args)
45         if err != nil {
46                 return nil, err
47         }
48         files, err := findJSONFiles(outDir)
49         if err != nil {
50                 return nil, err
51         }
52         reports := make(map[VersionedFileIdentity][]*Diagnostic)
53         opts := snapshot.View().Options()
54         var parseError error
55         for _, fn := range files {
56                 uri, diagnostics, err := parseDetailsFile(fn, opts)
57                 if err != nil {
58                         // expect errors for all the files, save 1
59                         parseError = err
60                 }
61                 fh := snapshot.FindFile(uri)
62                 if fh == nil {
63                         continue
64                 }
65                 reports[fh.VersionedFileIdentity()] = diagnostics
66         }
67         return reports, parseError
68 }
69
70 func parseDetailsFile(filename string, options *Options) (span.URI, []*Diagnostic, error) {
71         buf, err := ioutil.ReadFile(filename)
72         if err != nil {
73                 return "", nil, err
74         }
75         var (
76                 uri         span.URI
77                 i           int
78                 diagnostics []*Diagnostic
79         )
80         type metadata struct {
81                 File string `json:"file,omitempty"`
82         }
83         for dec := json.NewDecoder(bytes.NewReader(buf)); dec.More(); {
84                 // The first element always contains metadata.
85                 if i == 0 {
86                         i++
87                         m := new(metadata)
88                         if err := dec.Decode(m); err != nil {
89                                 return "", nil, err
90                         }
91                         if !strings.HasSuffix(m.File, ".go") {
92                                 continue // <autogenerated>
93                         }
94                         uri = span.URIFromPath(m.File)
95                         continue
96                 }
97                 d := new(protocol.Diagnostic)
98                 if err := dec.Decode(d); err != nil {
99                         return "", nil, err
100                 }
101                 msg := d.Code.(string)
102                 if msg != "" {
103                         msg = fmt.Sprintf("%s(%s)", msg, d.Message)
104                 }
105                 if skipDiagnostic(msg, d.Source, options) {
106                         continue
107                 }
108                 var related []RelatedInformation
109                 for _, ri := range d.RelatedInformation {
110                         related = append(related, RelatedInformation{
111                                 URI:     ri.Location.URI.SpanURI(),
112                                 Range:   zeroIndexedRange(ri.Location.Range),
113                                 Message: ri.Message,
114                         })
115                 }
116                 diagnostic := &Diagnostic{
117                         Range:    zeroIndexedRange(d.Range),
118                         Message:  msg,
119                         Severity: d.Severity,
120                         Source:   d.Source,
121                         Tags:     d.Tags,
122                         Related:  related,
123                 }
124                 diagnostics = append(diagnostics, diagnostic)
125                 i++
126         }
127         return uri, diagnostics, nil
128 }
129
130 // skipDiagnostic reports whether a given diagnostic should be shown to the end
131 // user, given the current options.
132 func skipDiagnostic(msg, source string, o *Options) bool {
133         if source != "go compiler" {
134                 return false
135         }
136         switch {
137         case o.Annotations["noInline"]:
138                 return strings.HasPrefix(msg, "canInline") ||
139                         strings.HasPrefix(msg, "cannotInline") ||
140                         strings.HasPrefix(msg, "inlineCall")
141         case o.Annotations["noEscape"]:
142                 return strings.HasPrefix(msg, "escape") || msg == "leak"
143         case o.Annotations["noNilcheck"]:
144                 return strings.HasPrefix(msg, "nilcheck")
145         case o.Annotations["noBounds"]:
146                 return strings.HasPrefix(msg, "isInBounds") ||
147                         strings.HasPrefix(msg, "isSliceInBounds")
148         }
149         return false
150 }
151
152 // The range produced by the compiler is 1-indexed, so subtract range by 1.
153 func zeroIndexedRange(rng protocol.Range) protocol.Range {
154         return protocol.Range{
155                 Start: protocol.Position{
156                         Line:      rng.Start.Line - 1,
157                         Character: rng.Start.Character - 1,
158                 },
159                 End: protocol.Position{
160                         Line:      rng.End.Line - 1,
161                         Character: rng.End.Character - 1,
162                 },
163         }
164 }
165
166 func findJSONFiles(dir string) ([]string, error) {
167         ans := []string{}
168         f := func(path string, fi os.FileInfo, _ error) error {
169                 if fi.IsDir() {
170                         return nil
171                 }
172                 if strings.HasSuffix(path, ".json") {
173                         ans = append(ans, path)
174                 }
175                 return nil
176         }
177         err := filepath.Walk(dir, f)
178         return ans, err
179 }