Skip to content

Commit 1f1a1ce

Browse files
Merge #8595
8595: Diagnostic paths support specifying `remapPrefix` r=rickvanprim a=rickvanprim Currently VSCode Problem Matchers will resolve a path like `//foo_crate/src/main.rs` if `${workspaceFolder}/foo_crate/src/main.rs` exists. Presumably their behavior is functionally a string concatenation that would produce `${workspaceFolder///foo_crate/src/main.rs` and repeated path separators get ignored. This PR attempts to mimic this behavior by stripping any `Component::RootDir` from `file_name` before joining it to `workspace_root`, and then checking if the file exists. If it does, this path is used, and if not, the behavior falls through to the existing Rust path join behavior. Co-authored-by: James Leitch <rickvanprim@gmail.com>
2 parents 32491c0 + 72718bc commit 1f1a1ce

File tree

5 files changed

+62
-17
lines changed

5 files changed

+62
-17
lines changed

crates/rust-analyzer/src/config.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use ide_db::helpers::{
1717
};
1818
use lsp_types::{ClientCapabilities, MarkupKind};
1919
use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
20-
use rustc_hash::FxHashSet;
20+
use rustc_hash::{FxHashMap, FxHashSet};
2121
use serde::{de::DeserializeOwned, Deserialize};
2222
use vfs::AbsPathBuf;
2323

@@ -99,6 +99,9 @@ config_data! {
9999
diagnostics_enableExperimental: bool = "true",
100100
/// List of rust-analyzer diagnostics to disable.
101101
diagnostics_disabled: FxHashSet<String> = "[]",
102+
/// Map of prefixes to be substituted when parsing diagnostic file paths.
103+
/// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
104+
diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
102105
/// List of warnings that should be displayed with info severity.
103106
///
104107
/// The warnings will be indicated by a blue squiggly underline in code
@@ -474,6 +477,7 @@ impl Config {
474477
}
475478
pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
476479
DiagnosticsMapConfig {
480+
remap_prefix: self.data.diagnostics_remapPrefix.clone(),
477481
warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
478482
warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
479483
}
@@ -835,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
835839
"items": { "type": "string" },
836840
"uniqueItems": true,
837841
},
842+
"FxHashMap<String, String>" => set! {
843+
"type": "object",
844+
},
838845
"Option<usize>" => set! {
839846
"type": ["null", "integer"],
840847
"minimum": 0,

crates/rust-analyzer/src/diagnostics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
1212

1313
#[derive(Debug, Default, Clone)]
1414
pub struct DiagnosticsMapConfig {
15+
pub remap_prefix: FxHashMap<String, String>,
1516
pub warnings_as_info: Vec<String>,
1617
pub warnings_as_hint: Vec<String>,
1718
}

crates/rust-analyzer/src/diagnostics/to_proto.rs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! This module provides the functionality needed to convert diagnostics from
22
//! `cargo check` json format to the LSP diagnostic format.
3-
use std::{collections::HashMap, path::Path};
3+
use std::{
4+
collections::HashMap,
5+
path::{Path, PathBuf},
6+
};
47

58
use flycheck::{DiagnosticLevel, DiagnosticSpan};
69
use stdx::format_to;
@@ -41,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool {
4144
}
4245

4346
/// Converts a Rust span to a LSP location
44-
fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
45-
let file_name = workspace_root.join(&span.file_name);
47+
fn location(
48+
config: &DiagnosticsMapConfig,
49+
workspace_root: &Path,
50+
span: &DiagnosticSpan,
51+
) -> lsp_types::Location {
52+
let file_name = resolve_path(config, workspace_root, &span.file_name);
4653
let uri = url_from_abs_path(&file_name);
4754

4855
// FIXME: this doesn't handle UTF16 offsets correctly
@@ -58,32 +65,50 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location
5865
///
5966
/// This takes locations pointing into the standard library, or generally outside the current
6067
/// workspace into account and tries to avoid those, in case macros are involved.
61-
fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
68+
fn primary_location(
69+
config: &DiagnosticsMapConfig,
70+
workspace_root: &Path,
71+
span: &DiagnosticSpan,
72+
) -> lsp_types::Location {
6273
let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
6374
for span in span_stack.clone() {
64-
let abs_path = workspace_root.join(&span.file_name);
75+
let abs_path = resolve_path(config, workspace_root, &span.file_name);
6576
if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
66-
return location(workspace_root, span);
77+
return location(config, workspace_root, span);
6778
}
6879
}
6980

7081
// Fall back to the outermost macro invocation if no suitable span comes up.
7182
let last_span = span_stack.last().unwrap();
72-
location(workspace_root, last_span)
83+
location(config, workspace_root, last_span)
7384
}
7485

7586
/// Converts a secondary Rust span to a LSP related information
7687
///
7788
/// If the span is unlabelled this will return `None`.
7889
fn diagnostic_related_information(
90+
config: &DiagnosticsMapConfig,
7991
workspace_root: &Path,
8092
span: &DiagnosticSpan,
8193
) -> Option<lsp_types::DiagnosticRelatedInformation> {
8294
let message = span.label.clone()?;
83-
let location = location(workspace_root, span);
95+
let location = location(config, workspace_root, span);
8496
Some(lsp_types::DiagnosticRelatedInformation { location, message })
8597
}
8698

99+
/// Resolves paths applying any matching path prefix remappings, and then
100+
/// joining the path to the workspace root.
101+
fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf {
102+
match config
103+
.remap_prefix
104+
.iter()
105+
.find_map(|(from, to)| file_name.strip_prefix(from).map(|file_name| (to, file_name)))
106+
{
107+
Some((to, file_name)) => workspace_root.join(format!("{}{}", to, file_name)),
108+
None => workspace_root.join(file_name),
109+
}
110+
}
111+
87112
struct SubDiagnostic {
88113
related: lsp_types::DiagnosticRelatedInformation,
89114
suggested_fix: Option<lsp_ext::CodeAction>,
@@ -95,6 +120,7 @@ enum MappedRustChildDiagnostic {
95120
}
96121

97122
fn map_rust_child_diagnostic(
123+
config: &DiagnosticsMapConfig,
98124
workspace_root: &Path,
99125
rd: &flycheck::Diagnostic,
100126
) -> MappedRustChildDiagnostic {
@@ -108,7 +134,7 @@ fn map_rust_child_diagnostic(
108134
let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
109135
for &span in &spans {
110136
if let Some(suggested_replacement) = &span.suggested_replacement {
111-
let location = location(workspace_root, span);
137+
let location = location(config, workspace_root, span);
112138
let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
113139
edit_map.entry(location.uri).or_default().push(edit);
114140
}
@@ -117,15 +143,15 @@ fn map_rust_child_diagnostic(
117143
if edit_map.is_empty() {
118144
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
119145
related: lsp_types::DiagnosticRelatedInformation {
120-
location: location(workspace_root, spans[0]),
146+
location: location(config, workspace_root, spans[0]),
121147
message: rd.message.clone(),
122148
},
123149
suggested_fix: None,
124150
})
125151
} else {
126152
MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
127153
related: lsp_types::DiagnosticRelatedInformation {
128-
location: location(workspace_root, spans[0]),
154+
location: location(config, workspace_root, spans[0]),
129155
message: rd.message.clone(),
130156
},
131157
suggested_fix: Some(lsp_ext::CodeAction {
@@ -190,15 +216,15 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
190216
let mut tags = Vec::new();
191217

192218
for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
193-
let related = diagnostic_related_information(workspace_root, secondary_span);
219+
let related = diagnostic_related_information(config, workspace_root, secondary_span);
194220
if let Some(related) = related {
195221
subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
196222
}
197223
}
198224

199225
let mut message = rd.message.clone();
200226
for child in &rd.children {
201-
let child = map_rust_child_diagnostic(workspace_root, &child);
227+
let child = map_rust_child_diagnostic(config, workspace_root, &child);
202228
match child {
203229
MappedRustChildDiagnostic::SubDiagnostic(sub) => {
204230
subdiagnostics.push(sub);
@@ -242,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
242268
primary_spans
243269
.iter()
244270
.flat_map(|primary_span| {
245-
let primary_location = primary_location(workspace_root, &primary_span);
271+
let primary_location = primary_location(config, workspace_root, &primary_span);
246272

247273
let mut message = message.clone();
248274
if needs_primary_span_label {
@@ -272,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
272298
// generated that code.
273299
let is_in_macro_call = i != 0;
274300

275-
let secondary_location = location(workspace_root, &span);
301+
let secondary_location = location(config, workspace_root, &span);
276302
if secondary_location == primary_location {
277303
continue;
278304
}

docs/user/generated_config.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ have more false positives than usual.
147147
--
148148
List of rust-analyzer diagnostics to disable.
149149
--
150+
[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`)::
151+
+
152+
--
153+
Map of prefixes to be substituted when parsing diagnostic file paths.
154+
This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
155+
--
150156
[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
151157
+
152158
--

editors/code/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,11 @@
565565
},
566566
"uniqueItems": true
567567
},
568+
"rust-analyzer.diagnostics.remapPrefix": {
569+
"markdownDescription": "Map of 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`.",
570+
"default": {},
571+
"type": "object"
572+
},
568573
"rust-analyzer.diagnostics.warningsAsHint": {
569574
"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`.",
570575
"default": [],
@@ -1195,4 +1200,4 @@
11951200
]
11961201
}
11971202
}
1198-
}
1203+
}

0 commit comments

Comments
 (0)