aboutsummaryrefslogtreecommitdiff
path: root/crates/rust-analyzer/src/diagnostics
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-07-09 14:34:37 +0100
committerAleksey Kladov <[email protected]>2020-07-09 14:34:37 +0100
commitb3985190114233861132b0f479731f00380e1342 (patch)
tree540821f564832f0f55fabb314fd486728268c33d /crates/rust-analyzer/src/diagnostics
parent117392e879f2732aacd029189de844bda286df2c (diff)
Cleanup diagnostic conversion code
Diffstat (limited to 'crates/rust-analyzer/src/diagnostics')
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs173
1 files changed, 71 insertions, 102 deletions
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 3eed118a9..f3a22885e 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -2,11 +2,7 @@
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{collections::HashMap, path::Path}; 3use std::{collections::HashMap, path::Path};
4 4
5use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; 5use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
6use lsp_types::{
7 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
8 NumberOrString, Position, Range, TextEdit, Url,
9};
10use stdx::format_to; 6use stdx::format_to;
11 7
12use crate::{lsp_ext, to_proto::url_from_abs_path}; 8use crate::{lsp_ext, to_proto::url_from_abs_path};
@@ -14,22 +10,25 @@ use crate::{lsp_ext, to_proto::url_from_abs_path};
14use super::DiagnosticsConfig; 10use super::DiagnosticsConfig;
15 11
16/// Determines the LSP severity from a diagnostic 12/// Determines the LSP severity from a diagnostic
17fn map_diagnostic_to_severity( 13fn diagnostic_severity(
18 config: &DiagnosticsConfig, 14 config: &DiagnosticsConfig,
19 val: &flycheck::Diagnostic, 15 level: flycheck::DiagnosticLevel,
20) -> Option<DiagnosticSeverity> { 16 code: Option<flycheck::DiagnosticCode>,
21 let res = match val.level { 17) -> Option<lsp_types::DiagnosticSeverity> {
22 DiagnosticLevel::Ice => DiagnosticSeverity::Error, 18 let res = match level {
23 DiagnosticLevel::Error => DiagnosticSeverity::Error, 19 DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::Error,
24 DiagnosticLevel::Warning => match &val.code { 20 DiagnosticLevel::Error => lsp_types::DiagnosticSeverity::Error,
25 Some(code) if config.warnings_as_hint.contains(&code.code) => DiagnosticSeverity::Hint, 21 DiagnosticLevel::Warning => match &code {
22 Some(code) if config.warnings_as_hint.contains(&code.code) => {
23 lsp_types::DiagnosticSeverity::Hint
24 }
26 Some(code) if config.warnings_as_info.contains(&code.code) => { 25 Some(code) if config.warnings_as_info.contains(&code.code) => {
27 DiagnosticSeverity::Information 26 lsp_types::DiagnosticSeverity::Information
28 } 27 }
29 _ => DiagnosticSeverity::Warning, 28 _ => lsp_types::DiagnosticSeverity::Warning,
30 }, 29 },
31 DiagnosticLevel::Note => DiagnosticSeverity::Information, 30 DiagnosticLevel::Note => lsp_types::DiagnosticSeverity::Information,
32 DiagnosticLevel::Help => DiagnosticSeverity::Hint, 31 DiagnosticLevel::Help => lsp_types::DiagnosticSeverity::Hint,
33 DiagnosticLevel::Unknown => return None, 32 DiagnosticLevel::Unknown => return None,
34 }; 33 };
35 Some(res) 34 Some(res)
@@ -40,90 +39,50 @@ fn is_from_macro(file_name: &str) -> bool {
40 file_name.starts_with('<') && file_name.ends_with('>') 39 file_name.starts_with('<') && file_name.ends_with('>')
41} 40}
42 41
43/// Converts a Rust macro span to a LSP location recursively
44fn map_macro_span_to_location(
45 span_macro: &DiagnosticSpanMacroExpansion,
46 workspace_root: &Path,
47) -> Option<Location> {
48 if !is_from_macro(&span_macro.span.file_name) {
49 return Some(map_span_to_location(&span_macro.span, workspace_root));
50 }
51
52 if let Some(expansion) = &span_macro.span.expansion {
53 return map_macro_span_to_location(&expansion, workspace_root);
54 }
55
56 None
57}
58
59/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary 42/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
60fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &Path) -> Location { 43fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
61 if span.expansion.is_some() { 44 let mut span = span.clone();
62 let expansion = span.expansion.as_ref().unwrap(); 45 while let Some(expansion) = span.expansion {
63 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) { 46 span = expansion.span;
64 return macro_range;
65 }
66 } 47 }
67 48 return location_naive(workspace_root, &span);
68 map_span_to_location_naive(span, workspace_root)
69} 49}
70 50
71/// Converts a Rust span to a LSP location 51/// Converts a Rust span to a LSP location
72fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &Path) -> Location { 52fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
73 let mut file_name = workspace_root.to_path_buf(); 53 let file_name = workspace_root.join(&span.file_name);
74 file_name.push(&span.file_name);
75 let uri = url_from_abs_path(&file_name); 54 let uri = url_from_abs_path(&file_name);
76 55
77 // FIXME: this doesn't handle UTF16 offsets correctly 56 // FIXME: this doesn't handle UTF16 offsets correctly
78 let range = Range::new( 57 let range = lsp_types::Range::new(
79 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), 58 lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
80 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), 59 lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
81 ); 60 );
82 61
83 Location { uri, range } 62 lsp_types::Location { uri, range }
84} 63}
85 64
86/// Converts a secondary Rust span to a LSP related information 65/// Converts a secondary Rust span to a LSP related inflocation(ormation
87/// 66///
88/// If the span is unlabelled this will return `None`. 67/// If the span is unlabelled this will return `None`.
89fn map_secondary_span_to_related( 68fn diagnostic_related_information(
90 span: &DiagnosticSpan,
91 workspace_root: &Path, 69 workspace_root: &Path,
92) -> Option<DiagnosticRelatedInformation> { 70 span: &DiagnosticSpan,
71) -> Option<lsp_types::DiagnosticRelatedInformation> {
93 let message = span.label.clone()?; 72 let message = span.label.clone()?;
94 let location = map_span_to_location(span, workspace_root); 73 let location = location(workspace_root, span);
95 Some(DiagnosticRelatedInformation { location, message }) 74 Some(lsp_types::DiagnosticRelatedInformation { location, message })
96}
97
98/// Determines if diagnostic is related to unused code
99fn is_unused_or_unnecessary(rd: &flycheck::Diagnostic) -> bool {
100 match &rd.code {
101 Some(code) => match code.code.as_str() {
102 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
103 | "unused_imports" | "unused_macros" | "unused_variables" => true,
104 _ => false,
105 },
106 None => false,
107 }
108}
109
110/// Determines if diagnostic is related to deprecated code
111fn is_deprecated(rd: &flycheck::Diagnostic) -> bool {
112 match &rd.code {
113 Some(code) => code.code.as_str() == "deprecated",
114 None => false,
115 }
116} 75}
117 76
118enum MappedRustChildDiagnostic { 77enum MappedRustChildDiagnostic {
119 Related(DiagnosticRelatedInformation), 78 Related(lsp_types::DiagnosticRelatedInformation),
120 SuggestedFix(lsp_ext::CodeAction), 79 SuggestedFix(lsp_ext::CodeAction),
121 MessageLine(String), 80 MessageLine(String),
122} 81}
123 82
124fn map_rust_child_diagnostic( 83fn map_rust_child_diagnostic(
125 rd: &flycheck::Diagnostic,
126 workspace_root: &Path, 84 workspace_root: &Path,
85 rd: &flycheck::Diagnostic,
127) -> MappedRustChildDiagnostic { 86) -> MappedRustChildDiagnostic {
128 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); 87 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
129 if spans.is_empty() { 88 if spans.is_empty() {
@@ -132,21 +91,20 @@ fn map_rust_child_diagnostic(
132 return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); 91 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
133 } 92 }
134 93
135 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new(); 94 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
136 for &span in &spans { 95 for &span in &spans {
137 match (&span.suggestion_applicability, &span.suggested_replacement) { 96 if let (Some(Applicability::MachineApplicable), Some(suggested_replacement)) =
138 (Some(Applicability::MachineApplicable), Some(suggested_replacement)) => { 97 (&span.suggestion_applicability, &span.suggested_replacement)
139 let location = map_span_to_location(span, workspace_root); 98 {
140 let edit = TextEdit::new(location.range, suggested_replacement.clone()); 99 let location = location(workspace_root, span);
141 edit_map.entry(location.uri).or_default().push(edit); 100 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
142 } 101 edit_map.entry(location.uri).or_default().push(edit);
143 _ => {}
144 } 102 }
145 } 103 }
146 104
147 if edit_map.is_empty() { 105 if edit_map.is_empty() {
148 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { 106 MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
149 location: map_span_to_location(spans[0], workspace_root), 107 location: location(workspace_root, spans[0]),
150 message: rd.message.clone(), 108 message: rd.message.clone(),
151 }) 109 })
152 } else { 110 } else {
@@ -167,8 +125,8 @@ fn map_rust_child_diagnostic(
167 125
168#[derive(Debug)] 126#[derive(Debug)]
169pub(crate) struct MappedRustDiagnostic { 127pub(crate) struct MappedRustDiagnostic {
170 pub(crate) location: Location, 128 pub(crate) location: lsp_types::Location,
171 pub(crate) diagnostic: Diagnostic, 129 pub(crate) diagnostic: lsp_types::Diagnostic,
172 pub(crate) fixes: Vec<lsp_ext::CodeAction>, 130 pub(crate) fixes: Vec<lsp_ext::CodeAction>,
173} 131}
174 132
@@ -192,7 +150,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
192 return Vec::new(); 150 return Vec::new();
193 } 151 }
194 152
195 let severity = map_diagnostic_to_severity(config, rd); 153 let severity = diagnostic_severity(config, rd.level.clone(), rd.code.clone());
196 154
197 let mut source = String::from("rustc"); 155 let mut source = String::from("rustc");
198 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 156 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -210,7 +168,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
210 let mut tags = Vec::new(); 168 let mut tags = Vec::new();
211 169
212 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 170 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
213 let related = map_secondary_span_to_related(secondary_span, workspace_root); 171 let related = diagnostic_related_information(workspace_root, secondary_span);
214 if let Some(related) = related { 172 if let Some(related) = related {
215 related_information.push(related); 173 related_information.push(related);
216 } 174 }
@@ -219,7 +177,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
219 let mut fixes = Vec::new(); 177 let mut fixes = Vec::new();
220 let mut message = rd.message.clone(); 178 let mut message = rd.message.clone();
221 for child in &rd.children { 179 for child in &rd.children {
222 let child = map_rust_child_diagnostic(&child, workspace_root); 180 let child = map_rust_child_diagnostic(workspace_root, &child);
223 match child { 181 match child {
224 MappedRustChildDiagnostic::Related(related) => related_information.push(related), 182 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
225 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), 183 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
@@ -233,18 +191,30 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
233 } 191 }
234 } 192 }
235 193
236 if is_unused_or_unnecessary(rd) { 194 if let Some(code) = &rd.code {
237 tags.push(DiagnosticTag::Unnecessary); 195 let code = code.code.as_str();
238 } 196 if matches!(
197 code,
198 "dead_code"
199 | "unknown_lints"
200 | "unreachable_code"
201 | "unused_attributes"
202 | "unused_imports"
203 | "unused_macros"
204 | "unused_variables"
205 ) {
206 tags.push(lsp_types::DiagnosticTag::Unnecessary);
207 }
239 208
240 if is_deprecated(rd) { 209 if matches!(code, "deprecated") {
241 tags.push(DiagnosticTag::Deprecated); 210 tags.push(lsp_types::DiagnosticTag::Deprecated);
211 }
242 } 212 }
243 213
244 primary_spans 214 primary_spans
245 .iter() 215 .iter()
246 .map(|primary_span| { 216 .map(|primary_span| {
247 let location = map_span_to_location(&primary_span, workspace_root); 217 let location = location(workspace_root, &primary_span);
248 218
249 let mut message = message.clone(); 219 let mut message = message.clone();
250 if needs_primary_span_label { 220 if needs_primary_span_label {
@@ -256,17 +226,16 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
256 // If error occurs from macro expansion, add related info pointing to 226 // If error occurs from macro expansion, add related info pointing to
257 // where the error originated 227 // where the error originated
258 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { 228 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
259 let def_loc = map_span_to_location_naive(&primary_span, workspace_root); 229 related_information.push(lsp_types::DiagnosticRelatedInformation {
260 related_information.push(DiagnosticRelatedInformation { 230 location: location_naive(workspace_root, &primary_span),
261 location: def_loc,
262 message: "Error originated from macro here".to_string(), 231 message: "Error originated from macro here".to_string(),
263 }); 232 });
264 } 233 }
265 234
266 let diagnostic = Diagnostic { 235 let diagnostic = lsp_types::Diagnostic {
267 range: location.range, 236 range: location.range,
268 severity, 237 severity,
269 code: code.clone().map(NumberOrString::String), 238 code: code.clone().map(lsp_types::NumberOrString::String),
270 source: Some(source.clone()), 239 source: Some(source.clone()),
271 message, 240 message,
272 related_information: if related_information.is_empty() { 241 related_information: if related_information.is_empty() {