2
2
3
3
use rustc_ast:: ast;
4
4
use rustc_ast:: attr:: HasAttrs ;
5
- use rustc_span:: { symbol:: sym, BytePos , Span , DUMMY_SP } ;
5
+ use rustc_span:: { symbol:: sym, Span } ;
6
6
7
7
use self :: doc_comment:: DocCommentFormatter ;
8
8
use crate :: comment:: { contains_comment, rewrite_doc_comment, CommentStyle } ;
@@ -52,15 +52,6 @@ fn is_derive(attr: &ast::Attribute) -> bool {
52
52
attr. check_name ( sym:: derive)
53
53
}
54
54
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
-
64
55
// The shape of the arguments to a function-like attribute.
65
56
fn argument_shape (
66
57
left : usize ,
@@ -89,36 +80,104 @@ fn argument_shape(
89
80
}
90
81
91
82
fn format_derive (
92
- derive_args : & [ Span ] ,
93
- prefix : & str ,
83
+ derives : & [ ast:: Attribute ] ,
94
84
shape : Shape ,
95
85
context : & RewriteContext < ' _ > ,
96
86
) -> 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 ( ) ,
113
127
false ,
128
+ shape,
129
+ context,
114
130
) ?;
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 ;
115
135
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) ;
119
171
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 ] ) ;
120
178
}
121
179
result. push_str ( ")]" ) ;
180
+
122
181
Some ( result)
123
182
}
124
183
@@ -244,7 +303,7 @@ impl Rewrite for ast::MetaItem {
244
303
// width. Since a literal is basically unformattable unless it
245
304
// is a string literal (and only if `format_strings` is set),
246
305
// 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.
248
307
// See #2479 for example.
249
308
let value = rewrite_literal ( context, literal, lit_shape)
250
309
. unwrap_or_else ( || context. snippet ( literal. span ) . to_owned ( ) ) ;
@@ -258,61 +317,6 @@ impl Rewrite for ast::MetaItem {
258
317
}
259
318
}
260
319
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
-
316
320
impl Rewrite for ast:: Attribute {
317
321
fn rewrite ( & self , context : & RewriteContext < ' _ > , shape : Shape ) -> Option < String > {
318
322
let snippet = context. snippet ( self . span ) ;
@@ -417,13 +421,7 @@ impl<'a> Rewrite for [ast::Attribute] {
417
421
// Handle derives if we will merge them.
418
422
if context. config . merge_derives ( ) && is_derive ( & attrs[ 0 ] ) {
419
423
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) ?;
427
425
result. push_str ( & derive_str) ;
428
426
429
427
let missing_span = attrs
0 commit comments