@@ -71,91 +71,72 @@ impl<'a> SessionDiagnosticDerive<'a> {
7171 }
7272 } ;
7373
74- // Keep track of which fields are subdiagnostics
75- let mut subdiagnostics = std:: collections:: HashSet :: new ( ) ;
74+ // Keep track of which fields are subdiagnostics or have no attributes.
75+ let mut subdiagnostics_or_empty = std:: collections:: HashSet :: new ( ) ;
7676
7777 // Generates calls to `span_label` and similar functions based on the attributes
7878 // on fields. Code for suggestions uses formatting machinery and the value of
7979 // other fields - because any given field can be referenced multiple times, it
80- // should be accessed through a borrow. When passing fields to `set_arg` (which
81- // happens below) for Fluent, we want to move the data, so that has to happen
82- // in a separate pass over the fields.
80+ // should be accessed through a borrow. When passing fields to `add_subdiagnostic`
81+ // or `set_arg` (which happens below) for Fluent, we want to move the data, so that
82+ // has to happen in a separate pass over the fields.
8383 let attrs = structure
8484 . clone ( )
85- // Remove the fields that have a `subdiagnostic` attribute.
8685 . filter ( |field_binding| {
87- field_binding. ast ( ) . attrs . iter ( ) . all ( |attr| {
88- "subdiagnostic" != attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( )
89- || {
90- subdiagnostics. insert ( field_binding. binding . clone ( ) ) ;
91- false
92- }
93- } )
86+ let attrs = & field_binding. ast ( ) . attrs ;
87+
88+ ( !attrs. is_empty ( )
89+ && attrs. iter ( ) . all ( |attr| {
90+ "subdiagnostic"
91+ != attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( )
92+ } ) )
93+ || {
94+ subdiagnostics_or_empty. insert ( field_binding. binding . clone ( ) ) ;
95+ false
96+ }
9497 } )
9598 . each ( |field_binding| {
9699 let field = field_binding. ast ( ) ;
97- let result = field. attrs . iter ( ) . map ( |attr| {
98- builder
99- . generate_field_attr_code (
100- attr,
101- FieldInfo {
102- vis : & field. vis ,
103- binding : field_binding,
104- ty : & field. ty ,
105- span : & field. span ( ) ,
106- } ,
107- )
108- . unwrap_or_else ( |v| v. to_compile_error ( ) )
109- } ) ;
110-
111- quote ! { #( #result) ; * }
100+ let field_span = field. span ( ) ;
101+
102+ builder. generate_field_attrs_code (
103+ & field. attrs ,
104+ FieldInfo {
105+ vis : & field. vis ,
106+ binding : field_binding,
107+ ty : & field. ty ,
108+ span : & field_span,
109+ } ,
110+ )
112111 } ) ;
113112
114- // When generating `set_arg` calls, move data rather than borrow it to avoid
115- // requiring clones - this must therefore be the last use of each field (for
116- // example, any formatting machinery that might refer to a field should be
117- // generated already).
113+ // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than
114+ // borrow it to avoid requiring clones - this must therefore be the last use of
115+ // each field (for example, any formatting machinery that might refer to a field
116+ // should be generated already).
118117 structure. bind_with ( |_| synstructure:: BindStyle :: Move ) ;
119- let args = structure. each ( |field_binding| {
120- let field = field_binding. ast ( ) ;
121- // When a field has attributes like `#[label]` or `#[note]` then it doesn't
122- // need to be passed as an argument to the diagnostic. But when a field has no
123- // attributes then it must be passed as an argument to the diagnostic so that
124- // it can be referred to by Fluent messages.
125- let tokens = if field. attrs . is_empty ( ) {
126- let diag = & builder. diag ;
127- let ident = field_binding. ast ( ) . ident . as_ref ( ) . unwrap ( ) ;
128- quote ! {
129- #diag. set_arg(
130- stringify!( #ident) ,
131- #field_binding
132- ) ;
133- }
134- } else {
135- quote ! { }
136- } ;
137- // If this field had a subdiagnostic attribute, we generate the code here to
138- // avoid binding it twice.
139- if subdiagnostics. contains ( & field_binding. binding ) {
140- let result = field. attrs . iter ( ) . map ( |attr| {
141- builder
142- . generate_field_attr_code (
143- attr,
144- FieldInfo {
145- vis : & field. vis ,
146- binding : field_binding,
147- ty : & field. ty ,
148- span : & field. span ( ) ,
149- } ,
150- )
151- . unwrap_or_else ( |v| v. to_compile_error ( ) )
152- } ) ;
153-
154- quote ! { #( #result) ; * #tokens }
155- } else {
156- tokens
157- }
158- } ) ;
118+ // When a field has attributes like `#[label]` or `#[note]` then it doesn't
119+ // need to be passed as an argument to the diagnostic. But when a field has no
120+ // attributes or a `#[subdiagnostic]` attribute then it must be passed as an
121+ // argument to the diagnostic so that it can be referred to by Fluent messages.
122+ let args = structure
123+ . filter ( |field_binding| {
124+ subdiagnostics_or_empty. contains ( & field_binding. binding )
125+ } )
126+ . each ( |field_binding| {
127+ let field = field_binding. ast ( ) ;
128+ let field_span = field. span ( ) ;
129+
130+ builder. generate_field_attrs_code (
131+ & field. attrs ,
132+ FieldInfo {
133+ vis : & field. vis ,
134+ binding : field_binding,
135+ ty : & field. ty ,
136+ span : & field_span,
137+ } ,
138+ )
139+ } ) ;
159140
160141 let span = ast. span ( ) . unwrap ( ) ;
161142 let ( diag, sess) = ( & builder. diag , & builder. sess ) ;
@@ -383,38 +364,59 @@ impl SessionDiagnosticDeriveBuilder {
383364 Ok ( tokens. drain ( ..) . collect ( ) )
384365 }
385366
386- fn generate_field_attr_code (
387- & mut self ,
388- attr : & syn:: Attribute ,
389- info : FieldInfo < ' _ > ,
390- ) -> Result < TokenStream , SessionDiagnosticDeriveError > {
367+ fn generate_field_attrs_code < ' a > (
368+ & ' a mut self ,
369+ attrs : & ' a [ syn:: Attribute ] ,
370+ info : FieldInfo < ' a > ,
371+ ) -> TokenStream {
391372 let field_binding = & info. binding . binding ;
392373
393374 let inner_ty = FieldInnerTy :: from_type ( & info. ty ) ;
394- let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
395- let ( binding, needs_destructure) = match ( name. as_str ( ) , & inner_ty) {
396- // `primary_span` can accept a `Vec<Span>` so don't destructure that.
397- ( "primary_span" , FieldInnerTy :: Vec ( _) ) => ( quote ! { #field_binding. clone( ) } , false ) ,
398- // `subdiagnostics` are not derefed because they are bound by value.
399- ( "subdiagnostic" , _) => ( quote ! { #field_binding } , true ) ,
400- _ => ( quote ! { * #field_binding } , true ) ,
401- } ;
402-
403- let generated_code = self . generate_inner_field_code (
404- attr,
405- FieldInfo {
406- vis : info. vis ,
407- binding : info. binding ,
408- ty : inner_ty. inner_type ( ) . unwrap_or ( & info. ty ) ,
409- span : info. span ,
410- } ,
411- binding,
412- ) ?;
413375
414- if needs_destructure {
415- Ok ( inner_ty. with ( field_binding, generated_code) )
376+ if attrs. is_empty ( ) {
377+ let diag = & self . diag ;
378+ let ident = info. binding . ast ( ) . ident . as_ref ( ) . unwrap ( ) ;
379+ quote ! {
380+ #diag. set_arg(
381+ stringify!( #ident) ,
382+ #field_binding
383+ ) ;
384+ }
416385 } else {
417- Ok ( generated_code)
386+ attrs
387+ . iter ( )
388+ . map ( move |attr| {
389+ let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
390+ let ( binding, needs_destructure) = match ( name. as_str ( ) , & inner_ty) {
391+ // `primary_span` can accept a `Vec<Span>` so don't destructure that.
392+ ( "primary_span" , FieldInnerTy :: Vec ( _) ) => {
393+ ( quote ! { #field_binding. clone( ) } , false )
394+ }
395+ // `subdiagnostics` are not derefed because they are bound by value.
396+ ( "subdiagnostic" , _) => ( quote ! { #field_binding } , true ) ,
397+ _ => ( quote ! { * #field_binding } , true ) ,
398+ } ;
399+
400+ let generated_code = self
401+ . generate_inner_field_code (
402+ attr,
403+ FieldInfo {
404+ vis : info. vis ,
405+ binding : info. binding ,
406+ ty : inner_ty. inner_type ( ) . unwrap_or ( & info. ty ) ,
407+ span : info. span ,
408+ } ,
409+ binding,
410+ )
411+ . unwrap_or_else ( |v| v. to_compile_error ( ) ) ;
412+
413+ if needs_destructure {
414+ inner_ty. with ( field_binding, generated_code)
415+ } else {
416+ generated_code
417+ }
418+ } )
419+ . collect ( )
418420 }
419421 }
420422
0 commit comments