diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 9 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/to_proto.rs | 67 |
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 | }; |
18 | use lsp_types::{ClientCapabilities, MarkupKind}; | 18 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; | 19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; |
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::{FxHashMap, FxHashSet}; |
21 | use serde::{de::DeserializeOwned, Deserialize}; | 21 | use serde::{de::DeserializeOwned, Deserialize}; |
22 | use vfs::AbsPathBuf; | 22 | use 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)] |
14 | pub struct DiagnosticsMapConfig { | 14 | pub 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 |
47 | fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 47 | fn 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. |
64 | 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 { | ||
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`. |
81 | fn diagnostic_related_information( | 89 | fn 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 | 101 | fn 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)) { |
94 | fn 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 | ||
114 | struct SubDiagnostic { | 110 | struct SubDiagnostic { |
@@ -122,6 +118,7 @@ enum MappedRustChildDiagnostic { | |||
122 | } | 118 | } |
123 | 119 | ||
124 | fn map_rust_child_diagnostic( | 120 | fn 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 | } |