mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Emit additional diagnostics for hints/help/etc
This commit is contained in:
parent
8d5aa08712
commit
9d96a6d7af
1 changed files with 116 additions and 73 deletions
|
@ -75,8 +75,10 @@ fn diagnostic_related_information(
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MappedRustChildDiagnostic {
|
enum MappedRustChildDiagnostic {
|
||||||
Related(lsp_types::DiagnosticRelatedInformation),
|
Related {
|
||||||
SuggestedFix(lsp_ext::CodeAction),
|
related: lsp_types::DiagnosticRelatedInformation,
|
||||||
|
suggested_fix: Option<lsp_ext::CodeAction>,
|
||||||
|
},
|
||||||
MessageLine(String),
|
MessageLine(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,23 +105,32 @@ fn map_rust_child_diagnostic(
|
||||||
}
|
}
|
||||||
|
|
||||||
if edit_map.is_empty() {
|
if edit_map.is_empty() {
|
||||||
MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
|
MappedRustChildDiagnostic::Related {
|
||||||
location: location(workspace_root, spans[0]),
|
related: lsp_types::DiagnosticRelatedInformation {
|
||||||
message: rd.message.clone(),
|
location: location(workspace_root, spans[0]),
|
||||||
})
|
message: rd.message.clone(),
|
||||||
|
},
|
||||||
|
suggested_fix: None,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
|
MappedRustChildDiagnostic::Related {
|
||||||
title: rd.message.clone(),
|
related: lsp_types::DiagnosticRelatedInformation {
|
||||||
group: None,
|
location: location(workspace_root, spans[0]),
|
||||||
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
|
message: rd.message.clone(),
|
||||||
edit: Some(lsp_ext::SnippetWorkspaceEdit {
|
},
|
||||||
// FIXME: there's no good reason to use edit_map here....
|
suggested_fix: Some(lsp_ext::CodeAction {
|
||||||
changes: Some(edit_map),
|
title: rd.message.clone(),
|
||||||
document_changes: None,
|
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 {
|
for child in &rd.children {
|
||||||
let child = map_rust_child_diagnostic(workspace_root, &child);
|
let child = map_rust_child_diagnostic(workspace_root, &child);
|
||||||
match child {
|
match child {
|
||||||
MappedRustChildDiagnostic::Related(related) => related_information.push(related),
|
MappedRustChildDiagnostic::Related { related, suggested_fix } => {
|
||||||
MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
|
related_information.push(related);
|
||||||
|
if let Some(code_action) = suggested_fix {
|
||||||
|
fixes.push(code_action);
|
||||||
|
}
|
||||||
|
}
|
||||||
MappedRustChildDiagnostic::MessageLine(message_line) => {
|
MappedRustChildDiagnostic::MessageLine(message_line) => {
|
||||||
format_to!(message, "\n{}", message_line);
|
format_to!(message, "\n{}", message_line);
|
||||||
|
|
||||||
|
@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
|
|
||||||
primary_spans
|
primary_spans
|
||||||
.iter()
|
.iter()
|
||||||
.map(|primary_span| {
|
.flat_map(|primary_span| {
|
||||||
let location = location(workspace_root, &primary_span);
|
let location = location(workspace_root, &primary_span);
|
||||||
|
|
||||||
let mut message = message.clone();
|
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
|
// If error occurs from macro expansion, add related info pointing to
|
||||||
// where the error originated
|
// where the error originated
|
||||||
// Also, we would generate an additional diagnostic, so that exact place of macro
|
// Also, we would generate an additional diagnostic, so that exact place of macro
|
||||||
// will be highlighted in the error origin place.
|
// will be highlighted in the error origin place.
|
||||||
let additional_diagnostic =
|
if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
|
||||||
if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
|
let in_macro_location = location_naive(workspace_root, &primary_span);
|
||||||
let in_macro_location = location_naive(workspace_root, &primary_span);
|
|
||||||
|
|
||||||
// Add related information for the main disagnostic.
|
// Add related information for the main disagnostic.
|
||||||
related_information.push(lsp_types::DiagnosticRelatedInformation {
|
related_macro_info = Some(lsp_types::DiagnosticRelatedInformation {
|
||||||
location: in_macro_location.clone(),
|
location: in_macro_location.clone(),
|
||||||
message: "Error originated from macro here".to_string(),
|
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.
|
// For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
|
||||||
let information_for_additional_diagnostic =
|
let information_for_additional_diagnostic =
|
||||||
vec![lsp_types::DiagnosticRelatedInformation {
|
vec![lsp_types::DiagnosticRelatedInformation {
|
||||||
location: location.clone(),
|
location: location.clone(),
|
||||||
message: "Exact error occured here".to_string(),
|
message: "Exact error occured here".to_string(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let diagnostic = lsp_types::Diagnostic {
|
let diagnostic = lsp_types::Diagnostic {
|
||||||
range: in_macro_location.range,
|
range: in_macro_location.range,
|
||||||
severity,
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
fixes: fixes.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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: code.clone().map(lsp_types::NumberOrString::String),
|
||||||
code_description: code_description.clone(),
|
code_description: code_description.clone(),
|
||||||
source: Some(source.clone()),
|
source: Some(source.clone()),
|
||||||
message: message.clone(),
|
message: info.message.clone(),
|
||||||
related_information: Some(information_for_additional_diagnostic),
|
related_information: Some(vec![back_ref.clone()]),
|
||||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
tags: None, // don't apply modifiers again
|
||||||
data: None,
|
data: None,
|
||||||
};
|
},
|
||||||
|
});
|
||||||
Some(MappedRustDiagnostic {
|
|
||||||
url: in_macro_location.uri,
|
|
||||||
diagnostic,
|
|
||||||
fixes: fixes.clone(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
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())
|
|
||||||
},
|
|
||||||
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
|
|
||||||
data: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
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],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diagnostics
|
||||||
})
|
})
|
||||||
.flatten()
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue