From 9d96a6d7af0bf8f6c2e42c4c20237ed0440f0494 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sun, 6 Dec 2020 01:24:37 +0100 Subject: Emit additional diagnostics for hints/help/etc --- crates/rust-analyzer/src/diagnostics/to_proto.rs | 189 ++++++++++++++--------- 1 file changed, 116 insertions(+), 73 deletions(-) (limited to 'crates') 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( } enum MappedRustChildDiagnostic { - Related(lsp_types::DiagnosticRelatedInformation), - SuggestedFix(lsp_ext::CodeAction), + Related { + related: lsp_types::DiagnosticRelatedInformation, + suggested_fix: Option, + }, MessageLine(String), } @@ -103,23 +105,32 @@ fn map_rust_child_diagnostic( } if edit_map.is_empty() { - MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation { - location: location(workspace_root, spans[0]), - message: rd.message.clone(), - }) + MappedRustChildDiagnostic::Related { + related: lsp_types::DiagnosticRelatedInformation { + location: location(workspace_root, spans[0]), + message: rd.message.clone(), + }, + suggested_fix: None, + } } else { - MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { - title: rd.message.clone(), - group: None, - kind: Some(lsp_types::CodeActionKind::QUICKFIX), - edit: Some(lsp_ext::SnippetWorkspaceEdit { - // FIXME: there's no good reason to use edit_map here.... - changes: Some(edit_map), - document_changes: None, + MappedRustChildDiagnostic::Related { + related: lsp_types::DiagnosticRelatedInformation { + location: location(workspace_root, spans[0]), + message: rd.message.clone(), + }, + suggested_fix: Some(lsp_ext::CodeAction { + title: rd.message.clone(), + group: None, + kind: Some(lsp_types::CodeActionKind::QUICKFIX), + edit: Some(lsp_ext::SnippetWorkspaceEdit { + // FIXME: there's no good reason to use edit_map here.... + changes: Some(edit_map), + document_changes: None, + }), + is_preferred: Some(true), + data: None, }), - is_preferred: Some(true), - data: None, - }) + } } } @@ -179,8 +190,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp( for child in &rd.children { let child = map_rust_child_diagnostic(workspace_root, &child); match child { - MappedRustChildDiagnostic::Related(related) => related_information.push(related), - MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), + MappedRustChildDiagnostic::Related { related, suggested_fix } => { + related_information.push(related); + if let Some(code_action) = suggested_fix { + fixes.push(code_action); + } + } MappedRustChildDiagnostic::MessageLine(message_line) => { format_to!(message, "\n{}", message_line); @@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( primary_spans .iter() - .map(|primary_span| { + .flat_map(|primary_span| { let location = location(workspace_root, &primary_span); let mut message = message.clone(); @@ -229,72 +244,100 @@ pub(crate) fn map_rust_diagnostic_to_lsp( } } + // Each primary diagnostic span may result in multiple LSP diagnostics. + let mut diagnostics = Vec::new(); + + let mut related_macro_info = None; + // If error occurs from macro expansion, add related info pointing to // where the error originated // Also, we would generate an additional diagnostic, so that exact place of macro // will be highlighted in the error origin place. - let additional_diagnostic = - if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { - let in_macro_location = location_naive(workspace_root, &primary_span); + if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { + let in_macro_location = location_naive(workspace_root, &primary_span); - // Add related information for the main disagnostic. - related_information.push(lsp_types::DiagnosticRelatedInformation { - location: in_macro_location.clone(), - message: "Error originated from macro here".to_string(), - }); + // Add related information for the main disagnostic. + related_macro_info = Some(lsp_types::DiagnosticRelatedInformation { + location: in_macro_location.clone(), + message: "Error originated from macro here".to_string(), + }); - // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. - let information_for_additional_diagnostic = - vec![lsp_types::DiagnosticRelatedInformation { - location: location.clone(), - message: "Exact error occured here".to_string(), - }]; + // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. + let information_for_additional_diagnostic = + vec![lsp_types::DiagnosticRelatedInformation { + location: location.clone(), + message: "Exact error occured here".to_string(), + }]; - let diagnostic = lsp_types::Diagnostic { - range: in_macro_location.range, - severity, - code: code.clone().map(lsp_types::NumberOrString::String), - code_description: code_description.clone(), - source: Some(source.clone()), - message: message.clone(), - related_information: Some(information_for_additional_diagnostic), - tags: if tags.is_empty() { None } else { Some(tags.clone()) }, - data: None, - }; - - Some(MappedRustDiagnostic { - url: in_macro_location.uri, - diagnostic, - fixes: fixes.clone(), - }) - } else { - None + let diagnostic = lsp_types::Diagnostic { + range: in_macro_location.range, + severity, + code: code.clone().map(lsp_types::NumberOrString::String), + code_description: code_description.clone(), + source: Some(source.clone()), + message: message.clone(), + related_information: Some(information_for_additional_diagnostic), + tags: if tags.is_empty() { None } else { Some(tags.clone()) }, + data: None, }; - let diagnostic = lsp_types::Diagnostic { - range: location.range, - severity, - code: code.clone().map(lsp_types::NumberOrString::String), - code_description: code_description.clone(), - source: Some(source.clone()), - message, - related_information: if related_information.is_empty() { - None - } else { - Some(related_information.clone()) + diagnostics.push(MappedRustDiagnostic { + url: in_macro_location.uri, + diagnostic, + fixes: fixes.clone(), + }); + } + + // Emit the primary diagnostic. + diagnostics.push(MappedRustDiagnostic { + url: location.uri.clone(), + diagnostic: lsp_types::Diagnostic { + range: location.range, + severity, + code: code.clone().map(lsp_types::NumberOrString::String), + code_description: code_description.clone(), + source: Some(source.clone()), + message, + related_information: if related_information.is_empty() { + None + } else { + let mut related = related_information.clone(); + related.extend(related_macro_info); + Some(related) + }, + tags: if tags.is_empty() { None } else { Some(tags.clone()) }, + data: None, }, - tags: if tags.is_empty() { None } else { Some(tags.clone()) }, - data: None, - }; + fixes: fixes.clone(), + }); - let main_diagnostic = - MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }; - match additional_diagnostic { - None => vec![main_diagnostic], - Some(additional_diagnostic) => vec![main_diagnostic, additional_diagnostic], + // Emit hint-level diagnostics for all `related_information` entries such as "help"s. + // This is useful because they will show up in the user's editor, unlike + // `related_information`, which just produces hard-to-read links, at least in VS Code. + let back_ref = lsp_types::DiagnosticRelatedInformation { + location, + message: "original diagnostic".to_string(), + }; + for info in &related_information { + diagnostics.push(MappedRustDiagnostic { + url: info.location.uri.clone(), + fixes: fixes.clone(), // share fixes to make them easier to apply + diagnostic: lsp_types::Diagnostic { + range: info.location.range, + severity: Some(lsp_types::DiagnosticSeverity::Hint), + code: code.clone().map(lsp_types::NumberOrString::String), + code_description: code_description.clone(), + source: Some(source.clone()), + message: info.message.clone(), + related_information: Some(vec![back_ref.clone()]), + tags: None, // don't apply modifiers again + data: None, + }, + }); } + + diagnostics }) - .flatten() .collect() } -- cgit v1.2.3