@@ -211,11 +211,39 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
211
211
}
212
212
}
213
213
214
+ /// Provides frequently-needed information about the diagnostic kinds being derived for this type.
215
+ #[ derive( Clone , Copy , Debug ) ]
216
+ struct KindsStatistics {
217
+ has_multipart_suggestion : bool ,
218
+ all_multipart_suggestions : bool ,
219
+ has_normal_suggestion : bool ,
220
+ }
221
+
222
+ impl < ' a > FromIterator < & ' a SubdiagnosticKind > for KindsStatistics {
223
+ fn from_iter < T : IntoIterator < Item = & ' a SubdiagnosticKind > > ( kinds : T ) -> Self {
224
+ let mut ret = Self {
225
+ has_multipart_suggestion : false ,
226
+ all_multipart_suggestions : true ,
227
+ has_normal_suggestion : false ,
228
+ } ;
229
+ for kind in kinds {
230
+ if let SubdiagnosticKind :: MultipartSuggestion { .. } = kind {
231
+ ret. has_multipart_suggestion = true ;
232
+ } else {
233
+ ret. all_multipart_suggestions = false ;
234
+ }
235
+
236
+ if let SubdiagnosticKind :: Suggestion { .. } = kind {
237
+ ret. has_normal_suggestion = true ;
238
+ }
239
+ }
240
+ ret
241
+ }
242
+ }
243
+
214
244
impl < ' a > SessionSubdiagnosticDeriveBuilder < ' a > {
215
- fn identify_kind (
216
- & mut self ,
217
- ) -> Result < Option < ( SubdiagnosticKind , Path ) > , DiagnosticDeriveError > {
218
- let mut kind_slug = None ;
245
+ fn identify_kind ( & mut self ) -> Result < Vec < ( SubdiagnosticKind , Path ) > , DiagnosticDeriveError > {
246
+ let mut kind_slugs = vec ! [ ] ;
219
247
220
248
for attr in self . variant . ast ( ) . attrs {
221
249
let span = attr. span ( ) . unwrap ( ) ;
@@ -362,10 +390,10 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
362
390
| SubdiagnosticKind :: MultipartSuggestion { .. } => { }
363
391
}
364
392
365
- kind_slug . set_once ( ( ( kind, slug) , span ) )
393
+ kind_slugs . push ( ( kind, slug) )
366
394
}
367
395
368
- Ok ( kind_slug . map ( | ( kind_slug , _ ) | kind_slug ) )
396
+ Ok ( kind_slugs )
369
397
}
370
398
371
399
/// Generates the code for a field with no attributes.
@@ -387,7 +415,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
387
415
fn generate_field_attr_code (
388
416
& mut self ,
389
417
binding : & BindingInfo < ' _ > ,
390
- kind : & SubdiagnosticKind ,
418
+ kind_stats : KindsStatistics ,
391
419
) -> TokenStream {
392
420
let ast = binding. ast ( ) ;
393
421
assert ! ( ast. attrs. len( ) > 0 , "field without attributes generating attr code" ) ;
@@ -405,7 +433,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
405
433
} ;
406
434
407
435
let generated = self
408
- . generate_field_code_inner ( kind , attr, info)
436
+ . generate_field_code_inner ( kind_stats , attr, info)
409
437
. unwrap_or_else ( |v| v. to_compile_error ( ) ) ;
410
438
411
439
inner_ty. with ( binding, generated)
@@ -415,15 +443,15 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
415
443
416
444
fn generate_field_code_inner (
417
445
& mut self ,
418
- kind : & SubdiagnosticKind ,
446
+ kind_stats : KindsStatistics ,
419
447
attr : & Attribute ,
420
448
info : FieldInfo < ' _ > ,
421
449
) -> Result < TokenStream , DiagnosticDeriveError > {
422
450
let meta = attr. parse_meta ( ) ?;
423
451
match meta {
424
- Meta :: Path ( path) => self . generate_field_code_inner_path ( kind , attr, info, path) ,
452
+ Meta :: Path ( path) => self . generate_field_code_inner_path ( kind_stats , attr, info, path) ,
425
453
Meta :: List ( list @ MetaList { .. } ) => {
426
- self . generate_field_code_inner_list ( kind , attr, info, list)
454
+ self . generate_field_code_inner_list ( kind_stats , attr, info, list)
427
455
}
428
456
_ => throw_invalid_attr ! ( attr, & meta) ,
429
457
}
@@ -432,7 +460,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
432
460
/// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
433
461
fn generate_field_code_inner_path (
434
462
& mut self ,
435
- kind : & SubdiagnosticKind ,
463
+ kind_stats : KindsStatistics ,
436
464
attr : & Attribute ,
437
465
info : FieldInfo < ' _ > ,
438
466
path : Path ,
@@ -445,7 +473,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
445
473
match name {
446
474
"skip_arg" => Ok ( quote ! { } ) ,
447
475
"primary_span" => {
448
- if matches ! ( kind , SubdiagnosticKind :: MultipartSuggestion { .. } ) {
476
+ if kind_stats . has_multipart_suggestion {
449
477
throw_invalid_attr ! ( attr, & Meta :: Path ( path) , |diag| {
450
478
diag. help(
451
479
"multipart suggestions use one or more `#[suggestion_part]`s rather \
@@ -464,32 +492,20 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
464
492
"suggestion_part" => {
465
493
self . has_suggestion_parts = true ;
466
494
467
- match kind {
468
- SubdiagnosticKind :: MultipartSuggestion { .. } => {
469
- span_err (
470
- span,
471
- "`#[suggestion_part(...)]` attribute without `code = \" ...\" `" ,
472
- )
495
+ if kind_stats. has_multipart_suggestion {
496
+ span_err ( span, "`#[suggestion_part(...)]` attribute without `code = \" ...\" `" )
473
497
. emit ( ) ;
474
- Ok ( quote ! { } )
475
- }
476
- SubdiagnosticKind :: Label
477
- | SubdiagnosticKind :: Note
478
- | SubdiagnosticKind :: Help
479
- | SubdiagnosticKind :: Warn
480
- | SubdiagnosticKind :: Suggestion { .. } => {
481
- throw_invalid_attr ! ( attr, & Meta :: Path ( path) , |diag| {
482
- diag. help(
498
+ Ok ( quote ! { } )
499
+ } else {
500
+ throw_invalid_attr ! ( attr, & Meta :: Path ( path) , |diag| {
501
+ diag. help(
483
502
"`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead" ,
484
503
)
485
- } ) ;
486
- }
504
+ } ) ;
487
505
}
488
506
}
489
507
"applicability" => {
490
- if let SubdiagnosticKind :: Suggestion { .. }
491
- | SubdiagnosticKind :: MultipartSuggestion { .. } = kind
492
- {
508
+ if kind_stats. has_multipart_suggestion || kind_stats. has_normal_suggestion {
493
509
report_error_if_not_applied_to_applicability ( attr, & info) ?;
494
510
495
511
let binding = info. binding . binding . clone ( ) ;
@@ -501,13 +517,16 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
501
517
Ok ( quote ! { } )
502
518
}
503
519
_ => throw_invalid_attr ! ( attr, & Meta :: Path ( path) , |diag| {
504
- let span_attr = if let SubdiagnosticKind :: MultipartSuggestion { .. } = kind {
505
- "suggestion_part"
506
- } else {
507
- "primary_span"
508
- } ;
520
+ let mut span_attrs = vec![ ] ;
521
+ if kind_stats. has_multipart_suggestion {
522
+ span_attrs. push( "suggestion_part" ) ;
523
+ }
524
+ if !kind_stats. all_multipart_suggestions {
525
+ span_attrs. push( "primary_span" )
526
+ }
509
527
diag. help( format!(
510
- "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes" ,
528
+ "only `{}`, `applicability` and `skip_arg` are valid field attributes" ,
529
+ span_attrs. join( ", " )
511
530
) )
512
531
} ) ,
513
532
}
@@ -517,7 +536,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
517
536
/// `#[suggestion_part(code = "...")]`).
518
537
fn generate_field_code_inner_list (
519
538
& mut self ,
520
- kind : & SubdiagnosticKind ,
539
+ kind_stats : KindsStatistics ,
521
540
attr : & Attribute ,
522
541
info : FieldInfo < ' _ > ,
523
542
list : MetaList ,
@@ -529,7 +548,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
529
548
530
549
match name {
531
550
"suggestion_part" => {
532
- if !matches ! ( kind , SubdiagnosticKind :: MultipartSuggestion { .. } ) {
551
+ if !kind_stats . has_multipart_suggestion {
533
552
throw_invalid_attr ! ( attr, & Meta :: List ( list) , |diag| {
534
553
diag. help(
535
554
"`#[suggestion_part(...)]` is only valid in multipart suggestions" ,
@@ -576,43 +595,44 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
576
595
Ok ( quote ! { suggestions. push( ( #binding, #code) ) ; } )
577
596
}
578
597
_ => throw_invalid_attr ! ( attr, & Meta :: List ( list) , |diag| {
579
- let span_attr = if let SubdiagnosticKind :: MultipartSuggestion { .. } = kind {
580
- "suggestion_part"
581
- } else {
582
- "primary_span"
583
- } ;
598
+ let mut span_attrs = vec![ ] ;
599
+ if kind_stats. has_multipart_suggestion {
600
+ span_attrs. push( "suggestion_part" ) ;
601
+ }
602
+ if !kind_stats. all_multipart_suggestions {
603
+ span_attrs. push( "primary_span" )
604
+ }
584
605
diag. help( format!(
585
- "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes" ,
606
+ "only `{}`, `applicability` and `skip_arg` are valid field attributes" ,
607
+ span_attrs. join( ", " )
586
608
) )
587
609
} ) ,
588
610
}
589
611
}
590
612
591
613
pub fn into_tokens ( & mut self ) -> Result < TokenStream , DiagnosticDeriveError > {
592
- let Some ( ( kind, slug) ) = self . identify_kind ( ) ? else {
614
+ let kind_slugs = self . identify_kind ( ) ?;
615
+ if kind_slugs. is_empty ( ) {
593
616
throw_span_err ! (
594
617
self . variant. ast( ) . ident. span( ) . unwrap( ) ,
595
618
"subdiagnostic kind not specified"
596
619
) ;
597
620
} ;
598
621
599
- let init = match & kind {
600
- SubdiagnosticKind :: Label
601
- | SubdiagnosticKind :: Note
602
- | SubdiagnosticKind :: Help
603
- | SubdiagnosticKind :: Warn
604
- | SubdiagnosticKind :: Suggestion { .. } => quote ! { } ,
605
- SubdiagnosticKind :: MultipartSuggestion { .. } => {
606
- quote ! { let mut suggestions = Vec :: new( ) ; }
607
- }
622
+ let kind_stats: KindsStatistics = kind_slugs. iter ( ) . map ( |( kind, _slug) | kind) . collect ( ) ;
623
+
624
+ let init = if kind_stats. has_multipart_suggestion {
625
+ quote ! { let mut suggestions = Vec :: new( ) ; }
626
+ } else {
627
+ quote ! { }
608
628
} ;
609
629
610
630
let attr_args: TokenStream = self
611
631
. variant
612
632
. bindings ( )
613
633
. iter ( )
614
634
. filter ( |binding| !binding. ast ( ) . attrs . is_empty ( ) )
615
- . map ( |binding| self . generate_field_attr_code ( binding, & kind ) )
635
+ . map ( |binding| self . generate_field_attr_code ( binding, kind_stats ) )
616
636
. collect ( ) ;
617
637
618
638
let span_field = self . span_field . as_ref ( ) . map ( |( span, _) | span) ;
@@ -622,48 +642,52 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
622
642
) ;
623
643
624
644
let diag = & self . diag ;
625
- let name = format_ident ! ( "{}{}" , if span_field. is_some( ) { "span_" } else { "" } , kind) ;
626
- let message = quote ! { rustc_errors:: fluent:: #slug } ;
627
- let call = match kind {
628
- SubdiagnosticKind :: Suggestion { suggestion_kind, code } => {
629
- if let Some ( span) = span_field {
630
- let style = suggestion_kind. to_suggestion_style ( ) ;
631
-
632
- quote ! { #diag. #name( #span, #message, #code, #applicability, #style) ; }
633
- } else {
634
- span_err ( self . span , "suggestion without `#[primary_span]` field" ) . emit ( ) ;
635
- quote ! { unreachable!( ) ; }
636
- }
637
- }
638
- SubdiagnosticKind :: MultipartSuggestion { suggestion_kind } => {
639
- if !self . has_suggestion_parts {
640
- span_err (
641
- self . span ,
642
- "multipart suggestion without any `#[suggestion_part(...)]` fields" ,
643
- )
644
- . emit ( ) ;
645
+ let mut calls = TokenStream :: new ( ) ;
646
+ for ( kind, slug) in kind_slugs {
647
+ let name = format_ident ! ( "{}{}" , if span_field. is_some( ) { "span_" } else { "" } , kind) ;
648
+ let message = quote ! { rustc_errors:: fluent:: #slug } ;
649
+ let call = match kind {
650
+ SubdiagnosticKind :: Suggestion { suggestion_kind, code } => {
651
+ if let Some ( span) = span_field {
652
+ let style = suggestion_kind. to_suggestion_style ( ) ;
653
+
654
+ quote ! { #diag. #name( #span, #message, #code, #applicability, #style) ; }
655
+ } else {
656
+ span_err ( self . span , "suggestion without `#[primary_span]` field" ) . emit ( ) ;
657
+ quote ! { unreachable!( ) ; }
658
+ }
645
659
}
660
+ SubdiagnosticKind :: MultipartSuggestion { suggestion_kind } => {
661
+ if !self . has_suggestion_parts {
662
+ span_err (
663
+ self . span ,
664
+ "multipart suggestion without any `#[suggestion_part(...)]` fields" ,
665
+ )
666
+ . emit ( ) ;
667
+ }
646
668
647
- let style = suggestion_kind. to_suggestion_style ( ) ;
669
+ let style = suggestion_kind. to_suggestion_style ( ) ;
648
670
649
- quote ! { #diag. #name( #message, suggestions, #applicability, #style) ; }
650
- }
651
- SubdiagnosticKind :: Label => {
652
- if let Some ( span) = span_field {
653
- quote ! { #diag. #name( #span, #message) ; }
654
- } else {
655
- span_err ( self . span , "label without `#[primary_span]` field" ) . emit ( ) ;
656
- quote ! { unreachable!( ) ; }
671
+ quote ! { #diag. #name( #message, suggestions, #applicability, #style) ; }
657
672
}
658
- }
659
- _ => {
660
- if let Some ( span) = span_field {
661
- quote ! { #diag. #name( #span, #message) ; }
662
- } else {
663
- quote ! { #diag. #name( #message) ; }
673
+ SubdiagnosticKind :: Label => {
674
+ if let Some ( span) = span_field {
675
+ quote ! { #diag. #name( #span, #message) ; }
676
+ } else {
677
+ span_err ( self . span , "label without `#[primary_span]` field" ) . emit ( ) ;
678
+ quote ! { unreachable!( ) ; }
679
+ }
664
680
}
665
- }
666
- } ;
681
+ _ => {
682
+ if let Some ( span) = span_field {
683
+ quote ! { #diag. #name( #span, #message) ; }
684
+ } else {
685
+ quote ! { #diag. #name( #message) ; }
686
+ }
687
+ }
688
+ } ;
689
+ calls. extend ( call) ;
690
+ }
667
691
668
692
let plain_args: TokenStream = self
669
693
. variant
@@ -676,7 +700,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
676
700
Ok ( quote ! {
677
701
#init
678
702
#attr_args
679
- #call
703
+ #calls
680
704
#plain_args
681
705
} )
682
706
}
0 commit comments