mirror of
https://github.com/golang/go
synced 2024-11-18 23:14:43 -07:00
104 lines
2.5 KiB
Go
104 lines
2.5 KiB
Go
|
package cache
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"go/scanner"
|
||
|
"go/types"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/tools/go/packages"
|
||
|
"golang.org/x/tools/internal/lsp/protocol"
|
||
|
"golang.org/x/tools/internal/lsp/source"
|
||
|
"golang.org/x/tools/internal/span"
|
||
|
)
|
||
|
|
||
|
func sourceError(ctx context.Context, view *view, pkg *pkg, e error) (*source.Error, error) {
|
||
|
var (
|
||
|
spn span.Span
|
||
|
msg string
|
||
|
kind packages.ErrorKind
|
||
|
)
|
||
|
switch e := e.(type) {
|
||
|
case packages.Error:
|
||
|
if e.Pos == "" {
|
||
|
spn = parseGoListError(e.Msg)
|
||
|
} else {
|
||
|
spn = span.Parse(e.Pos)
|
||
|
}
|
||
|
msg = e.Msg
|
||
|
kind = e.Kind
|
||
|
case *scanner.Error:
|
||
|
msg = e.Msg
|
||
|
kind = packages.ParseError
|
||
|
spn = span.Parse(e.Pos.String())
|
||
|
case scanner.ErrorList:
|
||
|
// The first parser error is likely the root cause of the problem.
|
||
|
if e.Len() > 0 {
|
||
|
spn = span.Parse(e[0].Pos.String())
|
||
|
msg = e[0].Msg
|
||
|
kind = packages.ParseError
|
||
|
}
|
||
|
case types.Error:
|
||
|
spn = span.Parse(view.session.cache.fset.Position(e.Pos).String())
|
||
|
msg = e.Msg
|
||
|
kind = packages.TypeError
|
||
|
}
|
||
|
rng, err := spanToRange(ctx, pkg, spn, kind == packages.TypeError)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &source.Error{
|
||
|
URI: spn.URI(),
|
||
|
Range: rng,
|
||
|
Msg: msg,
|
||
|
Kind: kind,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// spanToRange converts a span.Span to a protocol.Range,
|
||
|
// assuming that the span belongs to the package whose diagnostics are being computed.
|
||
|
func spanToRange(ctx context.Context, pkg *pkg, spn span.Span, isTypeError bool) (protocol.Range, error) {
|
||
|
ph, err := pkg.File(spn.URI())
|
||
|
if err != nil {
|
||
|
return protocol.Range{}, err
|
||
|
}
|
||
|
_, m, _, err := ph.Cached(ctx)
|
||
|
if err != nil {
|
||
|
return protocol.Range{}, err
|
||
|
}
|
||
|
data, _, err := ph.File().Read(ctx)
|
||
|
if err != nil {
|
||
|
return protocol.Range{}, err
|
||
|
}
|
||
|
if spn.IsPoint() && isTypeError {
|
||
|
if s, err := spn.WithOffset(m.Converter); err == nil {
|
||
|
start := s.Start()
|
||
|
offset := start.Offset()
|
||
|
if offset < len(data) {
|
||
|
if width := bytes.IndexAny(data[offset:], " \n,():;[]"); width > 0 {
|
||
|
spn = span.New(spn.URI(), start, span.NewPoint(start.Line(), start.Column()+width, offset+width))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return m.Range(spn)
|
||
|
}
|
||
|
|
||
|
// parseGoListError attempts to parse a standard `go list` error message
|
||
|
// by stripping off the trailing error message.
|
||
|
//
|
||
|
// It works only on errors whose message is prefixed by colon,
|
||
|
// followed by a space (": "). For example:
|
||
|
//
|
||
|
// attributes.go:13:1: expected 'package', found 'type'
|
||
|
//
|
||
|
func parseGoListError(input string) span.Span {
|
||
|
input = strings.TrimSpace(input)
|
||
|
msgIndex := strings.Index(input, ": ")
|
||
|
if msgIndex < 0 {
|
||
|
return span.Parse(input)
|
||
|
}
|
||
|
return span.Parse(input[:msgIndex])
|
||
|
}
|