@@ -157,7 +157,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
157
157
}
158
158
}
159
159
fn into_tokens ( self ) -> proc_macro2:: TokenStream {
160
- let SessionDiagnosticDerive { structure, mut builder } = self ;
160
+ let SessionDiagnosticDerive { mut structure, mut builder } = self ;
161
161
162
162
let ast = structure. ast ( ) ;
163
163
let attrs = & ast. attrs ;
@@ -175,11 +175,17 @@ impl<'a> SessionDiagnosticDerive<'a> {
175
175
}
176
176
} ;
177
177
178
- let body = structure. each ( |field_binding| {
178
+ // Generates calls to `span_label` and similar functions based on the attributes
179
+ // on fields. Code for suggestions uses formatting machinery and the value of
180
+ // other fields - because any given field can be referenced multiple times, it
181
+ // should be accessed through a borrow. When passing fields to `set_arg` (which
182
+ // happens below) for Fluent, we want to move the data, so that has to happen
183
+ // in a separate pass over the fields.
184
+ let attrs = structure. each ( |field_binding| {
179
185
let field = field_binding. ast ( ) ;
180
186
let result = field. attrs . iter ( ) . map ( |attr| {
181
187
builder
182
- . generate_field_code (
188
+ . generate_field_attr_code (
183
189
attr,
184
190
FieldInfo {
185
191
vis : & field. vis ,
@@ -190,10 +196,30 @@ impl<'a> SessionDiagnosticDerive<'a> {
190
196
)
191
197
. unwrap_or_else ( |v| v. to_compile_error ( ) )
192
198
} ) ;
193
- return quote ! {
194
- #( #result) ; *
195
- } ;
199
+
200
+ quote ! { #( #result) ; * }
196
201
} ) ;
202
+
203
+ // When generating `set_arg` calls, move data rather than borrow it to avoid
204
+ // requiring clones - this must therefore be the last use of each field (for
205
+ // example, any formatting machinery that might refer to a field should be
206
+ // generated already).
207
+ structure. bind_with ( |_| synstructure:: BindStyle :: Move ) ;
208
+ let args = structure. each ( |field_binding| {
209
+ let field = field_binding. ast ( ) ;
210
+ // When a field has attributes like `#[label]` or `#[note]` then it doesn't
211
+ // need to be passed as an argument to the diagnostic. But when a field has no
212
+ // attributes then it must be passed as an argument to the diagnostic so that
213
+ // it can be referred to by Fluent messages.
214
+ if field. attrs . is_empty ( ) {
215
+ let diag = & builder. diag ;
216
+ let ident = & field_binding. binding ;
217
+ quote ! { #diag. set_arg( stringify!( #ident) , #field_binding. into_diagnostic_arg( ) ) ; }
218
+ } else {
219
+ quote ! { }
220
+ }
221
+ } ) ;
222
+
197
223
// Finally, putting it altogether.
198
224
match builder. kind {
199
225
None => {
@@ -210,7 +236,10 @@ impl<'a> SessionDiagnosticDerive<'a> {
210
236
let mut #diag = #sess. struct_err_with_code( "" , rustc_errors:: DiagnosticId :: Error ( #code) ) ;
211
237
#preamble
212
238
match self {
213
- #body
239
+ #attrs
240
+ }
241
+ match self {
242
+ #args
214
243
}
215
244
#diag
216
245
}
@@ -236,6 +265,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
236
265
self ,
237
266
#sess: & ' __session_diagnostic_sess rustc_session:: Session
238
267
) -> rustc_errors:: DiagnosticBuilder <' __session_diagnostic_sess, rustc_errors:: ErrorGuaranteed > {
268
+ use rustc_errors:: IntoDiagnosticArg ;
239
269
#implementation
240
270
}
241
271
}
@@ -345,15 +375,13 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
345
375
}
346
376
}
347
377
348
- fn generate_field_code (
378
+ fn generate_field_attr_code (
349
379
& mut self ,
350
380
attr : & syn:: Attribute ,
351
381
info : FieldInfo < ' _ > ,
352
382
) -> Result < proc_macro2:: TokenStream , SessionDiagnosticDeriveError > {
353
383
let field_binding = & info. binding . binding ;
354
-
355
384
let option_ty = option_inner_ty ( & info. ty ) ;
356
-
357
385
let generated_code = self . generate_non_option_field_code (
358
386
attr,
359
387
FieldInfo {
@@ -363,15 +391,16 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
363
391
span : info. span ,
364
392
} ,
365
393
) ?;
366
- Ok ( if option_ty. is_none ( ) {
367
- quote ! { #generated_code }
394
+
395
+ if option_ty. is_none ( ) {
396
+ Ok ( quote ! { #generated_code } )
368
397
} else {
369
- quote ! {
398
+ Ok ( quote ! {
370
399
if let Some ( #field_binding) = #field_binding {
371
400
#generated_code
372
401
}
373
- }
374
- } )
402
+ } )
403
+ }
375
404
}
376
405
377
406
fn generate_non_option_field_code (
@@ -383,19 +412,20 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
383
412
let field_binding = & info. binding . binding ;
384
413
let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
385
414
let name = name. as_str ( ) ;
415
+
386
416
// At this point, we need to dispatch based on the attribute key + the
387
417
// type.
388
418
let meta = attr. parse_meta ( ) ?;
389
- Ok ( match meta {
419
+ match meta {
390
420
syn:: Meta :: NameValue ( syn:: MetaNameValue { lit : syn:: Lit :: Str ( s) , .. } ) => {
391
421
let formatted_str = self . build_format ( & s. value ( ) , attr. span ( ) ) ;
392
422
match name {
393
423
"message" => {
394
424
if type_matches_path ( & info. ty , & [ "rustc_span" , "Span" ] ) {
395
- quote ! {
425
+ return Ok ( quote ! {
396
426
#diag. set_span( * #field_binding) ;
397
427
#diag. set_primary_message( #formatted_str) ;
398
- }
428
+ } ) ;
399
429
} else {
400
430
throw_span_err ! (
401
431
attr. span( ) . unwrap( ) ,
@@ -405,9 +435,9 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
405
435
}
406
436
"label" => {
407
437
if type_matches_path ( & info. ty , & [ "rustc_span" , "Span" ] ) {
408
- quote ! {
438
+ return Ok ( quote ! {
409
439
#diag. span_label( * #field_binding, #formatted_str) ;
410
- }
440
+ } ) ;
411
441
} else {
412
442
throw_span_err ! (
413
443
attr. span( ) . unwrap( ) ,
@@ -480,11 +510,11 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
480
510
) ;
481
511
} ;
482
512
let code = code. unwrap_or_else ( || quote ! { String :: new( ) } ) ;
483
- // Now build it out:
513
+
484
514
let suggestion_method = format_ident ! ( "span_{}" , suggestion_kind) ;
485
- quote ! {
515
+ return Ok ( quote ! {
486
516
#diag. #suggestion_method( #span, #msg, #code, #applicability) ;
487
- }
517
+ } ) ;
488
518
}
489
519
other => throw_span_err ! (
490
520
list. span( ) . unwrap( ) ,
@@ -493,7 +523,7 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> {
493
523
}
494
524
}
495
525
_ => panic ! ( "unhandled meta kind" ) ,
496
- } )
526
+ }
497
527
}
498
528
499
529
fn span_and_applicability_of_ty (
0 commit comments