aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_flycheck/src/lib.rs3
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs157
2 files changed, 75 insertions, 85 deletions
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index 541179920..041e38a9f 100644
--- a/crates/ra_flycheck/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -13,8 +13,7 @@ use cargo_metadata::Message;
13use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
14 14
15pub use cargo_metadata::diagnostic::{ 15pub use cargo_metadata::diagnostic::{
16 Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, 16 Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion,
17 DiagnosticSpanMacroExpansion,
18}; 17};
19 18
20#[derive(Clone, Debug, PartialEq, Eq)] 19#[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 5c8d86eb5..eabf4908f 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -2,8 +2,7 @@
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{ 3use std::{
4 collections::HashMap, 4 collections::HashMap,
5 fmt::Write, 5 path::{Component, Path, Prefix},
6 path::{Component, Path, PathBuf, Prefix},
7 str::FromStr, 6 str::FromStr,
8}; 7};
9 8
@@ -12,17 +11,21 @@ use lsp_types::{
12 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, 11 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit,
13}; 12};
14use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; 13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
14use stdx::format_to;
15
16use crate::Result;
15 17
16/// Converts a Rust level string to a LSP severity 18/// Converts a Rust level string to a LSP severity
17fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { 19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
18 match val { 20 let res = match val {
19 DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error), 21 DiagnosticLevel::Ice => DiagnosticSeverity::Error,
20 DiagnosticLevel::Error => Some(DiagnosticSeverity::Error), 22 DiagnosticLevel::Error => DiagnosticSeverity::Error,
21 DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning), 23 DiagnosticLevel::Warning => DiagnosticSeverity::Warning,
22 DiagnosticLevel::Note => Some(DiagnosticSeverity::Information), 24 DiagnosticLevel::Note => DiagnosticSeverity::Information,
23 DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint), 25 DiagnosticLevel::Help => DiagnosticSeverity::Hint,
24 DiagnosticLevel::Unknown => None, 26 DiagnosticLevel::Unknown => return None,
25 } 27 };
28 Some(res)
26} 29}
27 30
28/// Check whether a file name is from macro invocation 31/// Check whether a file name is from macro invocation
@@ -33,7 +36,7 @@ fn is_from_macro(file_name: &str) -> bool {
33/// Converts a Rust macro span to a LSP location recursively 36/// Converts a Rust macro span to a LSP location recursively
34fn map_macro_span_to_location( 37fn map_macro_span_to_location(
35 span_macro: &DiagnosticSpanMacroExpansion, 38 span_macro: &DiagnosticSpanMacroExpansion,
36 workspace_root: &PathBuf, 39 workspace_root: &Path,
37) -> Option<Location> { 40) -> Option<Location> {
38 if !is_from_macro(&span_macro.span.file_name) { 41 if !is_from_macro(&span_macro.span.file_name) {
39 return Some(map_span_to_location(&span_macro.span, workspace_root)); 42 return Some(map_span_to_location(&span_macro.span, workspace_root));
@@ -47,7 +50,7 @@ fn map_macro_span_to_location(
47} 50}
48 51
49/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary 52/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
50fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location { 53fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &Path) -> Location {
51 if span.expansion.is_some() { 54 if span.expansion.is_some() {
52 let expansion = span.expansion.as_ref().unwrap(); 55 let expansion = span.expansion.as_ref().unwrap();
53 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) { 56 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
@@ -59,11 +62,12 @@ fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Loca
59} 62}
60 63
61/// Converts a Rust span to a LSP location 64/// Converts a Rust span to a LSP location
62fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location { 65fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &Path) -> Location {
63 let mut file_name = workspace_root.clone(); 66 let mut file_name = workspace_root.to_path_buf();
64 file_name.push(&span.file_name); 67 file_name.push(&span.file_name);
65 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap(); 68 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap();
66 69
70 // FIXME: this doesn't handle UTF16 offsets correctly
67 let range = Range::new( 71 let range = Range::new(
68 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), 72 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
69 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), 73 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
@@ -77,39 +81,30 @@ fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &PathBuf) -
77/// If the span is unlabelled this will return `None`. 81/// If the span is unlabelled this will return `None`.
78fn map_secondary_span_to_related( 82fn map_secondary_span_to_related(
79 span: &DiagnosticSpan, 83 span: &DiagnosticSpan,
80 workspace_root: &PathBuf, 84 workspace_root: &Path,
81) -> Option<DiagnosticRelatedInformation> { 85) -> Option<DiagnosticRelatedInformation> {
82 if let Some(label) = &span.label { 86 let message = span.label.clone()?;
83 let location = map_span_to_location(span, workspace_root); 87 let location = map_span_to_location(span, workspace_root);
84 Some(DiagnosticRelatedInformation { location, message: label.clone() }) 88 Some(DiagnosticRelatedInformation { location, message })
85 } else {
86 // Nothing to label this with
87 None
88 }
89} 89}
90 90
91/// Determines if diagnostic is related to unused code 91/// Determines if diagnostic is related to unused code
92fn is_unused_or_unnecessary(rd: &ra_flycheck::Diagnostic) -> bool { 92fn is_unused_or_unnecessary(rd: &ra_flycheck::Diagnostic) -> bool {
93 if let Some(code) = &rd.code { 93 match &rd.code {
94 match code.code.as_str() { 94 Some(code) => match code.code.as_str() {
95 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes" 95 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
96 | "unused_imports" | "unused_macros" | "unused_variables" => true, 96 | "unused_imports" | "unused_macros" | "unused_variables" => true,
97 _ => false, 97 _ => false,
98 } 98 },
99 } else { 99 None => false,
100 false
101 } 100 }
102} 101}
103 102
104/// Determines if diagnostic is related to deprecated code 103/// Determines if diagnostic is related to deprecated code
105fn is_deprecated(rd: &RustDiagnostic) -> bool { 104fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool {
106 if let Some(code) = &rd.code { 105 match &rd.code {
107 match code.code.as_str() { 106 Some(code) => code.code.as_str() == "deprecated",
108 "deprecated" => true, 107 None => false,
109 _ => false,
110 }
111 } else {
112 false
113 } 108 }
114} 109}
115 110
@@ -121,7 +116,7 @@ enum MappedRustChildDiagnostic {
121 116
122fn map_rust_child_diagnostic( 117fn map_rust_child_diagnostic(
123 rd: &ra_flycheck::Diagnostic, 118 rd: &ra_flycheck::Diagnostic,
124 workspace_root: &PathBuf, 119 workspace_root: &Path,
125) -> MappedRustChildDiagnostic { 120) -> MappedRustChildDiagnostic {
126 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); 121 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
127 if spans.is_empty() { 122 if spans.is_empty() {
@@ -142,7 +137,12 @@ fn map_rust_child_diagnostic(
142 } 137 }
143 } 138 }
144 139
145 if !edit_map.is_empty() { 140 if edit_map.is_empty() {
141 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
142 location: map_span_to_location(spans[0], workspace_root),
143 message: rd.message.clone(),
144 })
145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(CodeAction { 146 MappedRustChildDiagnostic::SuggestedFix(CodeAction {
147 title: rd.message.clone(), 147 title: rd.message.clone(),
148 kind: Some("quickfix".to_string()), 148 kind: Some("quickfix".to_string()),
@@ -151,11 +151,6 @@ fn map_rust_child_diagnostic(
151 command: None, 151 command: None,
152 is_preferred: None, 152 is_preferred: None,
153 }) 153 })
154 } else {
155 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
156 location: map_span_to_location(spans[0], workspace_root),
157 message: rd.message.clone(),
158 })
159 } 154 }
160} 155}
161 156
@@ -178,11 +173,11 @@ pub(crate) struct MappedRustDiagnostic {
178/// If the diagnostic has no primary span this will return `None` 173/// If the diagnostic has no primary span this will return `None`
179pub(crate) fn map_rust_diagnostic_to_lsp( 174pub(crate) fn map_rust_diagnostic_to_lsp(
180 rd: &ra_flycheck::Diagnostic, 175 rd: &ra_flycheck::Diagnostic,
181 workspace_root: &PathBuf, 176 workspace_root: &Path,
182) -> Vec<MappedRustDiagnostic> { 177) -> Vec<MappedRustDiagnostic> {
183 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); 178 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
184 if primary_spans.is_empty() { 179 if primary_spans.is_empty() {
185 return vec![]; 180 return Vec::new();
186 } 181 }
187 182
188 let severity = map_level_to_severity(rd.level); 183 let severity = map_level_to_severity(rd.level);
@@ -199,8 +194,8 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
199 } 194 }
200 195
201 let mut needs_primary_span_label = true; 196 let mut needs_primary_span_label = true;
202 let mut related_information = vec![]; 197 let mut related_information = Vec::new();
203 let mut tags = vec![]; 198 let mut tags = Vec::new();
204 199
205 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 200 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
206 let related = map_secondary_span_to_related(secondary_span, workspace_root); 201 let related = map_secondary_span_to_related(secondary_span, workspace_root);
@@ -209,7 +204,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
209 } 204 }
210 } 205 }
211 206
212 let mut fixes = vec![]; 207 let mut fixes = Vec::new();
213 let mut message = rd.message.clone(); 208 let mut message = rd.message.clone();
214 for child in &rd.children { 209 for child in &rd.children {
215 let child = map_rust_child_diagnostic(&child, workspace_root); 210 let child = map_rust_child_diagnostic(&child, workspace_root);
@@ -217,7 +212,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
217 MappedRustChildDiagnostic::Related(related) => related_information.push(related), 212 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
218 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), 213 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
219 MappedRustChildDiagnostic::MessageLine(message_line) => { 214 MappedRustChildDiagnostic::MessageLine(message_line) => {
220 write!(&mut message, "\n{}", message_line).unwrap(); 215 format_to!(message, "\n{}", message_line);
221 216
222 // These secondary messages usually duplicate the content of the 217 // These secondary messages usually duplicate the content of the
223 // primary span label. 218 // primary span label.
@@ -242,7 +237,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
242 let mut message = message.clone(); 237 let mut message = message.clone();
243 if needs_primary_span_label { 238 if needs_primary_span_label {
244 if let Some(primary_span_label) = &primary_span.label { 239 if let Some(primary_span_label) = &primary_span.label {
245 write!(&mut message, "\n{}", primary_span_label).unwrap(); 240 format_to!(message, "\n{}", primary_span_label);
246 } 241 }
247 } 242 }
248 243
@@ -262,12 +257,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
262 code: code.clone().map(NumberOrString::String), 257 code: code.clone().map(NumberOrString::String),
263 source: Some(source.clone()), 258 source: Some(source.clone()),
264 message, 259 message,
265 related_information: if !related_information.is_empty() { 260 related_information: if related_information.is_empty() {
266 Some(related_information.clone())
267 } else {
268 None 261 None
262 } else {
263 Some(related_information.clone())
269 }, 264 },
270 tags: if !tags.is_empty() { Some(tags.clone()) } else { None }, 265 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
271 }; 266 };
272 267
273 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() } 268 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() }
@@ -279,21 +274,16 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
279/// This will only happen when processing windows paths. 274/// This will only happen when processing windows paths.
280/// 275///
281/// When processing non-windows path, this is essentially the same as `Url::from_file_path`. 276/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
282pub fn url_from_path_with_drive_lowercasing( 277pub fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
283 path: impl AsRef<Path>,
284) -> Result<Url, Box<dyn std::error::Error + Send + Sync>> {
285 let component_has_windows_drive = path.as_ref().components().any(|comp| { 278 let component_has_windows_drive = path.as_ref().components().any(|comp| {
286 if let Component::Prefix(c) = comp { 279 if let Component::Prefix(c) = comp {
287 match c.kind() { 280 return matches!(c.kind(), Prefix::Disk(_) | Prefix::VerbatimDisk(_));
288 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
289 _ => return false,
290 }
291 } 281 }
292 false 282 false
293 }); 283 });
294 284
295 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters. 285 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
296 if component_has_windows_drive { 286 let res = if component_has_windows_drive {
297 let url_original = Url::from_file_path(&path) 287 let url_original = Url::from_file_path(&path)
298 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?; 288 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
299 289
@@ -308,11 +298,12 @@ pub fn url_from_path_with_drive_lowercasing(
308 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0]; 298 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
309 let url = Url::from_str(&joined).expect("This came from a valid `Url`"); 299 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
310 300
311 Ok(url) 301 url
312 } else { 302 } else {
313 Ok(Url::from_file_path(&path) 303 Url::from_file_path(&path)
314 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?) 304 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?
315 } 305 };
306 Ok(res)
316} 307}
317 308
318#[cfg(test)] 309#[cfg(test)]
@@ -337,8 +328,8 @@ mod tests {
337 } 328 }
338 329
339 #[cfg(not(windows))] 330 #[cfg(not(windows))]
340 fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic { 331 fn parse_diagnostic(val: &str) -> ra_flycheck::Diagnostic {
341 serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap() 332 serde_json::from_str::<ra_flycheck::Diagnostic>(val).unwrap()
342 } 333 }
343 334
344 #[test] 335 #[test]
@@ -390,8 +381,8 @@ mod tests {
390 "##, 381 "##,
391 ); 382 );
392 383
393 let workspace_root = PathBuf::from("/test/"); 384 let workspace_root = Path::new("/test/");
394 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 385 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
395 insta::assert_debug_snapshot!(diag); 386 insta::assert_debug_snapshot!(diag);
396 } 387 }
397 388
@@ -473,8 +464,8 @@ mod tests {
473 }"##, 464 }"##,
474 ); 465 );
475 466
476 let workspace_root = PathBuf::from("/test/"); 467 let workspace_root = Path::new("/test/");
477 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 468 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
478 insta::assert_debug_snapshot!(diag); 469 insta::assert_debug_snapshot!(diag);
479 } 470 }
480 471
@@ -598,8 +589,8 @@ mod tests {
598 }"##, 589 }"##,
599 ); 590 );
600 591
601 let workspace_root = PathBuf::from("/test/"); 592 let workspace_root = Path::new("/test/");
602 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 593 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
603 insta::assert_debug_snapshot!(diag); 594 insta::assert_debug_snapshot!(diag);
604 } 595 }
605 596
@@ -719,8 +710,8 @@ mod tests {
719 }"##, 710 }"##,
720 ); 711 );
721 712
722 let workspace_root = PathBuf::from("/test/"); 713 let workspace_root = Path::new("/test/");
723 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 714 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
724 insta::assert_debug_snapshot!(diag); 715 insta::assert_debug_snapshot!(diag);
725 } 716 }
726 717
@@ -763,8 +754,8 @@ mod tests {
763 }"##, 754 }"##,
764 ); 755 );
765 756
766 let workspace_root = PathBuf::from("/test/"); 757 let workspace_root = Path::new("/test/");
767 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 758 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
768 insta::assert_debug_snapshot!(diag); 759 insta::assert_debug_snapshot!(diag);
769 } 760 }
770 761
@@ -1035,8 +1026,8 @@ mod tests {
1035 }"##, 1026 }"##,
1036 ); 1027 );
1037 1028
1038 let workspace_root = PathBuf::from("/test/"); 1029 let workspace_root = Path::new("/test/");
1039 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 1030 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1040 insta::assert_debug_snapshot!(diag); 1031 insta::assert_debug_snapshot!(diag);
1041 } 1032 }
1042 1033
@@ -1265,8 +1256,8 @@ mod tests {
1265 "##, 1256 "##,
1266 ); 1257 );
1267 1258
1268 let workspace_root = PathBuf::from("/test/"); 1259 let workspace_root = Path::new("/test/");
1269 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 1260 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1270 insta::assert_debug_snapshot!(diag); 1261 insta::assert_debug_snapshot!(diag);
1271 } 1262 }
1272 1263
@@ -1399,8 +1390,8 @@ mod tests {
1399 "##, 1390 "##,
1400 ); 1391 );
1401 1392
1402 let workspace_root = PathBuf::from("/test/"); 1393 let workspace_root = Path::new("/test/");
1403 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root); 1394 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1404 insta::assert_debug_snapshot!(diag); 1395 insta::assert_debug_snapshot!(diag);
1405 } 1396 }
1406} 1397}