aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-03-30 18:29:26 +0100
committerJonas Schievink <[email protected]>2021-03-30 18:29:26 +0100
commit9b41effd076b6d845a6ac2c31af431c83af4ed42 (patch)
tree779c9c9c0aa31a0291be4ad40a1ff8c560e4668b
parentfd7c454d516e3af90d8c0d0d8a22655345fec2d7 (diff)
Improve rustc diagnostic mapping
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt118
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt138
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs65
3 files changed, 272 insertions, 49 deletions
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
index e5f01fb33..206d89cfa 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
@@ -22,6 +22,94 @@
22 }, 22 },
23 }, 23 },
24 severity: Some( 24 severity: Some(
25 Hint,
26 ),
27 code: Some(
28 String(
29 "E0277",
30 ),
31 ),
32 code_description: Some(
33 CodeDescription {
34 href: Url {
35 scheme: "https",
36 username: "",
37 password: None,
38 host: Some(
39 Domain(
40 "doc.rust-lang.org",
41 ),
42 ),
43 port: None,
44 path: "/error-index.html",
45 query: None,
46 fragment: Some(
47 "E0277",
48 ),
49 },
50 },
51 ),
52 source: Some(
53 "rustc",
54 ),
55 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
56 related_information: Some(
57 [
58 DiagnosticRelatedInformation {
59 location: Location {
60 uri: Url {
61 scheme: "file",
62 username: "",
63 password: None,
64 host: None,
65 port: None,
66 path: "/test/%3C::core::macros::assert_eq%20macros%3E",
67 query: None,
68 fragment: None,
69 },
70 range: Range {
71 start: Position {
72 line: 6,
73 character: 30,
74 },
75 end: Position {
76 line: 6,
77 character: 32,
78 },
79 },
80 },
81 message: "Exact error occurred here",
82 },
83 ],
84 ),
85 tags: None,
86 data: None,
87 },
88 fixes: [],
89 },
90 MappedRustDiagnostic {
91 url: Url {
92 scheme: "file",
93 username: "",
94 password: None,
95 host: None,
96 port: None,
97 path: "/test/%3C::core::macros::assert_eq%20macros%3E",
98 query: None,
99 fragment: None,
100 },
101 diagnostic: Diagnostic {
102 range: Range {
103 start: Position {
104 line: 6,
105 character: 30,
106 },
107 end: Position {
108 line: 6,
109 character: 32,
110 },
111 },
112 severity: Some(
25 Error, 113 Error,
26 ), 114 ),
27 code: Some( 115 code: Some(
@@ -53,7 +141,35 @@
53 "rustc", 141 "rustc",
54 ), 142 ),
55 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`", 143 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
56 related_information: None, 144 related_information: Some(
145 [
146 DiagnosticRelatedInformation {
147 location: Location {
148 uri: Url {
149 scheme: "file",
150 username: "",
151 password: None,
152 host: None,
153 port: None,
154 path: "/test/src/main.rs",
155 query: None,
156 fragment: None,
157 },
158 range: Range {
159 start: Position {
160 line: 1,
161 character: 4,
162 },
163 end: Position {
164 line: 1,
165 character: 26,
166 },
167 },
168 },
169 message: "Error originated from macro call here",
170 },
171 ],
172 ),
57 tags: None, 173 tags: None,
58 data: None, 174 data: None,
59 }, 175 },
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index f999848a7..c847bbb35 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -13,16 +13,16 @@
13 diagnostic: Diagnostic { 13 diagnostic: Diagnostic {
14 range: Range { 14 range: Range {
15 start: Position { 15 start: Position {
16 line: 264, 16 line: 271,
17 character: 8, 17 character: 8,
18 }, 18 },
19 end: Position { 19 end: Position {
20 line: 264, 20 line: 271,
21 character: 76, 21 character: 50,
22 }, 22 },
23 }, 23 },
24 severity: Some( 24 severity: Some(
25 Error, 25 Hint,
26 ), 26 ),
27 code: None, 27 code: None,
28 code_description: None, 28 code_description: None,
@@ -40,18 +40,18 @@
40 password: None, 40 password: None,
41 host: None, 41 host: None,
42 port: None, 42 port: None,
43 path: "/test/crates/hir_def/src/data.rs", 43 path: "/test/crates/hir_def/src/path.rs",
44 query: None, 44 query: None,
45 fragment: None, 45 fragment: None,
46 }, 46 },
47 range: Range { 47 range: Range {
48 start: Position { 48 start: Position {
49 line: 79, 49 line: 264,
50 character: 15, 50 character: 8,
51 }, 51 },
52 end: Position { 52 end: Position {
53 line: 79, 53 line: 264,
54 character: 41, 54 character: 76,
55 }, 55 },
56 }, 56 },
57 }, 57 },
@@ -87,6 +87,71 @@
87 }, 87 },
88 }, 88 },
89 severity: Some( 89 severity: Some(
90 Hint,
91 ),
92 code: None,
93 code_description: None,
94 source: Some(
95 "rustc",
96 ),
97 message: "Please register your known path in the path module",
98 related_information: Some(
99 [
100 DiagnosticRelatedInformation {
101 location: Location {
102 uri: Url {
103 scheme: "file",
104 username: "",
105 password: None,
106 host: None,
107 port: None,
108 path: "/test/crates/hir_def/src/path.rs",
109 query: None,
110 fragment: None,
111 },
112 range: Range {
113 start: Position {
114 line: 264,
115 character: 8,
116 },
117 end: Position {
118 line: 264,
119 character: 76,
120 },
121 },
122 },
123 message: "Exact error occurred here",
124 },
125 ],
126 ),
127 tags: None,
128 data: None,
129 },
130 fixes: [],
131 },
132 MappedRustDiagnostic {
133 url: Url {
134 scheme: "file",
135 username: "",
136 password: None,
137 host: None,
138 port: None,
139 path: "/test/crates/hir_def/src/path.rs",
140 query: None,
141 fragment: None,
142 },
143 diagnostic: Diagnostic {
144 range: Range {
145 start: Position {
146 line: 264,
147 character: 8,
148 },
149 end: Position {
150 line: 264,
151 character: 76,
152 },
153 },
154 severity: Some(
90 Error, 155 Error,
91 ), 156 ),
92 code: None, 157 code: None,
@@ -95,7 +160,60 @@
95 "rustc", 160 "rustc",
96 ), 161 ),
97 message: "Please register your known path in the path module", 162 message: "Please register your known path in the path module",
98 related_information: None, 163 related_information: Some(
164 [
165 DiagnosticRelatedInformation {
166 location: Location {
167 uri: Url {
168 scheme: "file",
169 username: "",
170 password: None,
171 host: None,
172 port: None,
173 path: "/test/crates/hir_def/src/path.rs",
174 query: None,
175 fragment: None,
176 },
177 range: Range {
178 start: Position {
179 line: 271,
180 character: 8,
181 },
182 end: Position {
183 line: 271,
184 character: 50,
185 },
186 },
187 },
188 message: "Error originated from macro call here",
189 },
190 DiagnosticRelatedInformation {
191 location: Location {
192 uri: Url {
193 scheme: "file",
194 username: "",
195 password: None,
196 host: None,
197 port: None,
198 path: "/test/crates/hir_def/src/data.rs",
199 query: None,
200 fragment: None,
201 },
202 range: Range {
203 start: Position {
204 line: 79,
205 character: 15,
206 },
207 end: Position {
208 line: 79,
209 character: 41,
210 },
211 },
212 },
213 message: "Error originated from macro call here",
214 },
215 ],
216 ),
99 tags: None, 217 tags: None,
100 data: None, 218 data: None,
101 }, 219 },
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 76994de71..5de481021 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -34,22 +34,8 @@ fn diagnostic_severity(
34 Some(res) 34 Some(res)
35} 35}
36 36
37/// Check whether a file name is from macro invocation
38fn is_from_macro(file_name: &str) -> bool {
39 file_name.starts_with('<') && file_name.ends_with('>')
40}
41
42/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
43fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
44 let mut span = span.clone();
45 while let Some(expansion) = span.expansion {
46 span = expansion.span;
47 }
48 return location_naive(workspace_root, &span);
49}
50
51/// Converts a Rust span to a LSP location 37/// Converts a Rust span to a LSP location
52fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 38fn convert_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
53 let file_name = workspace_root.join(&span.file_name); 39 let file_name = workspace_root.join(&span.file_name);
54 let uri = url_from_abs_path(&file_name); 40 let uri = url_from_abs_path(&file_name);
55 41
@@ -62,7 +48,7 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo
62 lsp_types::Location { uri, range } 48 lsp_types::Location { uri, range }
63} 49}
64 50
65/// Converts a secondary Rust span to a LSP related inflocation(ormation 51/// Converts a secondary Rust span to a LSP related information
66/// 52///
67/// If the span is unlabelled this will return `None`. 53/// If the span is unlabelled this will return `None`.
68fn diagnostic_related_information( 54fn diagnostic_related_information(
@@ -70,7 +56,7 @@ fn diagnostic_related_information(
70 span: &DiagnosticSpan, 56 span: &DiagnosticSpan,
71) -> Option<lsp_types::DiagnosticRelatedInformation> { 57) -> Option<lsp_types::DiagnosticRelatedInformation> {
72 let message = span.label.clone()?; 58 let message = span.label.clone()?;
73 let location = location(workspace_root, span); 59 let location = convert_location(workspace_root, span);
74 Some(lsp_types::DiagnosticRelatedInformation { location, message }) 60 Some(lsp_types::DiagnosticRelatedInformation { location, message })
75} 61}
76 62
@@ -98,7 +84,7 @@ fn map_rust_child_diagnostic(
98 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); 84 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
99 for &span in &spans { 85 for &span in &spans {
100 if let Some(suggested_replacement) = &span.suggested_replacement { 86 if let Some(suggested_replacement) = &span.suggested_replacement {
101 let location = location(workspace_root, span); 87 let location = convert_location(workspace_root, span);
102 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); 88 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
103 edit_map.entry(location.uri).or_default().push(edit); 89 edit_map.entry(location.uri).or_default().push(edit);
104 } 90 }
@@ -107,7 +93,7 @@ fn map_rust_child_diagnostic(
107 if edit_map.is_empty() { 93 if edit_map.is_empty() {
108 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 94 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
109 related: lsp_types::DiagnosticRelatedInformation { 95 related: lsp_types::DiagnosticRelatedInformation {
110 location: location(workspace_root, spans[0]), 96 location: convert_location(workspace_root, spans[0]),
111 message: rd.message.clone(), 97 message: rd.message.clone(),
112 }, 98 },
113 suggested_fix: None, 99 suggested_fix: None,
@@ -115,7 +101,7 @@ fn map_rust_child_diagnostic(
115 } else { 101 } else {
116 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 102 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
117 related: lsp_types::DiagnosticRelatedInformation { 103 related: lsp_types::DiagnosticRelatedInformation {
118 location: location(workspace_root, spans[0]), 104 location: convert_location(workspace_root, spans[0]),
119 message: rd.message.clone(), 105 message: rd.message.clone(),
120 }, 106 },
121 suggested_fix: Some(lsp_ext::CodeAction { 107 suggested_fix: Some(lsp_ext::CodeAction {
@@ -231,7 +217,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
231 primary_spans 217 primary_spans
232 .iter() 218 .iter()
233 .flat_map(|primary_span| { 219 .flat_map(|primary_span| {
234 let location = location(workspace_root, &primary_span); 220 let location = convert_location(workspace_root, &primary_span);
235 221
236 let mut message = message.clone(); 222 let mut message = message.clone();
237 if needs_primary_span_label { 223 if needs_primary_span_label {
@@ -243,21 +229,22 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
243 // Each primary diagnostic span may result in multiple LSP diagnostics. 229 // Each primary diagnostic span may result in multiple LSP diagnostics.
244 let mut diagnostics = Vec::new(); 230 let mut diagnostics = Vec::new();
245 231
246 let mut related_macro_info = None; 232 let mut related_info_macro_calls = vec![];
247 233
248 // If error occurs from macro expansion, add related info pointing to 234 // If error occurs from macro expansion, add related info pointing to
249 // where the error originated 235 // where the error originated
250 // Also, we would generate an additional diagnostic, so that exact place of macro 236 // Also, we would generate an additional diagnostic, so that exact place of macro
251 // will be highlighted in the error origin place. 237 // will be highlighted in the error origin place.
252 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { 238 let macro_calls = std::iter::successors(Some(*primary_span), |span| {
253 let in_macro_location = location_naive(workspace_root, &primary_span); 239 Some(&span.expansion.as_ref()?.span)
254 240 })
255 // Add related information for the main disagnostic. 241 .skip(1);
256 related_macro_info = Some(lsp_types::DiagnosticRelatedInformation { 242 for macro_span in macro_calls {
243 let in_macro_location = convert_location(workspace_root, &macro_span);
244 related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation {
257 location: in_macro_location.clone(), 245 location: in_macro_location.clone(),
258 message: "Error originated from macro here".to_string(), 246 message: "Error originated from macro call here".to_string(),
259 }); 247 });
260
261 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. 248 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
262 let information_for_additional_diagnostic = 249 let information_for_additional_diagnostic =
263 vec![lsp_types::DiagnosticRelatedInformation { 250 vec![lsp_types::DiagnosticRelatedInformation {
@@ -267,7 +254,8 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
267 254
268 let diagnostic = lsp_types::Diagnostic { 255 let diagnostic = lsp_types::Diagnostic {
269 range: in_macro_location.range, 256 range: in_macro_location.range,
270 severity, 257 // downgrade to hint if we're pointing at the macro
258 severity: Some(lsp_types::DiagnosticSeverity::Hint),
271 code: code.clone().map(lsp_types::NumberOrString::String), 259 code: code.clone().map(lsp_types::NumberOrString::String),
272 code_description: code_description.clone(), 260 code_description: code_description.clone(),
273 source: Some(source.clone()), 261 source: Some(source.clone()),
@@ -276,7 +264,6 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
276 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 264 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
277 data: None, 265 data: None,
278 }; 266 };
279
280 diagnostics.push(MappedRustDiagnostic { 267 diagnostics.push(MappedRustDiagnostic {
281 url: in_macro_location.uri, 268 url: in_macro_location.uri,
282 diagnostic, 269 diagnostic,
@@ -294,15 +281,17 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
294 code_description: code_description.clone(), 281 code_description: code_description.clone(),
295 source: Some(source.clone()), 282 source: Some(source.clone()),
296 message, 283 message,
297 related_information: if subdiagnostics.is_empty() { 284 related_information: {
298 None 285 let info = related_info_macro_calls
299 } else {
300 let mut related = subdiagnostics
301 .iter() 286 .iter()
302 .map(|sub| sub.related.clone()) 287 .cloned()
288 .chain(subdiagnostics.iter().map(|sub| sub.related.clone()))
303 .collect::<Vec<_>>(); 289 .collect::<Vec<_>>();
304 related.extend(related_macro_info); 290 if info.is_empty() {
305 Some(related) 291 None
292 } else {
293 Some(info)
294 }
306 }, 295 },
307 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 296 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
308 data: None, 297 data: None,