@@ -75,8 +75,10 @@ fn diagnostic_related_information(
75
75
}
76
76
77
77
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
+ } ,
80
82
MessageLine ( String ) ,
81
83
}
82
84
@@ -103,23 +105,32 @@ fn map_rust_child_diagnostic(
103
105
}
104
106
105
107
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
+ }
110
115
} 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 ,
119
132
} ) ,
120
- is_preferred : Some ( true ) ,
121
- data : None ,
122
- } )
133
+ }
123
134
}
124
135
}
125
136
@@ -179,8 +190,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
179
190
for child in & rd. children {
180
191
let child = map_rust_child_diagnostic ( workspace_root, & child) ;
181
192
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
+ }
184
199
MappedRustChildDiagnostic :: MessageLine ( message_line) => {
185
200
format_to ! ( message, "\n {}" , message_line) ;
186
201
@@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
219
234
220
235
primary_spans
221
236
. iter ( )
222
- . map ( |primary_span| {
237
+ . flat_map ( |primary_span| {
223
238
let location = location ( workspace_root, & primary_span) ;
224
239
225
240
let mut message = message. clone ( ) ;
@@ -229,72 +244,100 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
229
244
}
230
245
}
231
246
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
+
232
252
// If error occurs from macro expansion, add related info pointing to
233
253
// where the error originated
234
254
// Also, we would generate an additional diagnostic, so that exact place of macro
235
255
// 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) ;
239
258
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
+ } ) ;
245
264
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
+ } ] ;
252
271
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 ,
272
282
} ;
273
283
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 ,
285
310
} ,
286
- tags : if tags. is_empty ( ) { None } else { Some ( tags. clone ( ) ) } ,
287
- data : None ,
288
- } ;
311
+ fixes : fixes. clone ( ) ,
312
+ } ) ;
289
313
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
+ } ) ;
295
337
}
338
+
339
+ diagnostics
296
340
} )
297
- . flatten ( )
298
341
. collect ( )
299
342
}
300
343
0 commit comments