@@ -75,8 +75,10 @@ fn diagnostic_related_information(
7575}
7676
7777enum 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