diff options
author | Jonas Schievink <[email protected]> | 2021-03-30 18:29:26 +0100 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2021-03-30 18:29:26 +0100 |
commit | 9b41effd076b6d845a6ac2c31af431c83af4ed42 (patch) | |
tree | 779c9c9c0aa31a0291be4ad40a1ff8c560e4668b | |
parent | fd7c454d516e3af90d8c0d0d8a22655345fec2d7 (diff) |
Improve rustc diagnostic mapping
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 | ||
38 | fn 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 | ||
43 | fn 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 |
52 | fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { | 38 | fn 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`. |
68 | fn diagnostic_related_information( | 54 | fn 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, ¯o_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, |