diff options
author | Jonas Schievink <[email protected]> | 2020-12-06 00:24:37 +0000 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2020-12-06 00:24:37 +0000 |
commit | 9d96a6d7af0bf8f6c2e42c4c20237ed0440f0494 (patch) | |
tree | fbac86a3fe790d448a3ea763c2d6cf0027843b6c /crates | |
parent | 8d5aa08712e782f22f04525f291ec74dae183568 (diff) |
Emit additional diagnostics for hints/help/etc
Diffstat (limited to 'crates')
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/to_proto.rs | 189 |
1 files changed, 116 insertions, 73 deletions
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 324019614..766c342f1 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -75,8 +75,10 @@ fn diagnostic_related_information( | |||
75 | } | 75 | } |
76 | 76 | ||
77 | enum MappedRustChildDiagnostic { | 77 | enum MappedRustChildDiagnostic { |
78 | Related(lsp_types::DiagnosticRelatedInformation), | 78 | Related { |
79 | SuggestedFix(lsp_ext::CodeAction), | 79 | related: lsp_types::DiagnosticRelatedInformation, |
80 | suggested_fix: Option<lsp_ext::CodeAction>, | ||
81 | }, | ||
80 | MessageLine(String), | 82 | MessageLine(String), |
81 | } | 83 | } |
82 | 84 | ||
@@ -103,23 +105,32 @@ fn map_rust_child_diagnostic( | |||
103 | } | 105 | } |
104 | 106 | ||
105 | if edit_map.is_empty() { | 107 | if edit_map.is_empty() { |
106 | MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation { | 108 | MappedRustChildDiagnostic::Related { |
107 | location: location(workspace_root, spans[0]), | 109 | related: lsp_types::DiagnosticRelatedInformation { |
108 | message: rd.message.clone(), | 110 | location: location(workspace_root, spans[0]), |
109 | }) | 111 | message: rd.message.clone(), |
112 | }, | ||
113 | suggested_fix: None, | ||
114 | } | ||
110 | } else { | 115 | } else { |
111 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { | 116 | MappedRustChildDiagnostic::Related { |
112 | title: rd.message.clone(), | 117 | related: lsp_types::DiagnosticRelatedInformation { |
113 | group: None, | 118 | location: location(workspace_root, spans[0]), |
114 | kind: Some(lsp_types::CodeActionKind::QUICKFIX), | 119 | message: rd.message.clone(), |
115 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | 120 | }, |
116 | // FIXME: there's no good reason to use edit_map here.... | 121 | suggested_fix: Some(lsp_ext::CodeAction { |
117 | changes: Some(edit_map), | 122 | title: rd.message.clone(), |
118 | document_changes: None, | 123 | group: None, |
124 | kind: Some(lsp_types::CodeActionKind::QUICKFIX), | ||
125 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | ||
126 | // FIXME: there's no good reason to use edit_map here.... | ||
127 | changes: Some(edit_map), | ||
128 | document_changes: None, | ||
129 | }), | ||
130 | is_preferred: Some(true), | ||
131 | data: None, | ||
119 | }), | 132 | }), |
120 | is_preferred: Some(true), | 133 | } |
121 | data: None, | ||
122 | }) | ||
123 | } | 134 | } |
124 | } | 135 | } |
125 | 136 | ||
@@ -179,8 +190,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
179 | for child in &rd.children { | 190 | for child in &rd.children { |
180 | let child = map_rust_child_diagnostic(workspace_root, &child); | 191 | let child = map_rust_child_diagnostic(workspace_root, &child); |
181 | match child { | 192 | match child { |
182 | MappedRustChildDiagnostic::Related(related) => related_information.push(related), | 193 | MappedRustChildDiagnostic::Related { related, suggested_fix } => { |
183 | MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), | 194 | related_information.push(related); |
195 | if let Some(code_action) = suggested_fix { | ||
196 | fixes.push(code_action); | ||
197 | } | ||
198 | } | ||
184 | MappedRustChildDiagnostic::MessageLine(message_line) => { | 199 | MappedRustChildDiagnostic::MessageLine(message_line) => { |
185 | format_to!(message, "\n{}", message_line); | 200 | format_to!(message, "\n{}", message_line); |
186 | 201 | ||
@@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
219 | 234 | ||
220 | primary_spans | 235 | primary_spans |
221 | .iter() | 236 | .iter() |
222 | .map(|primary_span| { | 237 | .flat_map(|primary_span| { |
223 | let location = location(workspace_root, &primary_span); | 238 | let location = location(workspace_root, &primary_span); |
224 | 239 | ||
225 | let mut message = message.clone(); | 240 | let mut message = message.clone(); |
@@ -229,72 +244,100 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
229 | } | 244 | } |
230 | } | 245 | } |
231 | 246 | ||
247 | // Each primary diagnostic span may result in multiple LSP diagnostics. | ||
248 | let mut diagnostics = Vec::new(); | ||
249 | |||
250 | let mut related_macro_info = None; | ||
251 | |||
232 | // If error occurs from macro expansion, add related info pointing to | 252 | // If error occurs from macro expansion, add related info pointing to |
233 | // where the error originated | 253 | // where the error originated |
234 | // Also, we would generate an additional diagnostic, so that exact place of macro | 254 | // Also, we would generate an additional diagnostic, so that exact place of macro |
235 | // will be highlighted in the error origin place. | 255 | // will be highlighted in the error origin place. |
236 | let additional_diagnostic = | 256 | if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { |
237 | if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { | 257 | let in_macro_location = location_naive(workspace_root, &primary_span); |
238 | let in_macro_location = location_naive(workspace_root, &primary_span); | ||
239 | 258 | ||
240 | // Add related information for the main disagnostic. | 259 | // Add related information for the main disagnostic. |
241 | related_information.push(lsp_types::DiagnosticRelatedInformation { | 260 | related_macro_info = Some(lsp_types::DiagnosticRelatedInformation { |
242 | location: in_macro_location.clone(), | 261 | location: in_macro_location.clone(), |
243 | message: "Error originated from macro here".to_string(), | 262 | message: "Error originated from macro here".to_string(), |
244 | }); | 263 | }); |
245 | 264 | ||
246 | // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. | 265 | // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. |
247 | let information_for_additional_diagnostic = | 266 | let information_for_additional_diagnostic = |
248 | vec![lsp_types::DiagnosticRelatedInformation { | 267 | vec![lsp_types::DiagnosticRelatedInformation { |
249 | location: location.clone(), | 268 | location: location.clone(), |
250 | message: "Exact error occured here".to_string(), | 269 | message: "Exact error occured here".to_string(), |
251 | }]; | 270 | }]; |
252 | 271 | ||
253 | let diagnostic = lsp_types::Diagnostic { | 272 | let diagnostic = lsp_types::Diagnostic { |
254 | range: in_macro_location.range, | 273 | range: in_macro_location.range, |
255 | severity, | 274 | severity, |
256 | code: code.clone().map(lsp_types::NumberOrString::String), | 275 | code: code.clone().map(lsp_types::NumberOrString::String), |
257 | code_description: code_description.clone(), | 276 | code_description: code_description.clone(), |
258 | source: Some(source.clone()), | 277 | source: Some(source.clone()), |
259 | message: message.clone(), | 278 | message: message.clone(), |
260 | related_information: Some(information_for_additional_diagnostic), | 279 | related_information: Some(information_for_additional_diagnostic), |
261 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | 280 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, |
262 | data: None, | 281 | data: None, |
263 | }; | ||
264 | |||
265 | Some(MappedRustDiagnostic { | ||
266 | url: in_macro_location.uri, | ||
267 | diagnostic, | ||
268 | fixes: fixes.clone(), | ||
269 | }) | ||
270 | } else { | ||
271 | None | ||
272 | }; | 282 | }; |
273 | 283 | ||
274 | let diagnostic = lsp_types::Diagnostic { | 284 | diagnostics.push(MappedRustDiagnostic { |
275 | range: location.range, | 285 | url: in_macro_location.uri, |
276 | severity, | 286 | diagnostic, |
277 | code: code.clone().map(lsp_types::NumberOrString::String), | 287 | fixes: fixes.clone(), |
278 | code_description: code_description.clone(), | 288 | }); |
279 | source: Some(source.clone()), | 289 | } |
280 | message, | 290 | |
281 | related_information: if related_information.is_empty() { | 291 | // Emit the primary diagnostic. |
282 | None | 292 | diagnostics.push(MappedRustDiagnostic { |
283 | } else { | 293 | url: location.uri.clone(), |
284 | Some(related_information.clone()) | 294 | diagnostic: lsp_types::Diagnostic { |
295 | range: location.range, | ||
296 | severity, | ||
297 | code: code.clone().map(lsp_types::NumberOrString::String), | ||
298 | code_description: code_description.clone(), | ||
299 | source: Some(source.clone()), | ||
300 | message, | ||
301 | related_information: if related_information.is_empty() { | ||
302 | None | ||
303 | } else { | ||
304 | let mut related = related_information.clone(); | ||
305 | related.extend(related_macro_info); | ||
306 | Some(related) | ||
307 | }, | ||
308 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | ||
309 | data: None, | ||
285 | }, | 310 | }, |
286 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | 311 | fixes: fixes.clone(), |
287 | data: None, | 312 | }); |
288 | }; | ||
289 | 313 | ||
290 | let main_diagnostic = | 314 | // Emit hint-level diagnostics for all `related_information` entries such as "help"s. |
291 | MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }; | 315 | // This is useful because they will show up in the user's editor, unlike |
292 | match additional_diagnostic { | 316 | // `related_information`, which just produces hard-to-read links, at least in VS Code. |
293 | None => vec![main_diagnostic], | 317 | let back_ref = lsp_types::DiagnosticRelatedInformation { |
294 | Some(additional_diagnostic) => vec![main_diagnostic, additional_diagnostic], | 318 | location, |
319 | message: "original diagnostic".to_string(), | ||
320 | }; | ||
321 | for info in &related_information { | ||
322 | diagnostics.push(MappedRustDiagnostic { | ||
323 | url: info.location.uri.clone(), | ||
324 | fixes: fixes.clone(), // share fixes to make them easier to apply | ||
325 | diagnostic: lsp_types::Diagnostic { | ||
326 | range: info.location.range, | ||
327 | severity: Some(lsp_types::DiagnosticSeverity::Hint), | ||
328 | code: code.clone().map(lsp_types::NumberOrString::String), | ||
329 | code_description: code_description.clone(), | ||
330 | source: Some(source.clone()), | ||
331 | message: info.message.clone(), | ||
332 | related_information: Some(vec![back_ref.clone()]), | ||
333 | tags: None, // don't apply modifiers again | ||
334 | data: None, | ||
335 | }, | ||
336 | }); | ||
295 | } | 337 | } |
338 | |||
339 | diagnostics | ||
296 | }) | 340 | }) |
297 | .flatten() | ||
298 | .collect() | 341 | .collect() |
299 | } | 342 | } |
300 | 343 | ||