diff options
Diffstat (limited to 'internal/lsp/cache/errors.go')
-rw-r--r-- | internal/lsp/cache/errors.go | 411 |
1 files changed, 0 insertions, 411 deletions
diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go deleted file mode 100644 index e9a86de35..000000000 --- a/internal/lsp/cache/errors.go +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright 2019 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 cache - -import ( - "fmt" - "go/scanner" - "go/token" - "go/types" - "regexp" - "strconv" - "strings" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/packages" - "golang.org/x/tools/internal/analysisinternal" - "golang.org/x/tools/internal/lsp/command" - "golang.org/x/tools/internal/lsp/protocol" - "golang.org/x/tools/internal/lsp/source" - "golang.org/x/tools/internal/span" - "golang.org/x/tools/internal/typesinternal" - errors "golang.org/x/xerrors" -) - -func goPackagesErrorDiagnostics(snapshot *snapshot, pkg *pkg, e packages.Error) ([]*source.Diagnostic, error) { - if msg, spn, ok := parseGoListImportCycleError(snapshot, e, pkg); ok { - rng, err := spanToRange(pkg, spn) - if err != nil { - return nil, err - } - return []*source.Diagnostic{{ - URI: spn.URI(), - Range: rng, - Severity: protocol.SeverityError, - Source: source.ListError, - Message: msg, - }}, nil - } - - var spn span.Span - if e.Pos == "" { - spn = parseGoListError(e.Msg, pkg.m.Config.Dir) - // We may not have been able to parse a valid span. Apply the errors to all files. - if _, err := spanToRange(pkg, spn); err != nil { - var diags []*source.Diagnostic - for _, cgf := range pkg.compiledGoFiles { - diags = append(diags, &source.Diagnostic{ - URI: cgf.URI, - Severity: protocol.SeverityError, - Source: source.ListError, - Message: e.Msg, - }) - } - return diags, nil - } - } else { - spn = span.ParseInDir(e.Pos, pkg.m.Config.Dir) - } - - rng, err := spanToRange(pkg, spn) - if err != nil { - return nil, err - } - return []*source.Diagnostic{{ - URI: spn.URI(), - Range: rng, - Severity: protocol.SeverityError, - Source: source.ListError, - Message: e.Msg, - }}, nil -} - -func parseErrorDiagnostics(snapshot *snapshot, pkg *pkg, errList scanner.ErrorList) ([]*source.Diagnostic, error) { - // The first parser error is likely the root cause of the problem. - if errList.Len() <= 0 { - return nil, errors.Errorf("no errors in %v", errList) - } - e := errList[0] - pgf, err := pkg.File(span.URIFromPath(e.Pos.Filename)) - if err != nil { - return nil, err - } - pos := pgf.Tok.Pos(e.Pos.Offset) - spn, err := span.NewRange(snapshot.FileSet(), pos, pos).Span() - if err != nil { - return nil, err - } - rng, err := spanToRange(pkg, spn) - if err != nil { - return nil, err - } - return []*source.Diagnostic{{ - URI: spn.URI(), - Range: rng, - Severity: protocol.SeverityError, - Source: source.ParseError, - Message: e.Msg, - }}, nil -} - -var importErrorRe = regexp.MustCompile(`could not import ([^\s]+)`) -var unsupportedFeatureRe = regexp.MustCompile(`.*require.* go(\d+\.\d+) or later`) - -func typeErrorDiagnostics(snapshot *snapshot, pkg *pkg, e extendedError) ([]*source.Diagnostic, error) { - code, spn, err := typeErrorData(snapshot.FileSet(), pkg, e.primary) - if err != nil { - return nil, err - } - rng, err := spanToRange(pkg, spn) - if err != nil { - return nil, err - } - diag := &source.Diagnostic{ - URI: spn.URI(), - Range: rng, - Severity: protocol.SeverityError, - Source: source.TypeError, - Message: e.primary.Msg, - } - if code != 0 { - diag.Code = code.String() - diag.CodeHref = typesCodeHref(snapshot, code) - } - - for _, secondary := range e.secondaries { - _, secondarySpan, err := typeErrorData(snapshot.FileSet(), pkg, secondary) - if err != nil { - return nil, err - } - rng, err := spanToRange(pkg, secondarySpan) - if err != nil { - return nil, err - } - diag.Related = append(diag.Related, source.RelatedInformation{ - URI: secondarySpan.URI(), - Range: rng, - Message: secondary.Msg, - }) - } - - if match := importErrorRe.FindStringSubmatch(e.primary.Msg); match != nil { - diag.SuggestedFixes, err = goGetQuickFixes(snapshot, spn.URI(), match[1]) - if err != nil { - return nil, err - } - } - if match := unsupportedFeatureRe.FindStringSubmatch(e.primary.Msg); match != nil { - diag.SuggestedFixes, err = editGoDirectiveQuickFix(snapshot, spn.URI(), match[1]) - if err != nil { - return nil, err - } - } - return []*source.Diagnostic{diag}, nil -} - -func goGetQuickFixes(snapshot *snapshot, uri span.URI, pkg string) ([]source.SuggestedFix, error) { - // Go get only supports module mode for now. - if snapshot.workspaceMode()&moduleMode == 0 { - return nil, nil - } - title := fmt.Sprintf("go get package %v", pkg) - cmd, err := command.NewGoGetPackageCommand(title, command.GoGetPackageArgs{ - URI: protocol.URIFromSpanURI(uri), - AddRequire: true, - Pkg: pkg, - }) - if err != nil { - return nil, err - } - return []source.SuggestedFix{source.SuggestedFixFromCommand(cmd, protocol.QuickFix)}, nil -} - -func editGoDirectiveQuickFix(snapshot *snapshot, uri span.URI, version string) ([]source.SuggestedFix, error) { - // Go mod edit only supports module mode. - if snapshot.workspaceMode()&moduleMode == 0 { - return nil, nil - } - title := fmt.Sprintf("go mod edit -go=%s", version) - cmd, err := command.NewEditGoDirectiveCommand(title, command.EditGoDirectiveArgs{ - URI: protocol.URIFromSpanURI(uri), - Version: version, - }) - if err != nil { - return nil, err - } - return []source.SuggestedFix{source.SuggestedFixFromCommand(cmd, protocol.QuickFix)}, nil -} - -func analysisDiagnosticDiagnostics(snapshot *snapshot, pkg *pkg, a *analysis.Analyzer, e *analysis.Diagnostic) ([]*source.Diagnostic, error) { - var srcAnalyzer *source.Analyzer - // Find the analyzer that generated this diagnostic. - for _, sa := range source.EnabledAnalyzers(snapshot) { - if a == sa.Analyzer { - srcAnalyzer = sa - break - } - } - - spn, err := span.NewRange(snapshot.FileSet(), e.Pos, e.End).Span() - if err != nil { - return nil, err - } - rng, err := spanToRange(pkg, spn) - if err != nil { - return nil, err - } - kinds := srcAnalyzer.ActionKind - if len(srcAnalyzer.ActionKind) == 0 { - kinds = append(kinds, protocol.QuickFix) - } - fixes, err := suggestedAnalysisFixes(snapshot, pkg, e, kinds) - if err != nil { - return nil, err - } - if srcAnalyzer.Fix != "" { - cmd, err := command.NewApplyFixCommand(e.Message, command.ApplyFixArgs{ - URI: protocol.URIFromSpanURI(spn.URI()), - Range: rng, - Fix: srcAnalyzer.Fix, - }) - if err != nil { - return nil, err - } - for _, kind := range kinds { - fixes = append(fixes, source.SuggestedFixFromCommand(cmd, kind)) - } - } - related, err := relatedInformation(pkg, snapshot.FileSet(), e) - if err != nil { - return nil, err - } - - severity := srcAnalyzer.Severity - if severity == 0 { - severity = protocol.SeverityWarning - } - diag := &source.Diagnostic{ - URI: spn.URI(), - Range: rng, - Severity: severity, - Source: source.AnalyzerErrorKind(e.Category), - Message: e.Message, - Related: related, - SuggestedFixes: fixes, - Analyzer: srcAnalyzer, - } - // If the fixes only delete code, assume that the diagnostic is reporting dead code. - if onlyDeletions(fixes) { - diag.Tags = []protocol.DiagnosticTag{protocol.Unnecessary} - } - return []*source.Diagnostic{diag}, nil -} - -// onlyDeletions returns true if all of the suggested fixes are deletions. -func onlyDeletions(fixes []source.SuggestedFix) bool { - for _, fix := range fixes { - if fix.Command != nil { - return false - } - for _, edits := range fix.Edits { - for _, edit := range edits { - if edit.NewText != "" { - return false - } - if protocol.ComparePosition(edit.Range.Start, edit.Range.End) == 0 { - return false - } - } - } - } - return len(fixes) > 0 -} - -func typesCodeHref(snapshot *snapshot, code typesinternal.ErrorCode) string { - target := snapshot.View().Options().LinkTarget - return source.BuildLink(target, "golang.org/x/tools/internal/typesinternal", code.String()) -} - -func suggestedAnalysisFixes(snapshot *snapshot, pkg *pkg, diag *analysis.Diagnostic, kinds []protocol.CodeActionKind) ([]source.SuggestedFix, error) { - var fixes []source.SuggestedFix - for _, fix := range diag.SuggestedFixes { - edits := make(map[span.URI][]protocol.TextEdit) - for _, e := range fix.TextEdits { - spn, err := span.NewRange(snapshot.FileSet(), e.Pos, e.End).Span() - if err != nil { - return nil, err - } - rng, err := spanToRange(pkg, spn) - if err != nil { - return nil, err - } - edits[spn.URI()] = append(edits[spn.URI()], protocol.TextEdit{ - Range: rng, - NewText: string(e.NewText), - }) - } - for _, kind := range kinds { - fixes = append(fixes, source.SuggestedFix{ - Title: fix.Message, - Edits: edits, - ActionKind: kind, - }) - } - - } - return fixes, nil -} - -func relatedInformation(pkg *pkg, fset *token.FileSet, diag *analysis.Diagnostic) ([]source.RelatedInformation, error) { - var out []source.RelatedInformation - for _, related := range diag.Related { - spn, err := span.NewRange(fset, related.Pos, related.End).Span() - if err != nil { - return nil, err - } - rng, err := spanToRange(pkg, spn) - if err != nil { - return nil, err - } - out = append(out, source.RelatedInformation{ - URI: spn.URI(), - Range: rng, - Message: related.Message, - }) - } - return out, nil -} - -func typeErrorData(fset *token.FileSet, pkg *pkg, terr types.Error) (typesinternal.ErrorCode, span.Span, error) { - ecode, start, end, ok := typesinternal.ReadGo116ErrorData(terr) - if !ok { - start, end = terr.Pos, terr.Pos - ecode = 0 - } - posn := fset.Position(start) - pgf, err := pkg.File(span.URIFromPath(posn.Filename)) - if err != nil { - return 0, span.Span{}, err - } - if !end.IsValid() || end == start { - end = analysisinternal.TypeErrorEndPos(fset, pgf.Src, start) - } - spn, err := parsedGoSpan(pgf, start, end) - if err != nil { - return 0, span.Span{}, err - } - return ecode, spn, nil -} - -func parsedGoSpan(pgf *source.ParsedGoFile, start, end token.Pos) (span.Span, error) { - return span.FileSpan(pgf.Tok, pgf.Mapper.Converter, start, end) -} - -// spanToRange converts a span.Span to a protocol.Range, -// assuming that the span belongs to the package whose diagnostics are being computed. -func spanToRange(pkg *pkg, spn span.Span) (protocol.Range, error) { - pgf, err := pkg.File(spn.URI()) - if err != nil { - return protocol.Range{}, err - } - return pgf.Mapper.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, wd string) span.Span { - input = strings.TrimSpace(input) - msgIndex := strings.Index(input, ": ") - if msgIndex < 0 { - return span.Parse(input) - } - return span.ParseInDir(input[:msgIndex], wd) -} - -func parseGoListImportCycleError(snapshot *snapshot, e packages.Error, pkg *pkg) (string, span.Span, bool) { - re := regexp.MustCompile(`(.*): import stack: \[(.+)\]`) - matches := re.FindStringSubmatch(strings.TrimSpace(e.Msg)) - if len(matches) < 3 { - return e.Msg, span.Span{}, false - } - msg := matches[1] - importList := strings.Split(matches[2], " ") - // Since the error is relative to the current package. The import that is causing - // the import cycle error is the second one in the list. - if len(importList) < 2 { - return msg, span.Span{}, false - } - // Imports have quotation marks around them. - circImp := strconv.Quote(importList[1]) - for _, cgf := range pkg.compiledGoFiles { - // Search file imports for the import that is causing the import cycle. - for _, imp := range cgf.File.Imports { - if imp.Path.Value == circImp { - spn, err := span.NewRange(snapshot.FileSet(), imp.Pos(), imp.End()).Span() - if err != nil { - return msg, span.Span{}, false - } - return msg, spn, true - } - } - } - return msg, span.Span{}, false -} |