.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 / cache / errors.go
1 // Copyright 2019 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 cache
6
7 import (
8         "context"
9         "fmt"
10         "go/scanner"
11         "go/token"
12         "go/types"
13         "regexp"
14         "strconv"
15         "strings"
16
17         "golang.org/x/tools/go/analysis"
18         "golang.org/x/tools/go/packages"
19         "golang.org/x/tools/internal/analysisinternal"
20         "golang.org/x/tools/internal/lsp/command"
21         "golang.org/x/tools/internal/lsp/protocol"
22         "golang.org/x/tools/internal/lsp/source"
23         "golang.org/x/tools/internal/span"
24         "golang.org/x/tools/internal/typesinternal"
25         errors "golang.org/x/xerrors"
26 )
27
28 func goPackagesErrorDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, e packages.Error) ([]*source.Diagnostic, error) {
29         if msg, spn, ok := parseGoListImportCycleError(ctx, snapshot, e, pkg); ok {
30                 rng, err := spanToRange(snapshot, pkg, spn)
31                 if err != nil {
32                         return nil, err
33                 }
34                 return []*source.Diagnostic{{
35                         URI:      spn.URI(),
36                         Range:    rng,
37                         Severity: protocol.SeverityError,
38                         Source:   source.TypeError,
39                         Message:  msg,
40                 }}, nil
41         }
42
43         var spn span.Span
44         if e.Pos == "" {
45                 spn = parseGoListError(e.Msg, pkg.m.config.Dir)
46                 // We may not have been able to parse a valid span. Apply the errors to all files.
47                 if _, err := spanToRange(snapshot, pkg, spn); err != nil {
48                         var diags []*source.Diagnostic
49                         for _, cgf := range pkg.compiledGoFiles {
50                                 diags = append(diags, &source.Diagnostic{
51                                         URI:      cgf.URI,
52                                         Severity: protocol.SeverityError,
53                                         Source:   source.ListError,
54                                         Message:  e.Msg,
55                                 })
56                         }
57                         return diags, nil
58                 }
59         } else {
60                 spn = span.ParseInDir(e.Pos, pkg.m.config.Dir)
61         }
62
63         rng, err := spanToRange(snapshot, pkg, spn)
64         if err != nil {
65                 return nil, err
66         }
67         return []*source.Diagnostic{{
68                 URI:      spn.URI(),
69                 Range:    rng,
70                 Severity: protocol.SeverityError,
71                 Source:   source.ListError,
72                 Message:  e.Msg,
73         }}, nil
74 }
75
76 func parseErrorDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, errList scanner.ErrorList) ([]*source.Diagnostic, error) {
77         // The first parser error is likely the root cause of the problem.
78         if errList.Len() <= 0 {
79                 return nil, errors.Errorf("no errors in %v", errList)
80         }
81         e := errList[0]
82         pgf, err := pkg.File(span.URIFromPath(e.Pos.Filename))
83         if err != nil {
84                 return nil, err
85         }
86         pos := pgf.Tok.Pos(e.Pos.Offset)
87         spn, err := span.NewRange(snapshot.FileSet(), pos, pos).Span()
88         if err != nil {
89                 return nil, err
90         }
91         rng, err := spanToRange(snapshot, pkg, spn)
92         if err != nil {
93                 return nil, err
94         }
95         return []*source.Diagnostic{{
96                 URI:      spn.URI(),
97                 Range:    rng,
98                 Severity: protocol.SeverityError,
99                 Source:   source.ParseError,
100                 Message:  e.Msg,
101         }}, nil
102 }
103
104 var importErrorRe = regexp.MustCompile(`could not import ([^\s]+)`)
105
106 func typeErrorDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, e extendedError) ([]*source.Diagnostic, error) {
107         code, spn, err := typeErrorData(snapshot.FileSet(), pkg, e.primary)
108         if err != nil {
109                 return nil, err
110         }
111         rng, err := spanToRange(snapshot, pkg, spn)
112         if err != nil {
113                 return nil, err
114         }
115         diag := &source.Diagnostic{
116                 URI:      spn.URI(),
117                 Range:    rng,
118                 Severity: protocol.SeverityError,
119                 Source:   source.TypeError,
120                 Message:  e.primary.Msg,
121         }
122         if code != 0 {
123                 diag.Code = code.String()
124                 diag.CodeHref = typesCodeHref(snapshot, code)
125         }
126
127         for _, secondary := range e.secondaries {
128                 _, secondarySpan, err := typeErrorData(snapshot.FileSet(), pkg, secondary)
129                 if err != nil {
130                         return nil, err
131                 }
132                 rng, err := spanToRange(snapshot, pkg, secondarySpan)
133                 if err != nil {
134                         return nil, err
135                 }
136                 diag.Related = append(diag.Related, source.RelatedInformation{
137                         URI:     secondarySpan.URI(),
138                         Range:   rng,
139                         Message: secondary.Msg,
140                 })
141         }
142
143         if match := importErrorRe.FindStringSubmatch(e.primary.Msg); match != nil {
144                 diag.SuggestedFixes, err = goGetQuickFixes(snapshot, spn.URI(), match[1])
145                 if err != nil {
146                         return nil, err
147                 }
148         }
149         return []*source.Diagnostic{diag}, nil
150 }
151
152 func goGetQuickFixes(snapshot *snapshot, uri span.URI, pkg string) ([]source.SuggestedFix, error) {
153         // Go get only supports module mode for now.
154         if snapshot.workspaceMode()&moduleMode == 0 {
155                 return nil, nil
156         }
157         title := fmt.Sprintf("go get package %v", pkg)
158         cmd, err := command.NewGoGetPackageCommand(title, command.GoGetPackageArgs{
159                 URI:        protocol.URIFromSpanURI(uri),
160                 AddRequire: true,
161                 Pkg:        pkg,
162         })
163         if err != nil {
164                 return nil, err
165         }
166         return []source.SuggestedFix{source.SuggestedFixFromCommand(cmd)}, nil
167 }
168
169 func analysisDiagnosticDiagnostics(ctx context.Context, snapshot *snapshot, pkg *pkg, a *analysis.Analyzer, e *analysis.Diagnostic) ([]*source.Diagnostic, error) {
170         var srcAnalyzer *source.Analyzer
171         // Find the analyzer that generated this diagnostic.
172         for _, sa := range source.EnabledAnalyzers(snapshot) {
173                 if a == sa.Analyzer {
174                         srcAnalyzer = sa
175                         break
176                 }
177         }
178
179         spn, err := span.NewRange(snapshot.FileSet(), e.Pos, e.End).Span()
180         if err != nil {
181                 return nil, err
182         }
183         rng, err := spanToRange(snapshot, pkg, spn)
184         if err != nil {
185                 return nil, err
186         }
187         fixes, err := suggestedAnalysisFixes(snapshot, pkg, e)
188         if err != nil {
189                 return nil, err
190         }
191         if srcAnalyzer.Fix != "" {
192                 cmd, err := command.NewApplyFixCommand(e.Message, command.ApplyFixArgs{
193                         URI:   protocol.URIFromSpanURI(spn.URI()),
194                         Range: rng,
195                         Fix:   srcAnalyzer.Fix,
196                 })
197                 if err != nil {
198                         return nil, err
199                 }
200                 fixes = append(fixes, source.SuggestedFixFromCommand(cmd))
201         }
202         related, err := relatedInformation(snapshot, pkg, e)
203         if err != nil {
204                 return nil, err
205         }
206         diag := &source.Diagnostic{
207                 URI:            spn.URI(),
208                 Range:          rng,
209                 Severity:       protocol.SeverityWarning,
210                 Source:         source.AnalyzerErrorKind(e.Category),
211                 Message:        e.Message,
212                 Related:        related,
213                 SuggestedFixes: fixes,
214                 Analyzer:       srcAnalyzer,
215         }
216         // If the fixes only delete code, assume that the diagnostic is reporting dead code.
217         if onlyDeletions(fixes) {
218                 diag.Tags = []protocol.DiagnosticTag{protocol.Unnecessary}
219         }
220         return []*source.Diagnostic{diag}, nil
221 }
222
223 // onlyDeletions returns true if all of the suggested fixes are deletions.
224 func onlyDeletions(fixes []source.SuggestedFix) bool {
225         for _, fix := range fixes {
226                 for _, edits := range fix.Edits {
227                         for _, edit := range edits {
228                                 if edit.NewText != "" {
229                                         return false
230                                 }
231                                 if protocol.ComparePosition(edit.Range.Start, edit.Range.End) == 0 {
232                                         return false
233                                 }
234                         }
235                 }
236         }
237         return len(fixes) > 0
238 }
239
240 func typesCodeHref(snapshot *snapshot, code typesinternal.ErrorCode) string {
241         target := snapshot.View().Options().LinkTarget
242         return fmt.Sprintf("https://%s/golang.org/x/tools/internal/typesinternal#%s", target, code.String())
243 }
244
245 func suggestedAnalysisFixes(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic) ([]source.SuggestedFix, error) {
246         var fixes []source.SuggestedFix
247         for _, fix := range diag.SuggestedFixes {
248                 edits := make(map[span.URI][]protocol.TextEdit)
249                 for _, e := range fix.TextEdits {
250                         spn, err := span.NewRange(snapshot.view.session.cache.fset, e.Pos, e.End).Span()
251                         if err != nil {
252                                 return nil, err
253                         }
254                         rng, err := spanToRange(snapshot, pkg, spn)
255                         if err != nil {
256                                 return nil, err
257                         }
258                         edits[spn.URI()] = append(edits[spn.URI()], protocol.TextEdit{
259                                 Range:   rng,
260                                 NewText: string(e.NewText),
261                         })
262                 }
263                 fixes = append(fixes, source.SuggestedFix{
264                         Title: fix.Message,
265                         Edits: edits,
266                 })
267         }
268         return fixes, nil
269 }
270
271 func relatedInformation(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic) ([]source.RelatedInformation, error) {
272         var out []source.RelatedInformation
273         for _, related := range diag.Related {
274                 spn, err := span.NewRange(snapshot.view.session.cache.fset, related.Pos, related.End).Span()
275                 if err != nil {
276                         return nil, err
277                 }
278                 rng, err := spanToRange(snapshot, pkg, spn)
279                 if err != nil {
280                         return nil, err
281                 }
282                 out = append(out, source.RelatedInformation{
283                         URI:     spn.URI(),
284                         Range:   rng,
285                         Message: related.Message,
286                 })
287         }
288         return out, nil
289 }
290
291 func typeErrorData(fset *token.FileSet, pkg *pkg, terr types.Error) (typesinternal.ErrorCode, span.Span, error) {
292         ecode, start, end, ok := typesinternal.ReadGo116ErrorData(terr)
293         if !ok {
294                 start, end = terr.Pos, terr.Pos
295                 ecode = 0
296         }
297         posn := fset.Position(start)
298         pgf, err := pkg.File(span.URIFromPath(posn.Filename))
299         if err != nil {
300                 return 0, span.Span{}, err
301         }
302         if !end.IsValid() || end == start {
303                 end = analysisinternal.TypeErrorEndPos(fset, pgf.Src, start)
304         }
305         spn, err := parsedGoSpan(pgf, start, end)
306         if err != nil {
307                 return 0, span.Span{}, err
308         }
309         return ecode, spn, nil
310 }
311
312 func parsedGoSpan(pgf *source.ParsedGoFile, start, end token.Pos) (span.Span, error) {
313         return span.FileSpan(pgf.Tok, pgf.Mapper.Converter, start, end)
314 }
315
316 // spanToRange converts a span.Span to a protocol.Range,
317 // assuming that the span belongs to the package whose diagnostics are being computed.
318 func spanToRange(snapshot *snapshot, pkg *pkg, spn span.Span) (protocol.Range, error) {
319         pgf, err := pkg.File(spn.URI())
320         if err != nil {
321                 return protocol.Range{}, err
322         }
323         return pgf.Mapper.Range(spn)
324 }
325
326 // parseGoListError attempts to parse a standard `go list` error message
327 // by stripping off the trailing error message.
328 //
329 // It works only on errors whose message is prefixed by colon,
330 // followed by a space (": "). For example:
331 //
332 //   attributes.go:13:1: expected 'package', found 'type'
333 //
334 func parseGoListError(input, wd string) span.Span {
335         input = strings.TrimSpace(input)
336         msgIndex := strings.Index(input, ": ")
337         if msgIndex < 0 {
338                 return span.Parse(input)
339         }
340         return span.ParseInDir(input[:msgIndex], wd)
341 }
342
343 func parseGoListImportCycleError(ctx context.Context, snapshot *snapshot, e packages.Error, pkg *pkg) (string, span.Span, bool) {
344         re := regexp.MustCompile(`(.*): import stack: \[(.+)\]`)
345         matches := re.FindStringSubmatch(strings.TrimSpace(e.Msg))
346         if len(matches) < 3 {
347                 return e.Msg, span.Span{}, false
348         }
349         msg := matches[1]
350         importList := strings.Split(matches[2], " ")
351         // Since the error is relative to the current package. The import that is causing
352         // the import cycle error is the second one in the list.
353         if len(importList) < 2 {
354                 return msg, span.Span{}, false
355         }
356         // Imports have quotation marks around them.
357         circImp := strconv.Quote(importList[1])
358         for _, cgf := range pkg.compiledGoFiles {
359                 // Search file imports for the import that is causing the import cycle.
360                 for _, imp := range cgf.File.Imports {
361                         if imp.Path.Value == circImp {
362                                 spn, err := span.NewRange(snapshot.view.session.cache.fset, imp.Pos(), imp.End()).Span()
363                                 if err != nil {
364                                         return msg, span.Span{}, false
365                                 }
366                                 return msg, spn, true
367                         }
368                 }
369         }
370         return msg, span.Span{}, false
371 }