22
33use rustc_ast:: ast;
44use rustc_ast:: attr:: HasAttrs ;
5- use rustc_span:: { symbol:: sym, BytePos , Span , DUMMY_SP } ;
5+ use rustc_span:: { symbol:: sym, Span } ;
66
77use self :: doc_comment:: DocCommentFormatter ;
88use crate :: comment:: { contains_comment, rewrite_doc_comment, CommentStyle } ;
@@ -52,15 +52,6 @@ fn is_derive(attr: &ast::Attribute) -> bool {
5252 attr. check_name ( sym:: derive)
5353}
5454
55- /// Returns the arguments of `#[derive(...)]`.
56- fn get_derive_spans < ' a > ( attr : & ' a ast:: Attribute ) -> Option < impl Iterator < Item = Span > + ' a > {
57- attr. meta_item_list ( ) . map ( |meta_item_list| {
58- meta_item_list
59- . into_iter ( )
60- . map ( |nested_meta_item| nested_meta_item. span ( ) )
61- } )
62- }
63-
6455// The shape of the arguments to a function-like attribute.
6556fn argument_shape (
6657 left : usize ,
@@ -89,36 +80,104 @@ fn argument_shape(
8980}
9081
9182fn format_derive (
92- derive_args : & [ Span ] ,
93- prefix : & str ,
83+ derives : & [ ast:: Attribute ] ,
9484 shape : Shape ,
9585 context : & RewriteContext < ' _ > ,
9686) -> Option < String > {
97- let mut result = String :: with_capacity ( 128 ) ;
98- result. push_str ( prefix) ;
99- result. push_str ( "[derive(" ) ;
100-
101- let argument_shape = argument_shape ( 10 + prefix. len ( ) , 2 , false , shape, context) ?;
102- let item_str = format_arg_list (
103- derive_args. iter ( ) ,
104- |_| DUMMY_SP . lo ( ) ,
105- |_| DUMMY_SP . hi ( ) ,
106- |sp| Some ( context. snippet ( * * sp) . to_owned ( ) ) ,
107- DUMMY_SP ,
108- context,
109- argument_shape,
110- // 10 = "[derive()]", 3 = "()" and "]"
111- shape. offset_left ( 10 + prefix. len ( ) ) ?. sub_width ( 3 ) ?,
112- None ,
87+ // Collect all items from all attributes
88+ let all_items = derives
89+ . iter ( )
90+ . map ( |attr| {
91+ // Parse the derive items and extract the span for each item; if any
92+ // attribute is not parseable, none of the attributes will be
93+ // reformatted.
94+ let item_spans = attr. meta_item_list ( ) . map ( |meta_item_list| {
95+ meta_item_list
96+ . into_iter ( )
97+ . map ( |nested_meta_item| nested_meta_item. span ( ) )
98+ } ) ?;
99+
100+ let items = itemize_list (
101+ context. snippet_provider ,
102+ item_spans,
103+ ")" ,
104+ "," ,
105+ |span| span. lo ( ) ,
106+ |span| span. hi ( ) ,
107+ |span| Some ( context. snippet ( * span) . to_owned ( ) ) ,
108+ attr. span . lo ( ) ,
109+ attr. span . hi ( ) ,
110+ false ,
111+ ) ;
112+
113+ Some ( items)
114+ } )
115+ // Fail if any attribute failed.
116+ . collect :: < Option < Vec < _ > > > ( ) ?
117+ // Collect the results into a single, flat, Vec.
118+ . into_iter ( )
119+ . flatten ( )
120+ . collect :: < Vec < _ > > ( ) ;
121+
122+ // Collect formatting parameters.
123+ let prefix = attr_prefix ( & derives[ 0 ] ) ;
124+ let argument_shape = argument_shape (
125+ "[derive()]" . len ( ) + prefix. len ( ) ,
126+ ")]" . len ( ) ,
113127 false ,
128+ shape,
129+ context,
114130 ) ?;
131+ let one_line_shape = shape
132+ . offset_left ( "[derive()]" . len ( ) + prefix. len ( ) ) ?
133+ . sub_width ( "()]" . len ( ) ) ?;
134+ let one_line_budget = one_line_shape. width ;
115135
116- result. push_str ( & item_str) ;
117- if item_str. starts_with ( '\n' ) {
118- result. push ( ',' ) ;
136+ let tactic = definitive_tactic (
137+ & all_items,
138+ ListTactic :: HorizontalVertical ,
139+ Separator :: Comma ,
140+ argument_shape. width ,
141+ ) ;
142+ let trailing_separator = match context. config . indent_style ( ) {
143+ // We always add the trailing comma and remove it if it is not needed.
144+ IndentStyle :: Block => SeparatorTactic :: Always ,
145+ IndentStyle :: Visual => SeparatorTactic :: Never ,
146+ } ;
147+
148+ // Format the collection of items.
149+ let fmt = ListFormatting :: new ( argument_shape, context. config )
150+ . tactic ( tactic)
151+ . trailing_separator ( trailing_separator)
152+ . ends_with_newline ( false ) ;
153+ let item_str = write_list ( & all_items, & fmt) ?;
154+
155+ debug ! ( "item_str: '{}'" , item_str) ;
156+
157+ // Determine if the result will be nested, i.e. if we're using the block
158+ // indent style and either the items are on multiple lines or we've exceeded
159+ // our budget to fit on a single line.
160+ let nested = context. config . indent_style ( ) == IndentStyle :: Block
161+ && ( item_str. contains ( '\n' ) || item_str. len ( ) > one_line_budget) ;
162+
163+ // Format the final result.
164+ let mut result = String :: with_capacity ( 128 ) ;
165+ result. push_str ( prefix) ;
166+ result. push_str ( "[derive(" ) ;
167+ if nested {
168+ let nested_indent = argument_shape. indent . to_string_with_newline ( context. config ) ;
169+ result. push_str ( & nested_indent) ;
170+ result. push_str ( & item_str) ;
119171 result. push_str ( & shape. indent . to_string_with_newline ( context. config ) ) ;
172+ } else if let SeparatorTactic :: Always = context. config . trailing_comma ( ) {
173+ // Retain the trailing comma.
174+ result. push_str ( & item_str) ;
175+ } else {
176+ // Remove the trailing comma.
177+ result. push_str ( & item_str[ ..item_str. len ( ) - 1 ] ) ;
120178 }
121179 result. push_str ( ")]" ) ;
180+
122181 Some ( result)
123182}
124183
@@ -244,7 +303,7 @@ impl Rewrite for ast::MetaItem {
244303 // width. Since a literal is basically unformattable unless it
245304 // is a string literal (and only if `format_strings` is set),
246305 // we might be better off ignoring the fact that the attribute
247- // is longer than the max width and contiue on formatting.
306+ // is longer than the max width and continue on formatting.
248307 // See #2479 for example.
249308 let value = rewrite_literal ( context, literal, lit_shape)
250309 . unwrap_or_else ( || context. snippet ( literal. span ) . to_owned ( ) ) ;
@@ -258,61 +317,6 @@ impl Rewrite for ast::MetaItem {
258317 }
259318}
260319
261- fn format_arg_list < I , T , F1 , F2 , F3 > (
262- list : I ,
263- get_lo : F1 ,
264- get_hi : F2 ,
265- get_item_string : F3 ,
266- span : Span ,
267- context : & RewriteContext < ' _ > ,
268- shape : Shape ,
269- one_line_shape : Shape ,
270- one_line_limit : Option < usize > ,
271- combine : bool ,
272- ) -> Option < String >
273- where
274- I : Iterator < Item = T > ,
275- F1 : Fn ( & T ) -> BytePos ,
276- F2 : Fn ( & T ) -> BytePos ,
277- F3 : Fn ( & T ) -> Option < String > ,
278- {
279- let items = itemize_list (
280- context. snippet_provider ,
281- list,
282- ")" ,
283- "," ,
284- get_lo,
285- get_hi,
286- get_item_string,
287- span. lo ( ) ,
288- span. hi ( ) ,
289- false ,
290- ) ;
291- let item_vec = items. collect :: < Vec < _ > > ( ) ;
292- let tactic = if let Some ( limit) = one_line_limit {
293- ListTactic :: LimitedHorizontalVertical ( limit)
294- } else {
295- ListTactic :: HorizontalVertical
296- } ;
297-
298- let tactic = definitive_tactic ( & item_vec, tactic, Separator :: Comma , shape. width ) ;
299- let fmt = ListFormatting :: new ( shape, context. config )
300- . tactic ( tactic)
301- . ends_with_newline ( false ) ;
302- let item_str = write_list ( & item_vec, & fmt) ?;
303-
304- let one_line_budget = one_line_shape. width ;
305- if context. config . indent_style ( ) == IndentStyle :: Visual
306- || combine
307- || ( !item_str. contains ( '\n' ) && item_str. len ( ) <= one_line_budget)
308- {
309- Some ( item_str)
310- } else {
311- let nested_indent = shape. indent . to_string_with_newline ( context. config ) ;
312- Some ( format ! ( "{}{}" , nested_indent, item_str) )
313- }
314- }
315-
316320impl Rewrite for ast:: Attribute {
317321 fn rewrite ( & self , context : & RewriteContext < ' _ > , shape : Shape ) -> Option < String > {
318322 let snippet = context. snippet ( self . span ) ;
@@ -417,13 +421,7 @@ impl<'a> Rewrite for [ast::Attribute] {
417421 // Handle derives if we will merge them.
418422 if context. config . merge_derives ( ) && is_derive ( & attrs[ 0 ] ) {
419423 let derives = take_while_with_pred ( context, attrs, is_derive) ;
420- let derive_spans: Vec < _ > = derives
421- . iter ( )
422- . filter_map ( get_derive_spans)
423- . flatten ( )
424- . collect ( ) ;
425- let derive_str =
426- format_derive ( & derive_spans, attr_prefix ( & attrs[ 0 ] ) , shape, context) ?;
424+ let derive_str = format_derive ( derives, shape, context) ?;
427425 result. push_str ( & derive_str) ;
428426
429427 let missing_span = attrs
0 commit comments