@@ -71,31 +71,46 @@ impl<'a> SessionDiagnosticDerive<'a> {
7171 }
7272 } ;
7373
74+ // Keep track of which fields are subdiagnostics
75+ let mut subdiagnostics = std:: collections:: HashSet :: new ( ) ;
76+
7477 // Generates calls to `span_label` and similar functions based on the attributes
7578 // on fields. Code for suggestions uses formatting machinery and the value of
7679 // other fields - because any given field can be referenced multiple times, it
7780 // should be accessed through a borrow. When passing fields to `set_arg` (which
7881 // happens below) for Fluent, we want to move the data, so that has to happen
7982 // in a separate pass over the fields.
80- let attrs = structure. each ( |field_binding| {
81- let field = field_binding. ast ( ) ;
82- let result = field. attrs . iter ( ) . map ( |attr| {
83- builder
84- . generate_field_attr_code (
85- attr,
86- FieldInfo {
87- vis : & field. vis ,
88- binding : field_binding,
89- ty : & field. ty ,
90- span : & field. span ( ) ,
91- } ,
92- )
93- . unwrap_or_else ( |v| v. to_compile_error ( ) )
83+ let attrs = structure
84+ . clone ( )
85+ // Remove the fields that have a `subdiagnostic` attribute.
86+ . 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+ } )
94+ } )
95+ . each ( |field_binding| {
96+ 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) ; * }
94112 } ) ;
95113
96- quote ! { #( #result) ; * }
97- } ) ;
98-
99114 // When generating `set_arg` calls, move data rather than borrow it to avoid
100115 // requiring clones - this must therefore be the last use of each field (for
101116 // example, any formatting machinery that might refer to a field should be
@@ -107,7 +122,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
107122 // need to be passed as an argument to the diagnostic. But when a field has no
108123 // attributes then it must be passed as an argument to the diagnostic so that
109124 // it can be referred to by Fluent messages.
110- if field. attrs . is_empty ( ) {
125+ let tokens = if field. attrs . is_empty ( ) {
111126 let diag = & builder. diag ;
112127 let ident = field_binding. ast ( ) . ident . as_ref ( ) . unwrap ( ) ;
113128 quote ! {
@@ -118,6 +133,27 @@ impl<'a> SessionDiagnosticDerive<'a> {
118133 }
119134 } else {
120135 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
121157 }
122158 } ) ;
123159
@@ -359,6 +395,8 @@ impl SessionDiagnosticDeriveBuilder {
359395 let ( binding, needs_destructure) = match ( name. as_str ( ) , & inner_ty) {
360396 // `primary_span` can accept a `Vec<Span>` so don't destructure that.
361397 ( "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 ) ,
362400 _ => ( quote ! { * #field_binding } , true ) ,
363401 } ;
364402
0 commit comments