11#![ deny( unused_must_use) ]
22
3+ use std:: collections:: HashSet ;
4+
35use proc_macro2:: { Ident , Span , TokenStream } ;
46use quote:: { format_ident, quote, quote_spanned} ;
57use syn:: parse:: ParseStream ;
5254 formatting_init : TokenStream :: new ( ) ,
5355 message : None ,
5456 code : None ,
57+ used_fields : HashSet :: new ( ) ,
5558 } ;
5659 f ( builder, variant)
5760 } ) ;
@@ -83,6 +86,8 @@ pub(crate) struct DiagnosticDeriveVariantBuilder {
8386 /// Error codes are a optional part of the struct attribute - this is only set to detect
8487 /// multiple specifications.
8588 pub code : SpannedOption < ( ) > ,
89+
90+ pub used_fields : HashSet < proc_macro2:: Ident > ,
8691}
8792
8893impl DiagnosticDeriveVariantBuilder {
@@ -107,8 +112,7 @@ impl DiagnosticDeriveVariantBuilder {
107112 let ast = variant. ast ( ) ;
108113 let attrs = & ast. attrs ;
109114 let preamble = attrs. iter ( ) . map ( |attr| {
110- self . generate_structure_code_for_attr ( attr, variant)
111- . unwrap_or_else ( |v| v. to_compile_error ( ) )
115+ self . generate_structure_code_for_attr ( attr) . unwrap_or_else ( |v| v. to_compile_error ( ) )
112116 } ) ;
113117
114118 quote ! {
@@ -120,23 +124,34 @@ impl DiagnosticDeriveVariantBuilder {
120124 /// calls to `arg` when no attributes are present.
121125 pub ( crate ) fn body ( & mut self , variant : & VariantInfo < ' _ > ) -> TokenStream {
122126 let mut body = quote ! { } ;
127+ let mut second_part = quote ! { } ;
128+
129+ // Subdiagnostic additions.
130+ for binding in variant. bindings ( ) . iter ( ) . filter ( |bi| !should_generate_arg ( bi. ast ( ) ) ) {
131+ second_part. extend ( self . generate_field_attrs_code ( binding) ) ;
132+ }
123133 // Generate `arg` calls first..
124134 for binding in variant. bindings ( ) . iter ( ) . filter ( |bi| should_generate_arg ( bi. ast ( ) ) ) {
125- body. extend ( self . generate_field_code ( binding) ) ;
126- }
127- // ..and then subdiagnostic additions.
128- for binding in variant. bindings ( ) . iter ( ) . filter ( |bi| !should_generate_arg ( bi. ast ( ) ) ) {
129- body. extend ( self . generate_field_attrs_code ( binding, variant) ) ;
135+ if self . is_used_in_message ( binding) {
136+ body. extend ( self . generate_field_code ( binding) ) ;
137+ }
130138 }
139+ body. extend ( second_part) ;
131140 body
132141 }
133142
143+ fn is_used_in_message ( & self , binding : & BindingInfo < ' _ > ) -> bool {
144+ binding. ast ( ) . ident . as_ref ( ) . is_some_and ( |ident| self . used_fields . contains ( ident) )
145+ }
146+
134147 /// Parse a `SubdiagnosticKind` from an `Attribute`.
135148 fn parse_subdiag_attribute (
136- & self ,
149+ & mut self ,
137150 attr : & Attribute ,
138151 ) -> Result < Option < ( SubdiagnosticKind , Message , bool ) > , DiagnosticDeriveError > {
139- let Some ( subdiag) = SubdiagnosticVariant :: from_attr ( attr, & self . field_map ) ? else {
152+ let Some ( subdiag) =
153+ SubdiagnosticVariant :: from_attr ( attr, & self . field_map , & mut self . used_fields ) ?
154+ else {
140155 // Some attributes aren't errors - like documentation comments - but also aren't
141156 // subdiagnostics.
142157 return Ok ( None ) ;
@@ -160,7 +175,6 @@ impl DiagnosticDeriveVariantBuilder {
160175 fn generate_structure_code_for_attr (
161176 & mut self ,
162177 attr : & Attribute ,
163- variant : & VariantInfo < ' _ > ,
164178 ) -> Result < TokenStream , DiagnosticDeriveError > {
165179 // Always allow documentation comments.
166180 if is_doc_comment ( attr) {
@@ -183,11 +197,14 @@ impl DiagnosticDeriveVariantBuilder {
183197 )
184198 . emit ( ) ;
185199 }
186- self . message = Some ( Message {
187- attr_span : attr. span ( ) ,
188- message_span : message. span ( ) ,
189- value : message. value ( ) ,
190- } ) ;
200+ let message = Message :: new (
201+ attr. span ( ) ,
202+ message. span ( ) ,
203+ message. value ( ) ,
204+ & self . field_map ,
205+ & mut self . used_fields ,
206+ ) ;
207+ self . message = Some ( message) ;
191208 }
192209
193210 // Parse arguments
@@ -240,7 +257,7 @@ impl DiagnosticDeriveVariantBuilder {
240257 | SubdiagnosticKind :: NoteOnce
241258 | SubdiagnosticKind :: Help
242259 | SubdiagnosticKind :: HelpOnce
243- | SubdiagnosticKind :: Warn => Ok ( self . add_subdiagnostic ( & fn_ident, message, variant ) ) ,
260+ | SubdiagnosticKind :: Warn => Ok ( self . add_subdiagnostic ( & fn_ident, message) ) ,
244261 SubdiagnosticKind :: Label | SubdiagnosticKind :: Suggestion { .. } => {
245262 throw_invalid_attr ! ( attr, |diag| diag
246263 . help( "`#[label]` and `#[suggestion]` can only be applied to fields" ) ) ;
@@ -268,11 +285,7 @@ impl DiagnosticDeriveVariantBuilder {
268285 }
269286 }
270287
271- fn generate_field_attrs_code (
272- & mut self ,
273- binding_info : & BindingInfo < ' _ > ,
274- variant : & VariantInfo < ' _ > ,
275- ) -> TokenStream {
288+ fn generate_field_attrs_code ( & mut self , binding_info : & BindingInfo < ' _ > ) -> TokenStream {
276289 let field = binding_info. ast ( ) ;
277290 let field_binding = & binding_info. binding ;
278291
@@ -311,7 +324,6 @@ impl DiagnosticDeriveVariantBuilder {
311324 attr,
312325 FieldInfo { binding : binding_info, ty : inner_ty, span : & field. span ( ) } ,
313326 binding,
314- variant
315327 )
316328 . unwrap_or_else ( |v| v. to_compile_error ( ) ) ;
317329
@@ -329,14 +341,10 @@ impl DiagnosticDeriveVariantBuilder {
329341 attr : & Attribute ,
330342 info : FieldInfo < ' _ > ,
331343 binding : TokenStream ,
332- variant : & VariantInfo < ' _ > ,
333344 ) -> Result < TokenStream , DiagnosticDeriveError > {
334345 let ident = & attr. path ( ) . segments . last ( ) . unwrap ( ) . ident ;
335346 let name = ident. to_string ( ) ;
336347 match ( & attr. meta , name. as_str ( ) ) {
337- // Don't need to do anything - by virtue of the attribute existing, the
338- // `arg` call will not be generated.
339- ( Meta :: Path ( _) , "skip_arg" ) => return Ok ( quote ! { } ) ,
340348 ( Meta :: Path ( _) , "primary_span" ) => {
341349 report_error_if_not_applied_to_span ( attr, & info) ?;
342350
@@ -359,7 +367,7 @@ impl DiagnosticDeriveVariantBuilder {
359367 match subdiag {
360368 SubdiagnosticKind :: Label => {
361369 report_error_if_not_applied_to_span ( attr, & info) ?;
362- Ok ( self . add_spanned_subdiagnostic ( binding, & fn_ident, message, variant ) )
370+ Ok ( self . add_spanned_subdiagnostic ( binding, & fn_ident, message) )
363371 }
364372 SubdiagnosticKind :: Note
365373 | SubdiagnosticKind :: NoteOnce
@@ -370,11 +378,11 @@ impl DiagnosticDeriveVariantBuilder {
370378 if type_matches_path ( inner, & [ "rustc_span" , "Span" ] )
371379 || type_matches_path ( inner, & [ "rustc_span" , "MultiSpan" ] )
372380 {
373- Ok ( self . add_spanned_subdiagnostic ( binding, & fn_ident, message, variant ) )
381+ Ok ( self . add_spanned_subdiagnostic ( binding, & fn_ident, message) )
374382 } else if type_is_unit ( inner)
375383 || ( matches ! ( info. ty, FieldInnerTy :: Plain ( _) ) && type_is_bool ( inner) )
376384 {
377- Ok ( self . add_subdiagnostic ( & fn_ident, message, variant ) )
385+ Ok ( self . add_subdiagnostic ( & fn_ident, message) )
378386 } else {
379387 report_type_error ( attr, "`Span`, `MultiSpan`, `bool` or `()`" ) ?
380388 }
@@ -400,7 +408,7 @@ impl DiagnosticDeriveVariantBuilder {
400408 applicability. set_once ( quote ! { #static_applicability } , span) ;
401409 }
402410
403- let message = message. diag_message ( Some ( variant ) ) ;
411+ let message = message. diag_message ( ) ;
404412 let applicability = applicability
405413 . value ( )
406414 . unwrap_or_else ( || quote ! { rustc_errors:: Applicability :: Unspecified } ) ;
@@ -428,10 +436,9 @@ impl DiagnosticDeriveVariantBuilder {
428436 field_binding : TokenStream ,
429437 kind : & Ident ,
430438 message : Message ,
431- variant : & VariantInfo < ' _ > ,
432439 ) -> TokenStream {
433440 let fn_name = format_ident ! ( "span_{}" , kind) ;
434- let message = message. diag_message ( Some ( variant ) ) ;
441+ let message = message. diag_message ( ) ;
435442 quote ! {
436443 diag. #fn_name(
437444 #field_binding,
@@ -442,13 +449,8 @@ impl DiagnosticDeriveVariantBuilder {
442449
443450 /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current message
444451 /// and `fluent_attr_identifier`.
445- fn add_subdiagnostic (
446- & self ,
447- kind : & Ident ,
448- message : Message ,
449- variant : & VariantInfo < ' _ > ,
450- ) -> TokenStream {
451- let message = message. diag_message ( Some ( variant) ) ;
452+ fn add_subdiagnostic ( & self , kind : & Ident , message : Message ) -> TokenStream {
453+ let message = message. diag_message ( ) ;
452454 quote ! {
453455 diag. #kind( #message) ;
454456 }
0 commit comments