github.com/v2fly/tools@v0.100.0/internal/lsp/mod/diagnostics.go (about) 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 mod provides core features related to go.mod file 6 // handling for use by Go editors and tools. 7 package mod 8 9 import ( 10 "context" 11 "fmt" 12 13 "github.com/v2fly/tools/internal/event" 14 "github.com/v2fly/tools/internal/lsp/command" 15 "github.com/v2fly/tools/internal/lsp/debug/tag" 16 "github.com/v2fly/tools/internal/lsp/protocol" 17 "github.com/v2fly/tools/internal/lsp/source" 18 ) 19 20 func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.VersionedFileIdentity][]*source.Diagnostic, error) { 21 ctx, done := event.Start(ctx, "mod.Diagnostics", tag.Snapshot.Of(snapshot.ID())) 22 defer done() 23 24 reports := map[source.VersionedFileIdentity][]*source.Diagnostic{} 25 for _, uri := range snapshot.ModFiles() { 26 fh, err := snapshot.GetVersionedFile(ctx, uri) 27 if err != nil { 28 return nil, err 29 } 30 reports[fh.VersionedFileIdentity()] = []*source.Diagnostic{} 31 diagnostics, err := DiagnosticsForMod(ctx, snapshot, fh) 32 if err != nil { 33 return nil, err 34 } 35 for _, d := range diagnostics { 36 fh, err := snapshot.GetVersionedFile(ctx, d.URI) 37 if err != nil { 38 return nil, err 39 } 40 reports[fh.VersionedFileIdentity()] = append(reports[fh.VersionedFileIdentity()], d) 41 } 42 } 43 return reports, nil 44 } 45 46 func DiagnosticsForMod(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]*source.Diagnostic, error) { 47 pm, err := snapshot.ParseMod(ctx, fh) 48 if err != nil { 49 if pm == nil || len(pm.ParseErrors) == 0 { 50 return nil, err 51 } 52 return pm.ParseErrors, nil 53 } 54 55 var diagnostics []*source.Diagnostic 56 57 // Add upgrade quick fixes for individual modules if we know about them. 58 upgrades := snapshot.View().ModuleUpgrades() 59 for _, req := range pm.File.Require { 60 ver, ok := upgrades[req.Mod.Path] 61 if !ok || req.Mod.Version == ver { 62 continue 63 } 64 rng, err := lineToRange(pm.Mapper, fh.URI(), req.Syntax.Start, req.Syntax.End) 65 if err != nil { 66 return nil, err 67 } 68 // Upgrade to the exact version we offer the user, not the most recent. 69 title := fmt.Sprintf("Upgrade to %v", ver) 70 cmd, err := command.NewUpgradeDependencyCommand(title, command.DependencyArgs{ 71 URI: protocol.URIFromSpanURI(fh.URI()), 72 AddRequire: false, 73 GoCmdArgs: []string{req.Mod.Path + "@" + ver}, 74 }) 75 if err != nil { 76 return nil, err 77 } 78 diagnostics = append(diagnostics, &source.Diagnostic{ 79 URI: fh.URI(), 80 Range: rng, 81 Severity: protocol.SeverityInformation, 82 Source: source.UpgradeNotification, 83 Message: fmt.Sprintf("%v can be upgraded", req.Mod.Path), 84 SuggestedFixes: []source.SuggestedFix{source.SuggestedFixFromCommand(cmd, protocol.QuickFix)}, 85 }) 86 } 87 88 // Packages in the workspace can contribute diagnostics to go.mod files. 89 wspkgs, err := snapshot.WorkspacePackages(ctx) 90 if err != nil && !source.IsNonFatalGoModError(err) { 91 event.Error(ctx, "diagnosing go.mod", err) 92 } 93 if err == nil { 94 for _, pkg := range wspkgs { 95 pkgDiagnostics, err := snapshot.DiagnosePackage(ctx, pkg) 96 if err != nil { 97 return nil, err 98 } 99 diagnostics = append(diagnostics, pkgDiagnostics[fh.URI()]...) 100 } 101 } 102 103 tidied, err := snapshot.ModTidy(ctx, pm) 104 if err != nil && !source.IsNonFatalGoModError(err) { 105 event.Error(ctx, "diagnosing go.mod", err) 106 } 107 if err == nil { 108 for _, d := range tidied.Diagnostics { 109 if d.URI != fh.URI() { 110 continue 111 } 112 diagnostics = append(diagnostics, d) 113 } 114 } 115 return diagnostics, nil 116 }