@@ -211,11 +211,39 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
211211 }
212212}
213213
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+
214244impl < ' 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 ! [ ] ;
219247
220248 for attr in self . variant . ast ( ) . attrs {
221249 let span = attr. span ( ) . unwrap ( ) ;
@@ -362,10 +390,10 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
362390 | SubdiagnosticKind :: MultipartSuggestion { .. } => { }
363391 }
364392
365- kind_slug . set_once ( ( ( kind, slug) , span ) )
393+ kind_slugs . push ( ( kind, slug) )
366394 }
367395
368- Ok ( kind_slug . map ( | ( kind_slug , _ ) | kind_slug ) )
396+ Ok ( kind_slugs )
369397 }
370398
371399 /// Generates the code for a field with no attributes.
@@ -387,7 +415,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
387415 fn generate_field_attr_code (
388416 & mut self ,
389417 binding : & BindingInfo < ' _ > ,
390- kind : & SubdiagnosticKind ,
418+ kind_stats : KindsStatistics ,
391419 ) -> TokenStream {
392420 let ast = binding. ast ( ) ;
393421 assert ! ( ast. attrs. len( ) > 0 , "field without attributes generating attr code" ) ;
@@ -405,7 +433,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
405433 } ;
406434
407435 let generated = self
408- . generate_field_code_inner ( kind , attr, info)
436+ . generate_field_code_inner ( kind_stats , attr, info)
409437 . unwrap_or_else ( |v| v. to_compile_error ( ) ) ;
410438
411439 inner_ty. with ( binding, generated)
@@ -415,15 +443,15 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
415443
416444 fn generate_field_code_inner (
417445 & mut self ,
418- kind : & SubdiagnosticKind ,
446+ kind_stats : KindsStatistics ,
419447 attr : & Attribute ,
420448 info : FieldInfo < ' _ > ,
421449 ) -> Result < TokenStream , DiagnosticDeriveError > {
422450 let meta = attr. parse_meta ( ) ?;
423451 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) ,
425453 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)
427455 }
428456 _ => throw_invalid_attr ! ( attr, & meta) ,
429457 }
@@ -432,7 +460,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
432460 /// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
433461 fn generate_field_code_inner_path (
434462 & mut self ,
435- kind : & SubdiagnosticKind ,
463+ kind_stats : KindsStatistics ,
436464 attr : & Attribute ,
437465 info : FieldInfo < ' _ > ,
438466 path : Path ,
@@ -445,7 +473,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
445473 match name {
446474 "skip_arg" => Ok ( quote ! { } ) ,
447475 "primary_span" => {
448- if matches ! ( kind , SubdiagnosticKind :: MultipartSuggestion { .. } ) {
476+ if kind_stats . has_multipart_suggestion {
449477 throw_invalid_attr ! ( attr, & Meta :: Path ( path) , |diag| {
450478 diag. help(
451479 "multipart suggestions use one or more `#[suggestion_part]`s rather \
@@ -464,32 +492,20 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
464492 "suggestion_part" => {
465493 self . has_suggestion_parts = true ;
466494
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 = \" ...\" `" )
473497 . 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(
483502 "`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead" ,
484503 )
485- } ) ;
486- }
504+ } ) ;
487505 }
488506 }
489507 "applicability" => {
490- if let SubdiagnosticKind :: Suggestion { .. }
491- | SubdiagnosticKind :: MultipartSuggestion { .. } = kind
492- {
508+ if kind_stats. has_multipart_suggestion || kind_stats. has_normal_suggestion {
493509 report_error_if_not_applied_to_applicability ( attr, & info) ?;
494510
495511 let binding = info. binding . binding . clone ( ) ;
@@ -501,13 +517,16 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
501517 Ok ( quote ! { } )
502518 }
503519 _ => 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+ }
509527 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( ", " )
511530 ) )
512531 } ) ,
513532 }
@@ -517,7 +536,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
517536 /// `#[suggestion_part(code = "...")]`).
518537 fn generate_field_code_inner_list (
519538 & mut self ,
520- kind : & SubdiagnosticKind ,
539+ kind_stats : KindsStatistics ,
521540 attr : & Attribute ,
522541 info : FieldInfo < ' _ > ,
523542 list : MetaList ,
@@ -529,7 +548,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
529548
530549 match name {
531550 "suggestion_part" => {
532- if !matches ! ( kind , SubdiagnosticKind :: MultipartSuggestion { .. } ) {
551+ if !kind_stats . has_multipart_suggestion {
533552 throw_invalid_attr ! ( attr, & Meta :: List ( list) , |diag| {
534553 diag. help(
535554 "`#[suggestion_part(...)]` is only valid in multipart suggestions" ,
@@ -576,43 +595,44 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
576595 Ok ( quote ! { suggestions. push( ( #binding, #code) ) ; } )
577596 }
578597 _ => 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+ }
584605 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( ", " )
586608 ) )
587609 } ) ,
588610 }
589611 }
590612
591613 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 ( ) {
593616 throw_span_err ! (
594617 self . variant. ast( ) . ident. span( ) . unwrap( ) ,
595618 "subdiagnostic kind not specified"
596619 ) ;
597620 } ;
598621
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 ! { }
608628 } ;
609629
610630 let attr_args: TokenStream = self
611631 . variant
612632 . bindings ( )
613633 . iter ( )
614634 . 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 ) )
616636 . collect ( ) ;
617637
618638 let span_field = self . span_field . as_ref ( ) . map ( |( span, _) | span) ;
@@ -622,48 +642,52 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
622642 ) ;
623643
624644 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+ }
645659 }
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+ }
646668
647- let style = suggestion_kind. to_suggestion_style ( ) ;
669+ let style = suggestion_kind. to_suggestion_style ( ) ;
648670
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) ; }
657672 }
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+ }
664680 }
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+ }
667691
668692 let plain_args: TokenStream = self
669693 . variant
@@ -676,7 +700,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
676700 Ok ( quote ! {
677701 #init
678702 #attr_args
679- #call
703+ #calls
680704 #plain_args
681705 } )
682706 }
0 commit comments