aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/rust-analyzer/src/config.rs9
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs1
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs67
3 files changed, 41 insertions, 36 deletions
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 7ddea22c8..3be335550 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -17,7 +17,7 @@ use ide_db::helpers::{
17}; 17};
18use lsp_types::{ClientCapabilities, MarkupKind}; 18use lsp_types::{ClientCapabilities, MarkupKind};
19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; 19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
20use rustc_hash::FxHashSet; 20use rustc_hash::{FxHashMap, FxHashSet};
21use serde::{de::DeserializeOwned, Deserialize}; 21use serde::{de::DeserializeOwned, Deserialize};
22use vfs::AbsPathBuf; 22use vfs::AbsPathBuf;
23 23
@@ -99,6 +99,9 @@ config_data! {
99 diagnostics_enableExperimental: bool = "true", 99 diagnostics_enableExperimental: bool = "true",
100 /// List of rust-analyzer diagnostics to disable. 100 /// List of rust-analyzer diagnostics to disable.
101 diagnostics_disabled: FxHashSet<String> = "[]", 101 diagnostics_disabled: FxHashSet<String> = "[]",
102 /// Map of path 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_remapPathPrefixes: FxHashMap<String, String> = "{}",
102 /// List of warnings that should be displayed with info severity. 105 /// List of warnings that should be displayed with info severity.
103 /// 106 ///
104 /// The warnings will be indicated by a blue squiggly underline in code 107 /// The warnings will be indicated by a blue squiggly underline in code
@@ -474,6 +477,7 @@ impl Config {
474 } 477 }
475 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { 478 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
476 DiagnosticsMapConfig { 479 DiagnosticsMapConfig {
480 remap_path_prefixes: self.data.diagnostics_remapPathPrefixes.clone(),
477 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), 481 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
478 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), 482 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
479 } 483 }
@@ -835,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
835 "items": { "type": "string" }, 839 "items": { "type": "string" },
836 "uniqueItems": true, 840 "uniqueItems": true,
837 }, 841 },
842 "FxHashMap<String, String>" => set! {
843 "type": "object",
844 },
838 "Option<usize>" => set! { 845 "Option<usize>" => set! {
839 "type": ["null", "integer"], 846 "type": ["null", "integer"],
840 "minimum": 0, 847 "minimum": 0,
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index f01548c50..776d21778 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
12 12
13#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
14pub struct DiagnosticsMapConfig { 14pub struct DiagnosticsMapConfig {
15 pub remap_path_prefixes: FxHashMap<String, String>,
15 pub warnings_as_info: Vec<String>, 16 pub warnings_as_info: Vec<String>,
16 pub warnings_as_hint: Vec<String>, 17 pub warnings_as_hint: Vec<String>,
17} 18}
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 8b01a7e5d..08303a781 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -44,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool {
44} 44}
45 45
46/// Converts a Rust span to a LSP location 46/// Converts a Rust span to a LSP location
47fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 47fn location(
48 let file_name = resolve_path(workspace_root, &span.file_name); 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);
49 let uri = url_from_abs_path(&file_name); 53 let uri = url_from_abs_path(&file_name);
50 54
51 // FIXME: this doesn't handle UTF16 offsets correctly 55 // FIXME: this doesn't handle UTF16 offsets correctly
@@ -61,54 +65,46 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location
61/// 65///
62/// This takes locations pointing into the standard library, or generally outside the current 66/// This takes locations pointing into the standard library, or generally outside the current
63/// workspace into account and tries to avoid those, in case macros are involved. 67/// workspace into account and tries to avoid those, in case macros are involved.
64fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 68fn primary_location(
69 config: &DiagnosticsMapConfig,
70 workspace_root: &Path,
71 span: &DiagnosticSpan,
72) -> lsp_types::Location {
65 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); 73 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
66 for span in span_stack.clone() { 74 for span in span_stack.clone() {
67 let abs_path = resolve_path(workspace_root, &span.file_name); 75 let abs_path = resolve_path(config, workspace_root, &span.file_name);
68 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { 76 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
69 return location(workspace_root, span); 77 return location(config, workspace_root, span);
70 } 78 }
71 } 79 }
72 80
73 // Fall back to the outermost macro invocation if no suitable span comes up. 81 // Fall back to the outermost macro invocation if no suitable span comes up.
74 let last_span = span_stack.last().unwrap(); 82 let last_span = span_stack.last().unwrap();
75 location(workspace_root, last_span) 83 location(config, workspace_root, last_span)
76} 84}
77 85
78/// Converts a secondary Rust span to a LSP related information 86/// Converts a secondary Rust span to a LSP related information
79/// 87///
80/// If the span is unlabelled this will return `None`. 88/// If the span is unlabelled this will return `None`.
81fn diagnostic_related_information( 89fn diagnostic_related_information(
90 config: &DiagnosticsMapConfig,
82 workspace_root: &Path, 91 workspace_root: &Path,
83 span: &DiagnosticSpan, 92 span: &DiagnosticSpan,
84) -> Option<lsp_types::DiagnosticRelatedInformation> { 93) -> Option<lsp_types::DiagnosticRelatedInformation> {
85 let message = span.label.clone()?; 94 let message = span.label.clone()?;
86 let location = location(workspace_root, span); 95 let location = location(config, workspace_root, span);
87 Some(lsp_types::DiagnosticRelatedInformation { location, message }) 96 Some(lsp_types::DiagnosticRelatedInformation { location, message })
88} 97}
89 98
90/// Resolves paths mimicking VSCode's behavior when `file_name` starts 99/// Resolves paths applying any matching path prefix remappings, and then
91/// with the root directory component, which does not discard the base 100/// joining the path to the workspace root.
92/// path. If this relative path exists, use it, otherwise fall back 101fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf {
93/// to the existing Rust behavior of path joining. 102 match config.remap_path_prefixes.iter().find(|(from, _)| file_name.starts_with(*from)) {
94fn resolve_path(workspace_root: &Path, file_name: &str) -> PathBuf { 103 Some((from, to)) => {
95 let file_name = Path::new(file_name); 104 workspace_root.join(format!("{}{}", to, file_name.strip_prefix(from).unwrap()))
96 105 }
97 // Test path with VSCode's path join behavior. 106 None => workspace_root.join(file_name),
98 let vscode_path = {
99 let mut result = PathBuf::from(workspace_root);
100 result.extend(file_name.components().skip_while(|component| match component {
101 std::path::Component::RootDir => true,
102 _ => false,
103 }));
104 result
105 };
106 if vscode_path.exists() {
107 return vscode_path;
108 } 107 }
109
110 // Default to Rust's path join behavior.
111 workspace_root.join(file_name)
112} 108}
113 109
114struct SubDiagnostic { 110struct SubDiagnostic {
@@ -122,6 +118,7 @@ enum MappedRustChildDiagnostic {
122} 118}
123 119
124fn map_rust_child_diagnostic( 120fn map_rust_child_diagnostic(
121 config: &DiagnosticsMapConfig,
125 workspace_root: &Path, 122 workspace_root: &Path,
126 rd: &flycheck::Diagnostic, 123 rd: &flycheck::Diagnostic,
127) -> MappedRustChildDiagnostic { 124) -> MappedRustChildDiagnostic {
@@ -135,7 +132,7 @@ fn map_rust_child_diagnostic(
135 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); 132 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
136 for &span in &spans { 133 for &span in &spans {
137 if let Some(suggested_replacement) = &span.suggested_replacement { 134 if let Some(suggested_replacement) = &span.suggested_replacement {
138 let location = location(workspace_root, span); 135 let location = location(config, workspace_root, span);
139 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); 136 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
140 edit_map.entry(location.uri).or_default().push(edit); 137 edit_map.entry(location.uri).or_default().push(edit);
141 } 138 }
@@ -144,7 +141,7 @@ fn map_rust_child_diagnostic(
144 if edit_map.is_empty() { 141 if edit_map.is_empty() {
145 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 142 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
146 related: lsp_types::DiagnosticRelatedInformation { 143 related: lsp_types::DiagnosticRelatedInformation {
147 location: location(workspace_root, spans[0]), 144 location: location(config, workspace_root, spans[0]),
148 message: rd.message.clone(), 145 message: rd.message.clone(),
149 }, 146 },
150 suggested_fix: None, 147 suggested_fix: None,
@@ -152,7 +149,7 @@ fn map_rust_child_diagnostic(
152 } else { 149 } else {
153 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 150 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
154 related: lsp_types::DiagnosticRelatedInformation { 151 related: lsp_types::DiagnosticRelatedInformation {
155 location: location(workspace_root, spans[0]), 152 location: location(config, workspace_root, spans[0]),
156 message: rd.message.clone(), 153 message: rd.message.clone(),
157 }, 154 },
158 suggested_fix: Some(lsp_ext::CodeAction { 155 suggested_fix: Some(lsp_ext::CodeAction {
@@ -217,7 +214,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
217 let mut tags = Vec::new(); 214 let mut tags = Vec::new();
218 215
219 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 216 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
220 let related = diagnostic_related_information(workspace_root, secondary_span); 217 let related = diagnostic_related_information(config, workspace_root, secondary_span);
221 if let Some(related) = related { 218 if let Some(related) = related {
222 subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); 219 subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
223 } 220 }
@@ -225,7 +222,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
225 222
226 let mut message = rd.message.clone(); 223 let mut message = rd.message.clone();
227 for child in &rd.children { 224 for child in &rd.children {
228 let child = map_rust_child_diagnostic(workspace_root, &child); 225 let child = map_rust_child_diagnostic(config, workspace_root, &child);
229 match child { 226 match child {
230 MappedRustChildDiagnostic::SubDiagnostic(sub) => { 227 MappedRustChildDiagnostic::SubDiagnostic(sub) => {
231 subdiagnostics.push(sub); 228 subdiagnostics.push(sub);
@@ -269,7 +266,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
269 primary_spans 266 primary_spans
270 .iter() 267 .iter()
271 .flat_map(|primary_span| { 268 .flat_map(|primary_span| {
272 let primary_location = primary_location(workspace_root, &primary_span); 269 let primary_location = primary_location(config, workspace_root, &primary_span);
273 270
274 let mut message = message.clone(); 271 let mut message = message.clone();
275 if needs_primary_span_label { 272 if needs_primary_span_label {
@@ -299,7 +296,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
299 // generated that code. 296 // generated that code.
300 let is_in_macro_call = i != 0; 297 let is_in_macro_call = i != 0;
301 298
302 let secondary_location = location(workspace_root, &span); 299 let secondary_location = location(config, workspace_root, &span);
303 if secondary_location == primary_location { 300 if secondary_location == primary_location {
304 continue; 301 continue;
305 } 302 }