aboutsummaryrefslogtreecommitdiff
path: root/crates/rust-analyzer/src/diagnostics
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2020-12-06 00:24:37 +0000
committerJonas Schievink <[email protected]>2020-12-06 00:24:37 +0000
commit9d96a6d7af0bf8f6c2e42c4c20237ed0440f0494 (patch)
treefbac86a3fe790d448a3ea763c2d6cf0027843b6c /crates/rust-analyzer/src/diagnostics
parent8d5aa08712e782f22f04525f291ec74dae183568 (diff)
Emit additional diagnostics for hints/help/etc
Diffstat (limited to 'crates/rust-analyzer/src/diagnostics')
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs189
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
77enum MappedRustChildDiagnostic { 77enum 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