@@ -71,91 +71,72 @@ impl<'a> SessionDiagnosticDerive<'a> {
71
71
}
72
72
} ;
73
73
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 ( ) ;
76
76
77
77
// Generates calls to `span_label` and similar functions based on the attributes
78
78
// on fields. Code for suggestions uses formatting machinery and the value of
79
79
// 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.
83
83
let attrs = structure
84
84
. clone ( )
85
- // Remove the fields that have a `subdiagnostic` attribute.
86
85
. 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
+ }
94
97
} )
95
98
. each ( |field_binding| {
96
99
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
+ )
112
111
} ) ;
113
112
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).
118
117
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
+ } ) ;
159
140
160
141
let span = ast. span ( ) . unwrap ( ) ;
161
142
let ( diag, sess) = ( & builder. diag , & builder. sess ) ;
@@ -383,38 +364,59 @@ impl SessionDiagnosticDeriveBuilder {
383
364
Ok ( tokens. drain ( ..) . collect ( ) )
384
365
}
385
366
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 {
391
372
let field_binding = & info. binding . binding ;
392
373
393
374
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
- ) ?;
413
375
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
+ }
416
385
} 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 ( )
418
420
}
419
421
}
420
422
0 commit comments