.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / 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/event"
21         "golang.org/x/tools/internal/lsp/debug/tag"
22         "golang.org/x/tools/internal/lsp/protocol"
23         "golang.org/x/tools/internal/lsp/source"
24         "golang.org/x/tools/internal/span"
25         "golang.org/x/tools/internal/typesinternal"
26         errors "golang.org/x/xerrors"
27 )
28
29 func sourceError(ctx context.Context, snapshot *snapshot, pkg *pkg, e interface{}) (*source.Error, error) {
30         fset := snapshot.view.session.cache.fset
31         var (
32                 spn           span.Span
33                 err           error
34                 msg, category string
35                 code          typesinternal.ErrorCode
36                 kind          source.ErrorKind
37                 fixes         []source.SuggestedFix
38                 related       []source.RelatedInformation
39         )
40         switch e := e.(type) {
41         case packages.Error:
42                 kind = toSourceErrorKind(e.Kind)
43                 var ok bool
44                 if msg, spn, ok = parseGoListImportCycleError(ctx, snapshot, e, pkg); ok {
45                         kind = source.TypeError
46                         break
47                 }
48                 if e.Pos == "" {
49                         spn = parseGoListError(e.Msg)
50
51                         // We may not have been able to parse a valid span.
52                         if _, err := spanToRange(snapshot, pkg, spn); err != nil {
53                                 return &source.Error{
54                                         URI:     spn.URI(),
55                                         Message: msg,
56                                         Kind:    kind,
57                                 }, nil
58                         }
59                 } else {
60                         spn = span.Parse(e.Pos)
61                 }
62         case *scanner.Error:
63                 msg = e.Msg
64                 kind = source.ParseError
65                 spn, err = scannerErrorRange(snapshot, pkg, e.Pos)
66                 if err != nil {
67                         if ctx.Err() != nil {
68                                 return nil, ctx.Err()
69                         }
70                         event.Error(ctx, "no span for scanner.Error pos", err, tag.Package.Of(pkg.ID()))
71                         spn = span.Parse(e.Pos.String())
72                 }
73
74         case scanner.ErrorList:
75                 // The first parser error is likely the root cause of the problem.
76                 if e.Len() <= 0 {
77                         return nil, errors.Errorf("no errors in %v", e)
78                 }
79                 msg = e[0].Msg
80                 kind = source.ParseError
81                 spn, err = scannerErrorRange(snapshot, pkg, e[0].Pos)
82                 if err != nil {
83                         if ctx.Err() != nil {
84                                 return nil, ctx.Err()
85                         }
86                         event.Error(ctx, "no span for scanner.Error pos", err, tag.Package.Of(pkg.ID()))
87                         spn = span.Parse(e[0].Pos.String())
88                 }
89         case types.Error:
90                 msg = e.Msg
91                 kind = source.TypeError
92                 if !e.Pos.IsValid() {
93                         return nil, fmt.Errorf("invalid position for type error %v", e)
94                 }
95                 code, spn, err = typeErrorData(fset, pkg, e)
96                 if err != nil {
97                         return nil, err
98                 }
99         case extendedError:
100                 perr := e.primary
101                 msg = perr.Msg
102                 kind = source.TypeError
103                 if !perr.Pos.IsValid() {
104                         return nil, fmt.Errorf("invalid position for type error %v", e)
105                 }
106                 code, spn, err = typeErrorData(fset, pkg, e.primary)
107                 if err != nil {
108                         return nil, err
109                 }
110                 for _, s := range e.secondaries {
111                         var x source.RelatedInformation
112                         x.Message = s.Msg
113                         _, xspn, err := typeErrorData(fset, pkg, s)
114                         if err != nil {
115                                 return nil, fmt.Errorf("invalid position for type error %v", s)
116                         }
117                         x.URI = xspn.URI()
118                         rng, err := spanToRange(snapshot, pkg, xspn)
119                         if err != nil {
120                                 return nil, err
121                         }
122                         x.Range = rng
123                         related = append(related, x)
124                 }
125         case *analysis.Diagnostic:
126                 spn, err = span.NewRange(fset, e.Pos, e.End).Span()
127                 if err != nil {
128                         return nil, err
129                 }
130                 msg = e.Message
131                 kind = source.Analysis
132                 category = e.Category
133                 fixes, err = suggestedAnalysisFixes(snapshot, pkg, e)
134                 if err != nil {
135                         return nil, err
136                 }
137                 related, err = relatedInformation(snapshot, pkg, e)
138                 if err != nil {
139                         return nil, err
140                 }
141         default:
142                 panic(fmt.Sprintf("%T unexpected", e))
143         }
144         rng, err := spanToRange(snapshot, pkg, spn)
145         if err != nil {
146                 return nil, err
147         }
148         se := &source.Error{
149                 URI:            spn.URI(),
150                 Range:          rng,
151                 Message:        msg,
152                 Kind:           kind,
153                 Category:       category,
154                 SuggestedFixes: fixes,
155                 Related:        related,
156         }
157         if code != 0 {
158                 se.Code = code.String()
159                 se.CodeHref = typesCodeHref(snapshot, code)
160         }
161         return se, nil
162 }
163
164 func typesCodeHref(snapshot *snapshot, code typesinternal.ErrorCode) string {
165         target := snapshot.View().Options().LinkTarget
166         return fmt.Sprintf("%s/golang.org/x/tools/internal/typesinternal#%s", target, code.String())
167 }
168
169 func suggestedAnalysisFixes(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic) ([]source.SuggestedFix, error) {
170         var fixes []source.SuggestedFix
171         for _, fix := range diag.SuggestedFixes {
172                 edits := make(map[span.URI][]protocol.TextEdit)
173                 for _, e := range fix.TextEdits {
174                         spn, err := span.NewRange(snapshot.view.session.cache.fset, e.Pos, e.End).Span()
175                         if err != nil {
176                                 return nil, err
177                         }
178                         rng, err := spanToRange(snapshot, pkg, spn)
179                         if err != nil {
180                                 return nil, err
181                         }
182                         edits[spn.URI()] = append(edits[spn.URI()], protocol.TextEdit{
183                                 Range:   rng,
184                                 NewText: string(e.NewText),
185                         })
186                 }
187                 fixes = append(fixes, source.SuggestedFix{
188                         Title: fix.Message,
189                         Edits: edits,
190                 })
191         }
192         return fixes, nil
193 }
194
195 func relatedInformation(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic) ([]source.RelatedInformation, error) {
196         var out []source.RelatedInformation
197         for _, related := range diag.Related {
198                 spn, err := span.NewRange(snapshot.view.session.cache.fset, related.Pos, related.End).Span()
199                 if err != nil {
200                         return nil, err
201                 }
202                 rng, err := spanToRange(snapshot, pkg, spn)
203                 if err != nil {
204                         return nil, err
205                 }
206                 out = append(out, source.RelatedInformation{
207                         URI:     spn.URI(),
208                         Range:   rng,
209                         Message: related.Message,
210                 })
211         }
212         return out, nil
213 }
214
215 func toSourceErrorKind(kind packages.ErrorKind) source.ErrorKind {
216         switch kind {
217         case packages.ListError:
218                 return source.ListError
219         case packages.ParseError:
220                 return source.ParseError
221         case packages.TypeError:
222                 return source.TypeError
223         default:
224                 return source.UnknownError
225         }
226 }
227
228 func typeErrorData(fset *token.FileSet, pkg *pkg, terr types.Error) (typesinternal.ErrorCode, span.Span, error) {
229         ecode, start, end, ok := typesinternal.ReadGo116ErrorData(terr)
230         if !ok {
231                 start, end = terr.Pos, terr.Pos
232                 ecode = 0
233         }
234         posn := fset.Position(start)
235         pgf, err := pkg.File(span.URIFromPath(posn.Filename))
236         if err != nil {
237                 return 0, span.Span{}, err
238         }
239         if !end.IsValid() || end == start {
240                 end = analysisinternal.TypeErrorEndPos(fset, pgf.Src, start)
241         }
242         spn, err := parsedGoSpan(pgf, start, end)
243         if err != nil {
244                 return 0, span.Span{}, err
245         }
246         return ecode, spn, nil
247 }
248
249 func parsedGoSpan(pgf *source.ParsedGoFile, start, end token.Pos) (span.Span, error) {
250         return span.FileSpan(pgf.Tok, pgf.Mapper.Converter, start, end)
251 }
252
253 func scannerErrorRange(snapshot *snapshot, pkg *pkg, posn token.Position) (span.Span, error) {
254         fset := snapshot.view.session.cache.fset
255         pgf, err := pkg.File(span.URIFromPath(posn.Filename))
256         if err != nil {
257                 return span.Span{}, err
258         }
259         pos := pgf.Tok.Pos(posn.Offset)
260         return span.NewRange(fset, pos, pos).Span()
261 }
262
263 // spanToRange converts a span.Span to a protocol.Range,
264 // assuming that the span belongs to the package whose diagnostics are being computed.
265 func spanToRange(snapshot *snapshot, pkg *pkg, spn span.Span) (protocol.Range, error) {
266         pgf, err := pkg.File(spn.URI())
267         if err != nil {
268                 return protocol.Range{}, err
269         }
270         return pgf.Mapper.Range(spn)
271 }
272
273 // parseGoListError attempts to parse a standard `go list` error message
274 // by stripping off the trailing error message.
275 //
276 // It works only on errors whose message is prefixed by colon,
277 // followed by a space (": "). For example:
278 //
279 //   attributes.go:13:1: expected 'package', found 'type'
280 //
281 func parseGoListError(input string) span.Span {
282         input = strings.TrimSpace(input)
283         msgIndex := strings.Index(input, ": ")
284         if msgIndex < 0 {
285                 return span.Parse(input)
286         }
287         return span.Parse(input[:msgIndex])
288 }
289
290 func parseGoListImportCycleError(ctx context.Context, snapshot *snapshot, e packages.Error, pkg *pkg) (string, span.Span, bool) {
291         re := regexp.MustCompile(`(.*): import stack: \[(.+)\]`)
292         matches := re.FindStringSubmatch(strings.TrimSpace(e.Msg))
293         if len(matches) < 3 {
294                 return e.Msg, span.Span{}, false
295         }
296         msg := matches[1]
297         importList := strings.Split(matches[2], " ")
298         // Since the error is relative to the current package. The import that is causing
299         // the import cycle error is the second one in the list.
300         if len(importList) < 2 {
301                 return msg, span.Span{}, false
302         }
303         // Imports have quotation marks around them.
304         circImp := strconv.Quote(importList[1])
305         for _, cgf := range pkg.compiledGoFiles {
306                 // Search file imports for the import that is causing the import cycle.
307                 for _, imp := range cgf.File.Imports {
308                         if imp.Path.Value == circImp {
309                                 spn, err := span.NewRange(snapshot.view.session.cache.fset, imp.Pos(), imp.End()).Span()
310                                 if err != nil {
311                                         return msg, span.Span{}, false
312                                 }
313                                 return msg, spn, true
314                         }
315                 }
316         }
317         return msg, span.Span{}, false
318 }