diff options
Diffstat (limited to 'crates/ra_cargo_watch/src/conv.rs')
-rw-r--r-- | crates/ra_cargo_watch/src/conv.rs | 128 |
1 files changed, 65 insertions, 63 deletions
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs index 0246adfb5..c6f8ca329 100644 --- a/crates/ra_cargo_watch/src/conv.rs +++ b/crates/ra_cargo_watch/src/conv.rs | |||
@@ -8,6 +8,7 @@ use lsp_types::{ | |||
8 | Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, | 8 | Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, |
9 | }; | 9 | }; |
10 | use std::{ | 10 | use std::{ |
11 | collections::HashMap, | ||
11 | fmt::Write, | 12 | fmt::Write, |
12 | path::{Component, Path, PathBuf, Prefix}, | 13 | path::{Component, Path, PathBuf, Prefix}, |
13 | str::FromStr, | 14 | str::FromStr, |
@@ -126,44 +127,34 @@ fn map_rust_child_diagnostic( | |||
126 | rd: &RustDiagnostic, | 127 | rd: &RustDiagnostic, |
127 | workspace_root: &PathBuf, | 128 | workspace_root: &PathBuf, |
128 | ) -> MappedRustChildDiagnostic { | 129 | ) -> MappedRustChildDiagnostic { |
129 | let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) { | 130 | let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); |
130 | Some(span) => span, | 131 | if spans.is_empty() { |
131 | None => { | 132 | // `rustc` uses these spanless children as a way to print multi-line |
132 | // `rustc` uses these spanless children as a way to print multi-line | 133 | // messages |
133 | // messages | 134 | return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); |
134 | return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); | 135 | } |
136 | |||
137 | let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new(); | ||
138 | for &span in &spans { | ||
139 | if let Some(suggested_replacement) = &span.suggested_replacement { | ||
140 | let location = map_span_to_location(span, workspace_root); | ||
141 | let edit = TextEdit::new(location.range, suggested_replacement.clone()); | ||
142 | edit_map.entry(location.uri).or_default().push(edit); | ||
135 | } | 143 | } |
136 | }; | 144 | } |
137 | |||
138 | // If we have a primary span use its location, otherwise use the parent | ||
139 | let location = map_span_to_location(&span, workspace_root); | ||
140 | |||
141 | if let Some(suggested_replacement) = &span.suggested_replacement { | ||
142 | // Include our replacement in the title unless it's empty | ||
143 | let title = if !suggested_replacement.is_empty() { | ||
144 | format!("{}: '{}'", rd.message, suggested_replacement) | ||
145 | } else { | ||
146 | rd.message.clone() | ||
147 | }; | ||
148 | |||
149 | let edit = { | ||
150 | let edits = vec![TextEdit::new(location.range, suggested_replacement.clone())]; | ||
151 | let mut edit_map = std::collections::HashMap::new(); | ||
152 | edit_map.insert(location.uri, edits); | ||
153 | WorkspaceEdit::new(edit_map) | ||
154 | }; | ||
155 | 145 | ||
146 | if !edit_map.is_empty() { | ||
156 | MappedRustChildDiagnostic::SuggestedFix(CodeAction { | 147 | MappedRustChildDiagnostic::SuggestedFix(CodeAction { |
157 | title, | 148 | title: rd.message.clone(), |
158 | kind: Some("quickfix".to_string()), | 149 | kind: Some("quickfix".to_string()), |
159 | diagnostics: None, | 150 | diagnostics: None, |
160 | edit: Some(edit), | 151 | edit: Some(WorkspaceEdit::new(edit_map)), |
161 | command: None, | 152 | command: None, |
162 | is_preferred: None, | 153 | is_preferred: None, |
163 | }) | 154 | }) |
164 | } else { | 155 | } else { |
165 | MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { | 156 | MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { |
166 | location, | 157 | location: map_span_to_location(spans[0], workspace_root), |
167 | message: rd.message.clone(), | 158 | message: rd.message.clone(), |
168 | }) | 159 | }) |
169 | } | 160 | } |
@@ -189,13 +180,13 @@ pub(crate) struct MappedRustDiagnostic { | |||
189 | pub(crate) fn map_rust_diagnostic_to_lsp( | 180 | pub(crate) fn map_rust_diagnostic_to_lsp( |
190 | rd: &RustDiagnostic, | 181 | rd: &RustDiagnostic, |
191 | workspace_root: &PathBuf, | 182 | workspace_root: &PathBuf, |
192 | ) -> Option<MappedRustDiagnostic> { | 183 | ) -> Vec<MappedRustDiagnostic> { |
193 | let primary_span = rd.spans.iter().find(|s| s.is_primary)?; | 184 | let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); |
194 | 185 | if primary_spans.is_empty() { | |
195 | let location = map_span_to_location(&primary_span, workspace_root); | 186 | return vec![]; |
187 | } | ||
196 | 188 | ||
197 | let severity = map_level_to_severity(rd.level); | 189 | let severity = map_level_to_severity(rd.level); |
198 | let mut primary_span_label = primary_span.label.as_ref(); | ||
199 | 190 | ||
200 | let mut source = String::from("rustc"); | 191 | let mut source = String::from("rustc"); |
201 | let mut code = rd.code.as_ref().map(|c| c.code.clone()); | 192 | let mut code = rd.code.as_ref().map(|c| c.code.clone()); |
@@ -208,19 +199,10 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
208 | } | 199 | } |
209 | } | 200 | } |
210 | 201 | ||
202 | let mut needs_primary_span_label = true; | ||
211 | let mut related_information = vec![]; | 203 | let mut related_information = vec![]; |
212 | let mut tags = vec![]; | 204 | let mut tags = vec![]; |
213 | 205 | ||
214 | // If error occurs from macro expansion, add related info pointing to | ||
215 | // where the error originated | ||
216 | if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { | ||
217 | let def_loc = map_span_to_location_naive(&primary_span, workspace_root); | ||
218 | related_information.push(DiagnosticRelatedInformation { | ||
219 | location: def_loc, | ||
220 | message: "Error originated from macro here".to_string(), | ||
221 | }); | ||
222 | } | ||
223 | |||
224 | for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { | 206 | for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { |
225 | let related = map_secondary_span_to_related(secondary_span, workspace_root); | 207 | let related = map_secondary_span_to_related(secondary_span, workspace_root); |
226 | if let Some(related) = related { | 208 | if let Some(related) = related { |
@@ -240,15 +222,11 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
240 | 222 | ||
241 | // These secondary messages usually duplicate the content of the | 223 | // These secondary messages usually duplicate the content of the |
242 | // primary span label. | 224 | // primary span label. |
243 | primary_span_label = None; | 225 | needs_primary_span_label = false; |
244 | } | 226 | } |
245 | } | 227 | } |
246 | } | 228 | } |
247 | 229 | ||
248 | if let Some(primary_span_label) = primary_span_label { | ||
249 | write!(&mut message, "\n{}", primary_span_label).unwrap(); | ||
250 | } | ||
251 | |||
252 | if is_unused_or_unnecessary(rd) { | 230 | if is_unused_or_unnecessary(rd) { |
253 | tags.push(DiagnosticTag::Unnecessary); | 231 | tags.push(DiagnosticTag::Unnecessary); |
254 | } | 232 | } |
@@ -257,21 +235,45 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
257 | tags.push(DiagnosticTag::Deprecated); | 235 | tags.push(DiagnosticTag::Deprecated); |
258 | } | 236 | } |
259 | 237 | ||
260 | let diagnostic = Diagnostic { | 238 | primary_spans |
261 | range: location.range, | 239 | .iter() |
262 | severity, | 240 | .map(|primary_span| { |
263 | code: code.map(NumberOrString::String), | 241 | let location = map_span_to_location(&primary_span, workspace_root); |
264 | source: Some(source), | 242 | |
265 | message, | 243 | let mut message = message.clone(); |
266 | related_information: if !related_information.is_empty() { | 244 | if needs_primary_span_label { |
267 | Some(related_information) | 245 | if let Some(primary_span_label) = &primary_span.label { |
268 | } else { | 246 | write!(&mut message, "\n{}", primary_span_label).unwrap(); |
269 | None | 247 | } |
270 | }, | 248 | } |
271 | tags: if !tags.is_empty() { Some(tags) } else { None }, | 249 | |
272 | }; | 250 | // If error occurs from macro expansion, add related info pointing to |
273 | 251 | // where the error originated | |
274 | Some(MappedRustDiagnostic { location, diagnostic, fixes }) | 252 | if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { |
253 | let def_loc = map_span_to_location_naive(&primary_span, workspace_root); | ||
254 | related_information.push(DiagnosticRelatedInformation { | ||
255 | location: def_loc, | ||
256 | message: "Error originated from macro here".to_string(), | ||
257 | }); | ||
258 | } | ||
259 | |||
260 | let diagnostic = Diagnostic { | ||
261 | range: location.range, | ||
262 | severity, | ||
263 | code: code.clone().map(NumberOrString::String), | ||
264 | source: Some(source.clone()), | ||
265 | message, | ||
266 | related_information: if !related_information.is_empty() { | ||
267 | Some(related_information.clone()) | ||
268 | } else { | ||
269 | None | ||
270 | }, | ||
271 | tags: if !tags.is_empty() { Some(tags.clone()) } else { None }, | ||
272 | }; | ||
273 | |||
274 | MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() } | ||
275 | }) | ||
276 | .collect() | ||
275 | } | 277 | } |
276 | 278 | ||
277 | /// Returns a `Url` object from a given path, will lowercase drive letters if present. | 279 | /// Returns a `Url` object from a given path, will lowercase drive letters if present. |