@@ -63,15 +63,6 @@ fn is_derive(attr: &ast::Attribute) -> bool {
6363 attr. has_name ( sym:: derive)
6464}
6565
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-
7566// The shape of the arguments to a function-like attribute.
7667fn argument_shape (
7768 left : usize ,
@@ -100,36 +91,104 @@ fn argument_shape(
10091}
10192
10293fn format_derive (
103- derive_args : & [ Span ] ,
104- prefix : & str ,
94+ derives : & [ ast:: Attribute ] ,
10595 shape : Shape ,
10696 context : & RewriteContext < ' _ > ,
10797) -> 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 ( ) ,
124138 false ,
139+ shape,
140+ context,
125141 ) ?;
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 ;
126146
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) ;
130182 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 ] ) ;
131189 }
132190 result. push_str ( ")]" ) ;
191+
133192 Some ( result)
134193}
135194
@@ -255,7 +314,7 @@ impl Rewrite for ast::MetaItem {
255314 // width. Since a literal is basically unformattable unless it
256315 // is a string literal (and only if `format_strings` is set),
257316 // 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.
259318 // See #2479 for example.
260319 let value = rewrite_literal ( context, literal, lit_shape)
261320 . unwrap_or_else ( || context. snippet ( literal. span ) . to_owned ( ) ) ;
@@ -265,61 +324,6 @@ impl Rewrite for ast::MetaItem {
265324 }
266325}
267326
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-
323327impl Rewrite for ast:: Attribute {
324328 fn rewrite ( & self , context : & RewriteContext < ' _ > , shape : Shape ) -> Option < String > {
325329 let snippet = context. snippet ( self . span ) ;
@@ -424,13 +428,7 @@ impl<'a> Rewrite for [ast::Attribute] {
424428 // Handle derives if we will merge them.
425429 if context. config . merge_derives ( ) && is_derive ( & attrs[ 0 ] ) {
426430 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) ?;
434432 result. push_str ( & derive_str) ;
435433
436434 let missing_span = attrs
0 commit comments