}
enum MappedRustChildDiagnostic {
- Related(lsp_types::DiagnosticRelatedInformation),
- SuggestedFix(lsp_ext::CodeAction),
+ Related {
+ related: lsp_types::DiagnosticRelatedInformation,
+ suggested_fix: Option<lsp_ext::CodeAction>,
+ },
MessageLine(String),
}
}
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,
- })
+ }
}
}
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);
primary_spans
.iter()
- .map(|primary_span| {
+ .flat_map(|primary_span| {
let location = location(workspace_root, &primary_span);
let mut message = message.clone();
}
}
+ // 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()
}