Skip to content

Commit 9d96a6d

Browse files
Emit additional diagnostics for hints/help/etc
1 parent 8d5aa08 commit 9d96a6d

File tree

1 file changed

+116
-73
lines changed

1 file changed

+116
-73
lines changed

crates/rust-analyzer/src/diagnostics/to_proto.rs

Lines changed: 116 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ fn diagnostic_related_information(
7575
}
7676

7777
enum MappedRustChildDiagnostic {
78-
Related(lsp_types::DiagnosticRelatedInformation),
79-
SuggestedFix(lsp_ext::CodeAction),
78+
Related {
79+
related: lsp_types::DiagnosticRelatedInformation,
80+
suggested_fix: Option<lsp_ext::CodeAction>,
81+
},
8082
MessageLine(String),
8183
}
8284

@@ -103,23 +105,32 @@ fn map_rust_child_diagnostic(
103105
}
104106

105107
if edit_map.is_empty() {
106-
MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
107-
location: location(workspace_root, spans[0]),
108-
message: rd.message.clone(),
109-
})
108+
MappedRustChildDiagnostic::Related {
109+
related: lsp_types::DiagnosticRelatedInformation {
110+
location: location(workspace_root, spans[0]),
111+
message: rd.message.clone(),
112+
},
113+
suggested_fix: None,
114+
}
110115
} else {
111-
MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
112-
title: rd.message.clone(),
113-
group: None,
114-
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
115-
edit: Some(lsp_ext::SnippetWorkspaceEdit {
116-
// FIXME: there's no good reason to use edit_map here....
117-
changes: Some(edit_map),
118-
document_changes: None,
116+
MappedRustChildDiagnostic::Related {
117+
related: lsp_types::DiagnosticRelatedInformation {
118+
location: location(workspace_root, spans[0]),
119+
message: rd.message.clone(),
120+
},
121+
suggested_fix: Some(lsp_ext::CodeAction {
122+
title: rd.message.clone(),
123+
group: None,
124+
kind: Some(lsp_types::CodeActionKind::QUICKFIX),
125+
edit: Some(lsp_ext::SnippetWorkspaceEdit {
126+
// FIXME: there's no good reason to use edit_map here....
127+
changes: Some(edit_map),
128+
document_changes: None,
129+
}),
130+
is_preferred: Some(true),
131+
data: None,
119132
}),
120-
is_preferred: Some(true),
121-
data: None,
122-
})
133+
}
123134
}
124135
}
125136

@@ -179,8 +190,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
179190
for child in &rd.children {
180191
let child = map_rust_child_diagnostic(workspace_root, &child);
181192
match child {
182-
MappedRustChildDiagnostic::Related(related) => related_information.push(related),
183-
MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
193+
MappedRustChildDiagnostic::Related { related, suggested_fix } => {
194+
related_information.push(related);
195+
if let Some(code_action) = suggested_fix {
196+
fixes.push(code_action);
197+
}
198+
}
184199
MappedRustChildDiagnostic::MessageLine(message_line) => {
185200
format_to!(message, "\n{}", message_line);
186201

@@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
219234

220235
primary_spans
221236
.iter()
222-
.map(|primary_span| {
237+
.flat_map(|primary_span| {
223238
let location = location(workspace_root, &primary_span);
224239

225240
let mut message = message.clone();
@@ -229,72 +244,100 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
229244
}
230245
}
231246

247+
// Each primary diagnostic span may result in multiple LSP diagnostics.
248+
let mut diagnostics = Vec::new();
249+
250+
let mut related_macro_info = None;
251+
232252
// If error occurs from macro expansion, add related info pointing to
233253
// where the error originated
234254
// Also, we would generate an additional diagnostic, so that exact place of macro
235255
// will be highlighted in the error origin place.
236-
let additional_diagnostic =
237-
if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
238-
let in_macro_location = location_naive(workspace_root, &primary_span);
256+
if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
257+
let in_macro_location = location_naive(workspace_root, &primary_span);
239258

240-
// Add related information for the main disagnostic.
241-
related_information.push(lsp_types::DiagnosticRelatedInformation {
242-
location: in_macro_location.clone(),
243-
message: "Error originated from macro here".to_string(),
244-
});
259+
// Add related information for the main disagnostic.
260+
related_macro_info = Some(lsp_types::DiagnosticRelatedInformation {
261+
location: in_macro_location.clone(),
262+
message: "Error originated from macro here".to_string(),
263+
});
245264

246-
// For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
247-
let information_for_additional_diagnostic =
248-
vec![lsp_types::DiagnosticRelatedInformation {
249-
location: location.clone(),
250-
message: "Exact error occured here".to_string(),
251-
}];
265+
// For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
266+
let information_for_additional_diagnostic =
267+
vec![lsp_types::DiagnosticRelatedInformation {
268+
location: location.clone(),
269+
message: "Exact error occured here".to_string(),
270+
}];
252271

253-
let diagnostic = lsp_types::Diagnostic {
254-
range: in_macro_location.range,
255-
severity,
256-
code: code.clone().map(lsp_types::NumberOrString::String),
257-
code_description: code_description.clone(),
258-
source: Some(source.clone()),
259-
message: message.clone(),
260-
related_information: Some(information_for_additional_diagnostic),
261-
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
262-
data: None,
263-
};
264-
265-
Some(MappedRustDiagnostic {
266-
url: in_macro_location.uri,
267-
diagnostic,
268-
fixes: fixes.clone(),
269-
})
270-
} else {
271-
None
272+
let diagnostic = lsp_types::Diagnostic {
273+
range: in_macro_location.range,
274+
severity,
275+
code: code.clone().map(lsp_types::NumberOrString::String),
276+
code_description: code_description.clone(),
277+
source: Some(source.clone()),
278+
message: message.clone(),
279+
related_information: Some(information_for_additional_diagnostic),
280+
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
281+
data: None,
272282
};
273283

274-
let diagnostic = lsp_types::Diagnostic {
275-
range: location.range,
276-
severity,
277-
code: code.clone().map(lsp_types::NumberOrString::String),
278-
code_description: code_description.clone(),
279-
source: Some(source.clone()),
280-
message,
281-
related_information: if related_information.is_empty() {
282-
None
283-
} else {
284-
Some(related_information.clone())
284+
diagnostics.push(MappedRustDiagnostic {
285+
url: in_macro_location.uri,
286+
diagnostic,
287+
fixes: fixes.clone(),
288+
});
289+
}
290+
291+
// Emit the primary diagnostic.
292+
diagnostics.push(MappedRustDiagnostic {
293+
url: location.uri.clone(),
294+
diagnostic: lsp_types::Diagnostic {
295+
range: location.range,
296+
severity,
297+
code: code.clone().map(lsp_types::NumberOrString::String),
298+
code_description: code_description.clone(),
299+
source: Some(source.clone()),
300+
message,
301+
related_information: if related_information.is_empty() {
302+
None
303+
} else {
304+
let mut related = related_information.clone();
305+
related.extend(related_macro_info);
306+
Some(related)
307+
},
308+
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
309+
data: None,
285310
},
286-
tags: if tags.is_empty() { None } else { Some(tags.clone()) },
287-
data: None,
288-
};
311+
fixes: fixes.clone(),
312+
});
289313

290-
let main_diagnostic =
291-
MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() };
292-
match additional_diagnostic {
293-
None => vec![main_diagnostic],
294-
Some(additional_diagnostic) => vec![main_diagnostic, additional_diagnostic],
314+
// Emit hint-level diagnostics for all `related_information` entries such as "help"s.
315+
// This is useful because they will show up in the user's editor, unlike
316+
// `related_information`, which just produces hard-to-read links, at least in VS Code.
317+
let back_ref = lsp_types::DiagnosticRelatedInformation {
318+
location,
319+
message: "original diagnostic".to_string(),
320+
};
321+
for info in &related_information {
322+
diagnostics.push(MappedRustDiagnostic {
323+
url: info.location.uri.clone(),
324+
fixes: fixes.clone(), // share fixes to make them easier to apply
325+
diagnostic: lsp_types::Diagnostic {
326+
range: info.location.range,
327+
severity: Some(lsp_types::DiagnosticSeverity::Hint),
328+
code: code.clone().map(lsp_types::NumberOrString::String),
329+
code_description: code_description.clone(),
330+
source: Some(source.clone()),
331+
message: info.message.clone(),
332+
related_information: Some(vec![back_ref.clone()]),
333+
tags: None, // don't apply modifiers again
334+
data: None,
335+
},
336+
});
295337
}
338+
339+
diagnostics
296340
})
297-
.flatten()
298341
.collect()
299342
}
300343

0 commit comments

Comments
 (0)