cuelang.org/go@v0.10.1/internal/golangorgx/gopls/cache/diagnostics.go (about) 1 // Copyright 2023 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 "encoding/json" 9 "fmt" 10 11 "cuelang.org/go/internal/golangorgx/gopls/protocol" 12 "cuelang.org/go/internal/golangorgx/gopls/util/bug" 13 ) 14 15 // A InitializationError is an error that causes snapshot initialization to fail. 16 // It is either the error returned from go/packages.Load, or an error parsing a 17 // workspace go.work or go.mod file. 18 // 19 // Such an error generally indicates that the View is malformed, and will never 20 // be usable. 21 type InitializationError struct { 22 // MainError is the primary error. Must be non-nil. 23 MainError error 24 25 // Diagnostics contains any supplemental (structured) diagnostics extracted 26 // from the load error. 27 Diagnostics map[protocol.DocumentURI][]*Diagnostic 28 } 29 30 func byURI(d *Diagnostic) protocol.DocumentURI { return d.URI } // For use in maps.Group. 31 32 // An Diagnostic corresponds to an LSP Diagnostic. 33 // https://microsoft.github.io/language-server-protocol/specification#diagnostic 34 // 35 // It is (effectively) gob-serializable; see {encode,decode}Diagnostics. 36 type Diagnostic struct { 37 URI protocol.DocumentURI // of diagnosed file (not diagnostic documentation) 38 Range protocol.Range 39 Severity protocol.DiagnosticSeverity 40 Code string // analysis.Diagnostic.Category (or "default" if empty) or hidden go/types error code 41 CodeHref string 42 43 // Source is a human-readable description of the source of the error. 44 // Diagnostics generated by an analysis.Analyzer set it to Analyzer.Name. 45 Source DiagnosticSource 46 47 Message string 48 49 Tags []protocol.DiagnosticTag 50 Related []protocol.DiagnosticRelatedInformation 51 52 // Fields below are used internally to generate quick fixes. They aren't 53 // part of the LSP spec and historically didn't leave the server. 54 // 55 // Update(2023-05): version 3.16 of the LSP spec included support for the 56 // Diagnostic.data field, which holds arbitrary data preserved in the 57 // diagnostic for codeAction requests. This field allows bundling additional 58 // information for quick-fixes, and gopls can (and should) use this 59 // information to avoid re-evaluating diagnostics in code-action handlers. 60 // 61 // In order to stage this transition incrementally, the 'BundledFixes' field 62 // may store a 'bundled' (=json-serialized) form of the associated 63 // SuggestedFixes. Not all diagnostics have their fixes bundled. 64 BundledFixes *json.RawMessage 65 SuggestedFixes []SuggestedFix 66 } 67 68 func (d *Diagnostic) String() string { 69 return fmt.Sprintf("%v: %s", d.Range, d.Message) 70 } 71 72 type DiagnosticSource string 73 74 const ( 75 UnknownError DiagnosticSource = "<Unknown source>" 76 ListError DiagnosticSource = "go list" 77 ParseError DiagnosticSource = "syntax" 78 TypeError DiagnosticSource = "compiler" 79 ModTidyError DiagnosticSource = "go mod tidy" 80 OptimizationDetailsError DiagnosticSource = "optimizer details" 81 UpgradeNotification DiagnosticSource = "upgrade available" 82 Vulncheck DiagnosticSource = "vulncheck imports" 83 Govulncheck DiagnosticSource = "govulncheck" 84 TemplateError DiagnosticSource = "template" 85 WorkFileError DiagnosticSource = "go.work file" 86 ConsistencyInfo DiagnosticSource = "consistency" 87 ) 88 89 // A SuggestedFix represents a suggested fix (for a diagnostic) 90 // produced by analysis, in protocol form. 91 // 92 // The fixes are reported to the client as a set of code actions in 93 // response to a CodeAction query for a set of diagnostics. Multiple 94 // SuggestedFixes may be produced for the same logical fix, varying 95 // only in ActionKind. For example, a fix may be both a Refactor 96 // (which should appear on the refactoring menu) and a SourceFixAll (a 97 // clear fix that can be safely applied without explicit consent). 98 type SuggestedFix struct { 99 Title string 100 Edits map[protocol.DocumentURI][]protocol.TextEdit 101 Command *protocol.Command 102 ActionKind protocol.CodeActionKind 103 } 104 105 // SuggestedFixFromCommand returns a suggested fix to run the given command. 106 func SuggestedFixFromCommand(cmd protocol.Command, kind protocol.CodeActionKind) SuggestedFix { 107 return SuggestedFix{ 108 Title: cmd.Title, 109 Command: &cmd, 110 ActionKind: kind, 111 } 112 } 113 114 // quickFixesJSON is a JSON-serializable list of quick fixes 115 // to be saved in the protocol.Diagnostic.Data field. 116 type quickFixesJSON struct { 117 // TODO(rfindley): pack some sort of identifier here for later 118 // lookup/validation? 119 Fixes []protocol.CodeAction 120 } 121 122 // bundleQuickFixes attempts to bundle sd.SuggestedFixes into the 123 // sd.BundledFixes field, so that it can be round-tripped through the client. 124 // It returns false if the quick-fixes cannot be bundled. 125 func bundleQuickFixes(sd *Diagnostic) bool { 126 if len(sd.SuggestedFixes) == 0 { 127 return true 128 } 129 var actions []protocol.CodeAction 130 for _, fix := range sd.SuggestedFixes { 131 if fix.Edits != nil { 132 // For now, we only support bundled code actions that execute commands. 133 // 134 // In order to cleanly support bundled edits, we'd have to guarantee that 135 // the edits were generated on the current snapshot. But this naively 136 // implies that every fix would have to include a snapshot ID, which 137 // would require us to republish all diagnostics on each new snapshot. 138 // 139 // TODO(rfindley): in order to avoid this additional chatter, we'd need 140 // to build some sort of registry or other mechanism on the snapshot to 141 // check whether a diagnostic is still valid. 142 return false 143 } 144 action := protocol.CodeAction{ 145 Title: fix.Title, 146 Kind: fix.ActionKind, 147 Command: fix.Command, 148 } 149 actions = append(actions, action) 150 } 151 fixes := quickFixesJSON{ 152 Fixes: actions, 153 } 154 data, err := json.Marshal(fixes) 155 if err != nil { 156 bug.Reportf("marshalling quick fixes: %v", err) 157 return false 158 } 159 msg := json.RawMessage(data) 160 sd.BundledFixes = &msg 161 return true 162 } 163 164 // BundledQuickFixes extracts any bundled codeActions from the 165 // diag.Data field. 166 func BundledQuickFixes(diag protocol.Diagnostic) []protocol.CodeAction { 167 var fix quickFixesJSON 168 if diag.Data != nil { 169 err := protocol.UnmarshalJSON(*diag.Data, &fix) 170 if err != nil { 171 bug.Reportf("unmarshalling quick fix: %v", err) 172 return nil 173 } 174 } 175 176 var actions []protocol.CodeAction 177 for _, action := range fix.Fixes { 178 // See BundleQuickFixes: for now we only support bundling commands. 179 if action.Edit != nil { 180 bug.Reportf("bundled fix %q includes workspace edits", action.Title) 181 continue 182 } 183 // associate the action with the incoming diagnostic 184 // (Note that this does not mutate the fix.Fixes slice). 185 action.Diagnostics = []protocol.Diagnostic{diag} 186 actions = append(actions, action) 187 } 188 189 return actions 190 }