mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +00:00
Diagnostic Remap Path Prefixes added.
This commit is contained in:
parent
60841f4276
commit
9fcad82980
5 changed files with 53 additions and 37 deletions
|
@ -17,7 +17,7 @@ use ide_db::helpers::{
|
||||||
};
|
};
|
||||||
use lsp_types::{ClientCapabilities, MarkupKind};
|
use lsp_types::{ClientCapabilities, MarkupKind};
|
||||||
use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
|
use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use serde::{de::DeserializeOwned, Deserialize};
|
use serde::{de::DeserializeOwned, Deserialize};
|
||||||
use vfs::AbsPathBuf;
|
use vfs::AbsPathBuf;
|
||||||
|
|
||||||
|
@ -99,6 +99,9 @@ config_data! {
|
||||||
diagnostics_enableExperimental: bool = "true",
|
diagnostics_enableExperimental: bool = "true",
|
||||||
/// List of rust-analyzer diagnostics to disable.
|
/// List of rust-analyzer diagnostics to disable.
|
||||||
diagnostics_disabled: FxHashSet<String> = "[]",
|
diagnostics_disabled: FxHashSet<String> = "[]",
|
||||||
|
/// Map of path prefixes to be substituted when parsing diagnostic file paths.
|
||||||
|
/// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
|
||||||
|
diagnostics_remapPathPrefixes: FxHashMap<String, String> = "{}",
|
||||||
/// List of warnings that should be displayed with info severity.
|
/// List of warnings that should be displayed with info severity.
|
||||||
///
|
///
|
||||||
/// The warnings will be indicated by a blue squiggly underline in code
|
/// The warnings will be indicated by a blue squiggly underline in code
|
||||||
|
@ -474,6 +477,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
|
pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
|
||||||
DiagnosticsMapConfig {
|
DiagnosticsMapConfig {
|
||||||
|
remap_path_prefixes: self.data.diagnostics_remapPathPrefixes.clone(),
|
||||||
warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
|
warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
|
||||||
warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
|
warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
|
||||||
}
|
}
|
||||||
|
@ -835,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
||||||
"items": { "type": "string" },
|
"items": { "type": "string" },
|
||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
},
|
},
|
||||||
|
"FxHashMap<String, String>" => set! {
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
"Option<usize>" => set! {
|
"Option<usize>" => set! {
|
||||||
"type": ["null", "integer"],
|
"type": ["null", "integer"],
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct DiagnosticsMapConfig {
|
pub struct DiagnosticsMapConfig {
|
||||||
|
pub remap_path_prefixes: FxHashMap<String, String>,
|
||||||
pub warnings_as_info: Vec<String>,
|
pub warnings_as_info: Vec<String>,
|
||||||
pub warnings_as_hint: Vec<String>,
|
pub warnings_as_hint: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a Rust span to a LSP location
|
/// Converts a Rust span to a LSP location
|
||||||
fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
|
fn location(
|
||||||
let file_name = resolve_path(workspace_root, &span.file_name);
|
config: &DiagnosticsMapConfig,
|
||||||
|
workspace_root: &Path,
|
||||||
|
span: &DiagnosticSpan,
|
||||||
|
) -> lsp_types::Location {
|
||||||
|
let file_name = resolve_path(config, workspace_root, &span.file_name);
|
||||||
let uri = url_from_abs_path(&file_name);
|
let uri = url_from_abs_path(&file_name);
|
||||||
|
|
||||||
// FIXME: this doesn't handle UTF16 offsets correctly
|
// FIXME: this doesn't handle UTF16 offsets correctly
|
||||||
|
@ -61,54 +65,46 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location
|
||||||
///
|
///
|
||||||
/// This takes locations pointing into the standard library, or generally outside the current
|
/// This takes locations pointing into the standard library, or generally outside the current
|
||||||
/// workspace into account and tries to avoid those, in case macros are involved.
|
/// workspace into account and tries to avoid those, in case macros are involved.
|
||||||
fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
|
fn primary_location(
|
||||||
|
config: &DiagnosticsMapConfig,
|
||||||
|
workspace_root: &Path,
|
||||||
|
span: &DiagnosticSpan,
|
||||||
|
) -> lsp_types::Location {
|
||||||
let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
|
let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
|
||||||
for span in span_stack.clone() {
|
for span in span_stack.clone() {
|
||||||
let abs_path = resolve_path(workspace_root, &span.file_name);
|
let abs_path = resolve_path(config, workspace_root, &span.file_name);
|
||||||
if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
|
if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
|
||||||
return location(workspace_root, span);
|
return location(config, workspace_root, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to the outermost macro invocation if no suitable span comes up.
|
// Fall back to the outermost macro invocation if no suitable span comes up.
|
||||||
let last_span = span_stack.last().unwrap();
|
let last_span = span_stack.last().unwrap();
|
||||||
location(workspace_root, last_span)
|
location(config, workspace_root, last_span)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a secondary Rust span to a LSP related information
|
/// Converts a secondary Rust span to a LSP related information
|
||||||
///
|
///
|
||||||
/// If the span is unlabelled this will return `None`.
|
/// If the span is unlabelled this will return `None`.
|
||||||
fn diagnostic_related_information(
|
fn diagnostic_related_information(
|
||||||
|
config: &DiagnosticsMapConfig,
|
||||||
workspace_root: &Path,
|
workspace_root: &Path,
|
||||||
span: &DiagnosticSpan,
|
span: &DiagnosticSpan,
|
||||||
) -> Option<lsp_types::DiagnosticRelatedInformation> {
|
) -> Option<lsp_types::DiagnosticRelatedInformation> {
|
||||||
let message = span.label.clone()?;
|
let message = span.label.clone()?;
|
||||||
let location = location(workspace_root, span);
|
let location = location(config, workspace_root, span);
|
||||||
Some(lsp_types::DiagnosticRelatedInformation { location, message })
|
Some(lsp_types::DiagnosticRelatedInformation { location, message })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves paths mimicking VSCode's behavior when `file_name` starts
|
/// Resolves paths applying any matching path prefix remappings, and then
|
||||||
/// with the root directory component, which does not discard the base
|
/// joining the path to the workspace root.
|
||||||
/// path. If this relative path exists, use it, otherwise fall back
|
fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf {
|
||||||
/// to the existing Rust behavior of path joining.
|
match config.remap_path_prefixes.iter().find(|(from, _)| file_name.starts_with(*from)) {
|
||||||
fn resolve_path(workspace_root: &Path, file_name: &str) -> PathBuf {
|
Some((from, to)) => {
|
||||||
let file_name = Path::new(file_name);
|
workspace_root.join(format!("{}{}", to, file_name.strip_prefix(from).unwrap()))
|
||||||
|
}
|
||||||
// Test path with VSCode's path join behavior.
|
None => workspace_root.join(file_name),
|
||||||
let vscode_path = {
|
|
||||||
let mut result = PathBuf::from(workspace_root);
|
|
||||||
result.extend(file_name.components().skip_while(|component| match component {
|
|
||||||
std::path::Component::RootDir => true,
|
|
||||||
_ => false,
|
|
||||||
}));
|
|
||||||
result
|
|
||||||
};
|
|
||||||
if vscode_path.exists() {
|
|
||||||
return vscode_path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to Rust's path join behavior.
|
|
||||||
workspace_root.join(file_name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SubDiagnostic {
|
struct SubDiagnostic {
|
||||||
|
@ -122,6 +118,7 @@ enum MappedRustChildDiagnostic {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_rust_child_diagnostic(
|
fn map_rust_child_diagnostic(
|
||||||
|
config: &DiagnosticsMapConfig,
|
||||||
workspace_root: &Path,
|
workspace_root: &Path,
|
||||||
rd: &flycheck::Diagnostic,
|
rd: &flycheck::Diagnostic,
|
||||||
) -> MappedRustChildDiagnostic {
|
) -> MappedRustChildDiagnostic {
|
||||||
|
@ -135,7 +132,7 @@ fn map_rust_child_diagnostic(
|
||||||
let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
|
let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
|
||||||
for &span in &spans {
|
for &span in &spans {
|
||||||
if let Some(suggested_replacement) = &span.suggested_replacement {
|
if let Some(suggested_replacement) = &span.suggested_replacement {
|
||||||
let location = location(workspace_root, span);
|
let location = location(config, workspace_root, span);
|
||||||
let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
|
let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
|
||||||
edit_map.entry(location.uri).or_default().push(edit);
|
edit_map.entry(location.uri).or_default().push(edit);
|
||||||
}
|
}
|
||||||
|
@ -144,7 +141,7 @@ fn map_rust_child_diagnostic(
|
||||||
if edit_map.is_empty() {
|
if edit_map.is_empty() {
|
||||||
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
|
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
|
||||||
related: lsp_types::DiagnosticRelatedInformation {
|
related: lsp_types::DiagnosticRelatedInformation {
|
||||||
location: location(workspace_root, spans[0]),
|
location: location(config, workspace_root, spans[0]),
|
||||||
message: rd.message.clone(),
|
message: rd.message.clone(),
|
||||||
},
|
},
|
||||||
suggested_fix: None,
|
suggested_fix: None,
|
||||||
|
@ -152,7 +149,7 @@ fn map_rust_child_diagnostic(
|
||||||
} else {
|
} else {
|
||||||
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
|
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
|
||||||
related: lsp_types::DiagnosticRelatedInformation {
|
related: lsp_types::DiagnosticRelatedInformation {
|
||||||
location: location(workspace_root, spans[0]),
|
location: location(config, workspace_root, spans[0]),
|
||||||
message: rd.message.clone(),
|
message: rd.message.clone(),
|
||||||
},
|
},
|
||||||
suggested_fix: Some(lsp_ext::CodeAction {
|
suggested_fix: Some(lsp_ext::CodeAction {
|
||||||
|
@ -217,7 +214,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
let mut tags = Vec::new();
|
let mut tags = Vec::new();
|
||||||
|
|
||||||
for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
|
for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
|
||||||
let related = diagnostic_related_information(workspace_root, secondary_span);
|
let related = diagnostic_related_information(config, workspace_root, secondary_span);
|
||||||
if let Some(related) = related {
|
if let Some(related) = related {
|
||||||
subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
|
subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
|
||||||
}
|
}
|
||||||
|
@ -225,7 +222,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
|
|
||||||
let mut message = rd.message.clone();
|
let mut message = rd.message.clone();
|
||||||
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(config, workspace_root, &child);
|
||||||
match child {
|
match child {
|
||||||
MappedRustChildDiagnostic::SubDiagnostic(sub) => {
|
MappedRustChildDiagnostic::SubDiagnostic(sub) => {
|
||||||
subdiagnostics.push(sub);
|
subdiagnostics.push(sub);
|
||||||
|
@ -269,7 +266,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
primary_spans
|
primary_spans
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|primary_span| {
|
.flat_map(|primary_span| {
|
||||||
let primary_location = primary_location(workspace_root, &primary_span);
|
let primary_location = primary_location(config, workspace_root, &primary_span);
|
||||||
|
|
||||||
let mut message = message.clone();
|
let mut message = message.clone();
|
||||||
if needs_primary_span_label {
|
if needs_primary_span_label {
|
||||||
|
@ -299,7 +296,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
|
||||||
// generated that code.
|
// generated that code.
|
||||||
let is_in_macro_call = i != 0;
|
let is_in_macro_call = i != 0;
|
||||||
|
|
||||||
let secondary_location = location(workspace_root, &span);
|
let secondary_location = location(config, workspace_root, &span);
|
||||||
if secondary_location == primary_location {
|
if secondary_location == primary_location {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,12 @@ have more false positives than usual.
|
||||||
--
|
--
|
||||||
List of rust-analyzer diagnostics to disable.
|
List of rust-analyzer diagnostics to disable.
|
||||||
--
|
--
|
||||||
|
[[rust-analyzer.diagnostics.remapPathPrefixes]]rust-analyzer.diagnostics.remapPathPrefixes (default: `{}`)::
|
||||||
|
+
|
||||||
|
--
|
||||||
|
Map of path prefixes to be substituted when parsing diagnostic file paths.
|
||||||
|
This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
|
||||||
|
--
|
||||||
[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
|
[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
|
@ -565,6 +565,11 @@
|
||||||
},
|
},
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
|
"rust-analyzer.diagnostics.remapPathPrefixes": {
|
||||||
|
"markdownDescription": "Map of path prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.",
|
||||||
|
"default": {},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"rust-analyzer.diagnostics.warningsAsHint": {
|
"rust-analyzer.diagnostics.warningsAsHint": {
|
||||||
"markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
|
"markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
|
||||||
"default": [],
|
"default": [],
|
||||||
|
|
Loading…
Reference in a new issue