@@ -63,15 +63,6 @@ fn is_derive(attr: &ast::Attribute) -> bool {
63
63
attr. has_name ( sym:: derive)
64
64
}
65
65
66
- /// Returns the arguments of `#[derive(...)]`.
67
- fn get_derive_spans < ' a > ( attr : & ' a ast:: Attribute ) -> Option < impl Iterator < Item = Span > + ' a > {
68
- attr. meta_item_list ( ) . map ( |meta_item_list| {
69
- meta_item_list
70
- . into_iter ( )
71
- . map ( |nested_meta_item| nested_meta_item. span ( ) )
72
- } )
73
- }
74
-
75
66
// The shape of the arguments to a function-like attribute.
76
67
fn argument_shape (
77
68
left : usize ,
@@ -100,36 +91,104 @@ fn argument_shape(
100
91
}
101
92
102
93
fn format_derive (
103
- derive_args : & [ Span ] ,
104
- prefix : & str ,
94
+ derives : & [ ast:: Attribute ] ,
105
95
shape : Shape ,
106
96
context : & RewriteContext < ' _ > ,
107
97
) -> Option < String > {
108
- let mut result = String :: with_capacity ( 128 ) ;
109
- result. push_str ( prefix) ;
110
- result. push_str ( "[derive(" ) ;
111
-
112
- let argument_shape = argument_shape ( 10 + prefix. len ( ) , 2 , false , shape, context) ?;
113
- let item_str = format_arg_list (
114
- derive_args. iter ( ) ,
115
- |_| DUMMY_SP . lo ( ) ,
116
- |_| DUMMY_SP . hi ( ) ,
117
- |sp| Some ( context. snippet ( * * sp) . to_owned ( ) ) ,
118
- DUMMY_SP ,
119
- context,
120
- argument_shape,
121
- // 10 = "[derive()]", 3 = "()" and "]"
122
- shape. offset_left ( 10 + prefix. len ( ) ) ?. sub_width ( 3 ) ?,
123
- None ,
98
+ // Collect all items from all attributes
99
+ let all_items = derives
100
+ . iter ( )
101
+ . map ( |attr| {
102
+ // Parse the derive items and extract the span for each item; if any
103
+ // attribute is not parseable, none of the attributes will be
104
+ // reformatted.
105
+ let item_spans = attr. meta_item_list ( ) . map ( |meta_item_list| {
106
+ meta_item_list
107
+ . into_iter ( )
108
+ . map ( |nested_meta_item| nested_meta_item. span ( ) )
109
+ } ) ?;
110
+
111
+ let items = itemize_list (
112
+ context. snippet_provider ,
113
+ item_spans,
114
+ ")" ,
115
+ "," ,
116
+ |span| span. lo ( ) ,
117
+ |span| span. hi ( ) ,
118
+ |span| Some ( context. snippet ( * span) . to_owned ( ) ) ,
119
+ attr. span . lo ( ) ,
120
+ attr. span . hi ( ) ,
121
+ false ,
122
+ ) ;
123
+
124
+ Some ( items)
125
+ } )
126
+ // Fail if any attribute failed.
127
+ . collect :: < Option < Vec < _ > > > ( ) ?
128
+ // Collect the results into a single, flat, Vec.
129
+ . into_iter ( )
130
+ . flatten ( )
131
+ . collect :: < Vec < _ > > ( ) ;
132
+
133
+ // Collect formatting parameters.
134
+ let prefix = attr_prefix ( & derives[ 0 ] ) ;
135
+ let argument_shape = argument_shape (
136
+ "[derive()]" . len ( ) + prefix. len ( ) ,
137
+ ")]" . len ( ) ,
124
138
false ,
139
+ shape,
140
+ context,
125
141
) ?;
142
+ let one_line_shape = shape
143
+ . offset_left ( "[derive()]" . len ( ) + prefix. len ( ) ) ?
144
+ . sub_width ( "()]" . len ( ) ) ?;
145
+ let one_line_budget = one_line_shape. width ;
126
146
127
- result. push_str ( & item_str) ;
128
- if item_str. starts_with ( '\n' ) {
129
- result. push ( ',' ) ;
147
+ let tactic = definitive_tactic (
148
+ & all_items,
149
+ ListTactic :: HorizontalVertical ,
150
+ Separator :: Comma ,
151
+ argument_shape. width ,
152
+ ) ;
153
+ let trailing_separator = match context. config . indent_style ( ) {
154
+ // We always add the trailing comma and remove it if it is not needed.
155
+ IndentStyle :: Block => SeparatorTactic :: Always ,
156
+ IndentStyle :: Visual => SeparatorTactic :: Never ,
157
+ } ;
158
+
159
+ // Format the collection of items.
160
+ let fmt = ListFormatting :: new ( argument_shape, context. config )
161
+ . tactic ( tactic)
162
+ . trailing_separator ( trailing_separator)
163
+ . ends_with_newline ( false ) ;
164
+ let item_str = write_list ( & all_items, & fmt) ?;
165
+
166
+ debug ! ( "item_str: '{}'" , item_str) ;
167
+
168
+ // Determine if the result will be nested, i.e. if we're using the block
169
+ // indent style and either the items are on multiple lines or we've exceeded
170
+ // our budget to fit on a single line.
171
+ let nested = context. config . indent_style ( ) == IndentStyle :: Block
172
+ && ( item_str. contains ( '\n' ) || item_str. len ( ) > one_line_budget) ;
173
+
174
+ // Format the final result.
175
+ let mut result = String :: with_capacity ( 128 ) ;
176
+ result. push_str ( prefix) ;
177
+ result. push_str ( "[derive(" ) ;
178
+ if nested {
179
+ let nested_indent = argument_shape. indent . to_string_with_newline ( context. config ) ;
180
+ result. push_str ( & nested_indent) ;
181
+ result. push_str ( & item_str) ;
130
182
result. push_str ( & shape. indent . to_string_with_newline ( context. config ) ) ;
183
+ } else if let SeparatorTactic :: Always = context. config . trailing_comma ( ) {
184
+ // Retain the trailing comma.
185
+ result. push_str ( & item_str) ;
186
+ } else {
187
+ // Remove the trailing comma.
188
+ result. push_str ( & item_str[ ..item_str. len ( ) - 1 ] ) ;
131
189
}
132
190
result. push_str ( ")]" ) ;
191
+
133
192
Some ( result)
134
193
}
135
194
@@ -255,7 +314,7 @@ impl Rewrite for ast::MetaItem {
255
314
// width. Since a literal is basically unformattable unless it
256
315
// is a string literal (and only if `format_strings` is set),
257
316
// we might be better off ignoring the fact that the attribute
258
- // is longer than the max width and contiue on formatting.
317
+ // is longer than the max width and continue on formatting.
259
318
// See #2479 for example.
260
319
let value = rewrite_literal ( context, literal, lit_shape)
261
320
. unwrap_or_else ( || context. snippet ( literal. span ) . to_owned ( ) ) ;
@@ -265,61 +324,6 @@ impl Rewrite for ast::MetaItem {
265
324
}
266
325
}
267
326
268
- fn format_arg_list < I , T , F1 , F2 , F3 > (
269
- list : I ,
270
- get_lo : F1 ,
271
- get_hi : F2 ,
272
- get_item_string : F3 ,
273
- span : Span ,
274
- context : & RewriteContext < ' _ > ,
275
- shape : Shape ,
276
- one_line_shape : Shape ,
277
- one_line_limit : Option < usize > ,
278
- combine : bool ,
279
- ) -> Option < String >
280
- where
281
- I : Iterator < Item = T > ,
282
- F1 : Fn ( & T ) -> BytePos ,
283
- F2 : Fn ( & T ) -> BytePos ,
284
- F3 : Fn ( & T ) -> Option < String > ,
285
- {
286
- let items = itemize_list (
287
- context. snippet_provider ,
288
- list,
289
- ")" ,
290
- "," ,
291
- get_lo,
292
- get_hi,
293
- get_item_string,
294
- span. lo ( ) ,
295
- span. hi ( ) ,
296
- false ,
297
- ) ;
298
- let item_vec = items. collect :: < Vec < _ > > ( ) ;
299
- let tactic = if let Some ( limit) = one_line_limit {
300
- ListTactic :: LimitedHorizontalVertical ( limit)
301
- } else {
302
- ListTactic :: HorizontalVertical
303
- } ;
304
-
305
- let tactic = definitive_tactic ( & item_vec, tactic, Separator :: Comma , shape. width ) ;
306
- let fmt = ListFormatting :: new ( shape, context. config )
307
- . tactic ( tactic)
308
- . ends_with_newline ( false ) ;
309
- let item_str = write_list ( & item_vec, & fmt) ?;
310
-
311
- let one_line_budget = one_line_shape. width ;
312
- if context. config . indent_style ( ) == IndentStyle :: Visual
313
- || combine
314
- || ( !item_str. contains ( '\n' ) && item_str. len ( ) <= one_line_budget)
315
- {
316
- Some ( item_str)
317
- } else {
318
- let nested_indent = shape. indent . to_string_with_newline ( context. config ) ;
319
- Some ( format ! ( "{}{}" , nested_indent, item_str) )
320
- }
321
- }
322
-
323
327
impl Rewrite for ast:: Attribute {
324
328
fn rewrite ( & self , context : & RewriteContext < ' _ > , shape : Shape ) -> Option < String > {
325
329
let snippet = context. snippet ( self . span ) ;
@@ -424,13 +428,7 @@ impl<'a> Rewrite for [ast::Attribute] {
424
428
// Handle derives if we will merge them.
425
429
if context. config . merge_derives ( ) && is_derive ( & attrs[ 0 ] ) {
426
430
let derives = take_while_with_pred ( context, attrs, is_derive) ;
427
- let derive_spans: Vec < _ > = derives
428
- . iter ( )
429
- . filter_map ( get_derive_spans)
430
- . flatten ( )
431
- . collect ( ) ;
432
- let derive_str =
433
- format_derive ( & derive_spans, attr_prefix ( & attrs[ 0 ] ) , shape, context) ?;
431
+ let derive_str = format_derive ( derives, shape, context) ?;
434
432
result. push_str ( & derive_str) ;
435
433
436
434
let missing_span = attrs
0 commit comments