1 //! Rewrite a list some items with overflow.
2
3 use std::cmp::min;
4
5 use itertools::Itertools;
6 use rustc_ast::token::Delimiter;
7 use rustc_ast::{ast, ptr};
8 use rustc_span::Span;
9
10 use crate::closures;
11 use crate::config::lists::*;
12 use crate::config::Version;
13 use crate::expr::{
14 can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
15 rewrite_cond,
16 };
17 use crate::lists::{
18 definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
19 };
20 use crate::macros::MacroArg;
21 use crate::patterns::{can_be_overflowed_pat, TuplePatField};
22 use crate::rewrite::{Rewrite, RewriteContext};
23 use crate::shape::Shape;
24 use crate::source_map::SpanUtils;
25 use crate::spanned::Spanned;
26 use crate::types::{can_be_overflowed_type, SegmentParam};
27 use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
28
29 /// A list of `format!`-like macros, that take a long format string and a list of arguments to
30 /// format.
31 ///
32 /// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of
33 /// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result,
34 /// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`).
35 const SPECIAL_CASE_MACROS: &[(&str, usize)] = &[
36 // format! like macros
37 // From the Rust Standard Library.
38 ("eprint!", 0),
39 ("eprintln!", 0),
40 ("format!", 0),
41 ("format_args!", 0),
42 ("print!", 0),
43 ("println!", 0),
44 ("panic!", 0),
45 ("unreachable!", 0),
46 // From the `log` crate.
47 ("debug!", 0),
48 ("error!", 0),
49 ("info!", 0),
50 ("warn!", 0),
51 // write! like macros
52 ("assert!", 1),
53 ("debug_assert!", 1),
54 ("write!", 1),
55 ("writeln!", 1),
56 // assert_eq! like macros
57 ("assert_eq!", 2),
58 ("assert_ne!", 2),
59 ("debug_assert_eq!", 2),
60 ("debug_assert_ne!", 2),
61 ];
62
63 const SPECIAL_CASE_ATTR: &[(&str, usize)] = &[
64 // From the `failure` crate.
65 ("fail", 0),
66 ];
67
68 #[derive(Debug)]
69 pub(crate) enum OverflowableItem<'a> {
70 Expr(&'a ast::Expr),
71 GenericParam(&'a ast::GenericParam),
72 MacroArg(&'a MacroArg),
73 NestedMetaItem(&'a ast::NestedMetaItem),
74 SegmentParam(&'a SegmentParam<'a>),
75 FieldDef(&'a ast::FieldDef),
76 TuplePatField(&'a TuplePatField<'a>),
77 Ty(&'a ast::Ty),
78 Pat(&'a ast::Pat),
79 }
80
81 impl<'a> Rewrite for OverflowableItem<'a> {
rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>82 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
83 self.map(|item| item.rewrite(context, shape))
84 }
85 }
86
87 impl<'a> Spanned for OverflowableItem<'a> {
span(&self) -> Span88 fn span(&self) -> Span {
89 self.map(|item| item.span())
90 }
91 }
92
93 impl<'a> OverflowableItem<'a> {
has_attrs(&self) -> bool94 fn has_attrs(&self) -> bool {
95 match self {
96 OverflowableItem::Expr(ast::Expr { attrs, .. })
97 | OverflowableItem::GenericParam(ast::GenericParam { attrs, .. }) => !attrs.is_empty(),
98 OverflowableItem::FieldDef(ast::FieldDef { attrs, .. }) => !attrs.is_empty(),
99 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => !expr.attrs.is_empty(),
100 OverflowableItem::MacroArg(MacroArg::Item(item)) => !item.attrs.is_empty(),
101 _ => false,
102 }
103 }
104
map<F, T>(&self, f: F) -> T where F: Fn(&dyn IntoOverflowableItem<'a>) -> T,105 pub(crate) fn map<F, T>(&self, f: F) -> T
106 where
107 F: Fn(&dyn IntoOverflowableItem<'a>) -> T,
108 {
109 match self {
110 OverflowableItem::Expr(expr) => f(*expr),
111 OverflowableItem::GenericParam(gp) => f(*gp),
112 OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
113 OverflowableItem::NestedMetaItem(nmi) => f(*nmi),
114 OverflowableItem::SegmentParam(sp) => f(*sp),
115 OverflowableItem::FieldDef(sf) => f(*sf),
116 OverflowableItem::TuplePatField(pat) => f(*pat),
117 OverflowableItem::Ty(ty) => f(*ty),
118 OverflowableItem::Pat(pat) => f(*pat),
119 }
120 }
121
is_simple(&self) -> bool122 pub(crate) fn is_simple(&self) -> bool {
123 match self {
124 OverflowableItem::Expr(expr) => is_simple_expr(expr),
125 OverflowableItem::MacroArg(MacroArg::Keyword(..)) => true,
126 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
127 OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item {
128 ast::NestedMetaItem::Lit(..) => true,
129 ast::NestedMetaItem::MetaItem(ref meta_item) => {
130 matches!(meta_item.kind, ast::MetaItemKind::Word)
131 }
132 },
133 _ => false,
134 }
135 }
136
is_expr(&self) -> bool137 pub(crate) fn is_expr(&self) -> bool {
138 matches!(
139 self,
140 OverflowableItem::Expr(..) | OverflowableItem::MacroArg(MacroArg::Expr(..))
141 )
142 }
143
is_nested_call(&self) -> bool144 pub(crate) fn is_nested_call(&self) -> bool {
145 match self {
146 OverflowableItem::Expr(expr) => is_nested_call(expr),
147 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
148 _ => false,
149 }
150 }
151
to_expr(&self) -> Option<&'a ast::Expr>152 pub(crate) fn to_expr(&self) -> Option<&'a ast::Expr> {
153 match self {
154 OverflowableItem::Expr(expr) => Some(expr),
155 OverflowableItem::MacroArg(MacroArg::Expr(ref expr)) => Some(expr),
156 _ => None,
157 }
158 }
159
can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize) -> bool160 pub(crate) fn can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize) -> bool {
161 match self {
162 OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
163 OverflowableItem::MacroArg(macro_arg) => match macro_arg {
164 MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
165 MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
166 MacroArg::Pat(..) => false,
167 MacroArg::Item(..) => len == 1,
168 MacroArg::Keyword(..) => false,
169 },
170 OverflowableItem::NestedMetaItem(nested_meta_item) if len == 1 => {
171 match nested_meta_item {
172 ast::NestedMetaItem::Lit(..) => false,
173 ast::NestedMetaItem::MetaItem(..) => true,
174 }
175 }
176 OverflowableItem::SegmentParam(SegmentParam::Type(ty)) => {
177 can_be_overflowed_type(context, ty, len)
178 }
179 OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
180 OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
181 _ => false,
182 }
183 }
184
special_cases(&self) -> &'static [(&'static str, usize)]185 fn special_cases(&self) -> &'static [(&'static str, usize)] {
186 match self {
187 OverflowableItem::MacroArg(..) => SPECIAL_CASE_MACROS,
188 OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR,
189 _ => &[],
190 }
191 }
192 }
193
194 pub(crate) trait IntoOverflowableItem<'a>: Rewrite + Spanned {
into_overflowable_item(&'a self) -> OverflowableItem<'a>195 fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
196 }
197
198 impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
into_overflowable_item(&'a self) -> OverflowableItem<'a>199 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
200 (**self).into_overflowable_item()
201 }
202 }
203
204 macro_rules! impl_into_overflowable_item_for_ast_node {
205 ($($ast_node:ident),*) => {
206 $(
207 impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
208 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
209 OverflowableItem::$ast_node(self)
210 }
211 }
212 )*
213 }
214 }
215
216 macro_rules! impl_into_overflowable_item_for_rustfmt_types {
217 ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
218 $(
219 impl<'a> IntoOverflowableItem<'a> for $ty {
220 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
221 OverflowableItem::$ty(self)
222 }
223 }
224 )*
225 $(
226 impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
227 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
228 OverflowableItem::$ty_with_lifetime(self)
229 }
230 }
231 )*
232 }
233 }
234
235 impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, FieldDef, Ty, Pat);
236 impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
237
into_overflowable_list<'a, T>( iter: impl Iterator<Item = &'a T>, ) -> impl Iterator<Item = OverflowableItem<'a>> where T: 'a + IntoOverflowableItem<'a>,238 pub(crate) fn into_overflowable_list<'a, T>(
239 iter: impl Iterator<Item = &'a T>,
240 ) -> impl Iterator<Item = OverflowableItem<'a>>
241 where
242 T: 'a + IntoOverflowableItem<'a>,
243 {
244 iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
245 }
246
rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>( context: &'a RewriteContext<'_>, ident: &'a str, items: impl Iterator<Item = &'a T>, shape: Shape, span: Span, item_max_width: usize, force_separator_tactic: Option<SeparatorTactic>, ) -> Option<String>247 pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
248 context: &'a RewriteContext<'_>,
249 ident: &'a str,
250 items: impl Iterator<Item = &'a T>,
251 shape: Shape,
252 span: Span,
253 item_max_width: usize,
254 force_separator_tactic: Option<SeparatorTactic>,
255 ) -> Option<String> {
256 Context::new(
257 context,
258 items,
259 ident,
260 shape,
261 span,
262 "(",
263 ")",
264 item_max_width,
265 force_separator_tactic,
266 None,
267 )
268 .rewrite(shape)
269 }
270
rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>( context: &'a RewriteContext<'_>, ident: &'a str, items: impl Iterator<Item = &'a T>, shape: Shape, span: Span, ) -> Option<String>271 pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
272 context: &'a RewriteContext<'_>,
273 ident: &'a str,
274 items: impl Iterator<Item = &'a T>,
275 shape: Shape,
276 span: Span,
277 ) -> Option<String> {
278 Context::new(
279 context,
280 items,
281 ident,
282 shape,
283 span,
284 "<",
285 ">",
286 context.config.max_width(),
287 None,
288 None,
289 )
290 .rewrite(shape)
291 }
292
rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>( context: &'a RewriteContext<'_>, name: &'a str, items: impl Iterator<Item = &'a T>, shape: Shape, span: Span, force_separator_tactic: Option<SeparatorTactic>, delim_token: Option<Delimiter>, ) -> Option<String>293 pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
294 context: &'a RewriteContext<'_>,
295 name: &'a str,
296 items: impl Iterator<Item = &'a T>,
297 shape: Shape,
298 span: Span,
299 force_separator_tactic: Option<SeparatorTactic>,
300 delim_token: Option<Delimiter>,
301 ) -> Option<String> {
302 let (lhs, rhs) = match delim_token {
303 Some(Delimiter::Parenthesis) => ("(", ")"),
304 Some(Delimiter::Brace) => ("{", "}"),
305 _ => ("[", "]"),
306 };
307 Context::new(
308 context,
309 items,
310 name,
311 shape,
312 span,
313 lhs,
314 rhs,
315 context.config.array_width(),
316 force_separator_tactic,
317 Some(("[", "]")),
318 )
319 .rewrite(shape)
320 }
321
322 struct Context<'a> {
323 context: &'a RewriteContext<'a>,
324 items: Vec<OverflowableItem<'a>>,
325 ident: &'a str,
326 prefix: &'static str,
327 suffix: &'static str,
328 one_line_shape: Shape,
329 nested_shape: Shape,
330 span: Span,
331 item_max_width: usize,
332 one_line_width: usize,
333 force_separator_tactic: Option<SeparatorTactic>,
334 custom_delims: Option<(&'a str, &'a str)>,
335 }
336
337 impl<'a> Context<'a> {
new<T: 'a + IntoOverflowableItem<'a>>( context: &'a RewriteContext<'_>, items: impl Iterator<Item = &'a T>, ident: &'a str, shape: Shape, span: Span, prefix: &'static str, suffix: &'static str, item_max_width: usize, force_separator_tactic: Option<SeparatorTactic>, custom_delims: Option<(&'a str, &'a str)>, ) -> Context<'a>338 fn new<T: 'a + IntoOverflowableItem<'a>>(
339 context: &'a RewriteContext<'_>,
340 items: impl Iterator<Item = &'a T>,
341 ident: &'a str,
342 shape: Shape,
343 span: Span,
344 prefix: &'static str,
345 suffix: &'static str,
346 item_max_width: usize,
347 force_separator_tactic: Option<SeparatorTactic>,
348 custom_delims: Option<(&'a str, &'a str)>,
349 ) -> Context<'a> {
350 let used_width = extra_offset(ident, shape);
351 // 1 = `()`
352 let one_line_width = shape.width.saturating_sub(used_width + 2);
353
354 // 1 = "(" or ")"
355 let one_line_shape = shape
356 .offset_left(last_line_width(ident) + 1)
357 .and_then(|shape| shape.sub_width(1))
358 .unwrap_or(Shape { width: 0, ..shape });
359 let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
360 Context {
361 context,
362 items: into_overflowable_list(items).collect(),
363 ident,
364 one_line_shape,
365 nested_shape,
366 span,
367 prefix,
368 suffix,
369 item_max_width,
370 one_line_width,
371 force_separator_tactic,
372 custom_delims,
373 }
374 }
375
last_item(&self) -> Option<&OverflowableItem<'_>>376 fn last_item(&self) -> Option<&OverflowableItem<'_>> {
377 self.items.last()
378 }
379
items_span(&self) -> Span380 fn items_span(&self) -> Span {
381 let span_lo = self
382 .context
383 .snippet_provider
384 .span_after(self.span, self.prefix);
385 mk_sp(span_lo, self.span.hi())
386 }
387
rewrite_last_item_with_overflow( &self, last_list_item: &mut ListItem, shape: Shape, ) -> Option<String>388 fn rewrite_last_item_with_overflow(
389 &self,
390 last_list_item: &mut ListItem,
391 shape: Shape,
392 ) -> Option<String> {
393 let last_item = self.last_item()?;
394 let rewrite = match last_item {
395 OverflowableItem::Expr(expr) => {
396 match expr.kind {
397 // When overflowing the closure which consists of a single control flow
398 // expression, force to use block if its condition uses multi line.
399 ast::ExprKind::Closure(..) => {
400 // If the argument consists of multiple closures, we do not overflow
401 // the last closure.
402 if closures::args_have_many_closure(&self.items) {
403 None
404 } else {
405 closures::rewrite_last_closure(self.context, expr, shape)
406 }
407 }
408
409 // When overflowing the expressions which consists of a control flow
410 // expression, avoid condition to use multi line.
411 ast::ExprKind::If(..)
412 | ast::ExprKind::ForLoop(..)
413 | ast::ExprKind::Loop(..)
414 | ast::ExprKind::While(..)
415 | ast::ExprKind::Match(..) => {
416 let multi_line = rewrite_cond(self.context, expr, shape)
417 .map_or(false, |cond| cond.contains('\n'));
418
419 if multi_line {
420 None
421 } else {
422 expr.rewrite(self.context, shape)
423 }
424 }
425
426 _ => expr.rewrite(self.context, shape),
427 }
428 }
429 item => item.rewrite(self.context, shape),
430 };
431
432 if let Some(rewrite) = rewrite {
433 // splitn(2, *).next().unwrap() is always safe.
434 let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned());
435 last_list_item.item = rewrite_first_line;
436 Some(rewrite)
437 } else {
438 None
439 }
440 }
441
default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic442 fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
443 definitive_tactic(
444 list_items,
445 ListTactic::LimitedHorizontalVertical(self.item_max_width),
446 Separator::Comma,
447 self.one_line_width,
448 )
449 }
450
try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic451 fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
452 // 1 = "("
453 let combine_arg_with_callee = self.items.len() == 1
454 && self.items[0].is_expr()
455 && !self.items[0].has_attrs()
456 && self.ident.len() < self.context.config.tab_spaces();
457 let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
458
459 // Replace the last item with its first line to see if it fits with
460 // first arguments.
461 let placeholder = if overflow_last {
462 let old_value = self.context.force_one_line_chain.get();
463 match self.last_item() {
464 Some(OverflowableItem::Expr(expr))
465 if !combine_arg_with_callee && is_method_call(expr) =>
466 {
467 self.context.force_one_line_chain.replace(true);
468 }
469 Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
470 if !combine_arg_with_callee
471 && is_method_call(expr)
472 && self.context.config.version() == Version::Two =>
473 {
474 self.context.force_one_line_chain.replace(true);
475 }
476 _ => (),
477 }
478 let result = last_item_shape(
479 &self.items,
480 list_items,
481 self.one_line_shape,
482 self.item_max_width,
483 )
484 .and_then(|arg_shape| {
485 self.rewrite_last_item_with_overflow(
486 &mut list_items[self.items.len() - 1],
487 arg_shape,
488 )
489 });
490 self.context.force_one_line_chain.replace(old_value);
491 result
492 } else {
493 None
494 };
495
496 let mut tactic = definitive_tactic(
497 &*list_items,
498 ListTactic::LimitedHorizontalVertical(self.item_max_width),
499 Separator::Comma,
500 self.one_line_width,
501 );
502
503 // Replace the stub with the full overflowing last argument if the rewrite
504 // succeeded and its first line fits with the other arguments.
505 match (overflow_last, tactic, placeholder) {
506 (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
507 if self.items.len() == 1 =>
508 {
509 // When we are rewriting a nested function call, we restrict the
510 // budget for the inner function to avoid them being deeply nested.
511 // However, when the inner function has a prefix or a suffix
512 // (e.g., `foo() as u32`), this budget reduction may produce poorly
513 // formatted code, where a prefix or a suffix being left on its own
514 // line. Here we explicitlly check those cases.
515 if count_newlines(overflowed) == 1 {
516 let rw = self
517 .items
518 .last()
519 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
520 let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
521 if no_newline {
522 list_items[self.items.len() - 1].item = rw;
523 } else {
524 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
525 }
526 } else {
527 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
528 }
529 }
530 (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
531 list_items[self.items.len() - 1].item = placeholder;
532 }
533 _ if !self.items.is_empty() => {
534 list_items[self.items.len() - 1].item = self
535 .items
536 .last()
537 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
538
539 // Use horizontal layout for a function with a single argument as long as
540 // everything fits in a single line.
541 // `self.one_line_width == 0` means vertical layout is forced.
542 if self.items.len() == 1
543 && self.one_line_width != 0
544 && !list_items[0].has_comment()
545 && !list_items[0].inner_as_ref().contains('\n')
546 && crate::lists::total_item_width(&list_items[0]) <= self.one_line_width
547 {
548 tactic = DefinitiveListTactic::Horizontal;
549 } else {
550 tactic = self.default_tactic(list_items);
551
552 if tactic == DefinitiveListTactic::Vertical {
553 if let Some((all_simple, num_args_before)) =
554 maybe_get_args_offset(self.ident, &self.items)
555 {
556 let one_line = all_simple
557 && definitive_tactic(
558 &list_items[..num_args_before],
559 ListTactic::HorizontalVertical,
560 Separator::Comma,
561 self.nested_shape.width,
562 ) == DefinitiveListTactic::Horizontal
563 && definitive_tactic(
564 &list_items[num_args_before + 1..],
565 ListTactic::HorizontalVertical,
566 Separator::Comma,
567 self.nested_shape.width,
568 ) == DefinitiveListTactic::Horizontal;
569
570 if one_line {
571 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
572 };
573 } else if is_every_expr_simple(&self.items)
574 && no_long_items(
575 list_items,
576 self.context.config.short_array_element_width_threshold(),
577 )
578 {
579 tactic = DefinitiveListTactic::Mixed;
580 }
581 }
582 }
583 }
584 _ => (),
585 }
586
587 tactic
588 }
589
rewrite_items(&self) -> Option<(bool, String)>590 fn rewrite_items(&self) -> Option<(bool, String)> {
591 let span = self.items_span();
592 debug!("items: {:?}", self.items);
593
594 let items = itemize_list(
595 self.context.snippet_provider,
596 self.items.iter(),
597 self.suffix,
598 ",",
599 |item| item.span().lo(),
600 |item| item.span().hi(),
601 |item| item.rewrite(self.context, self.nested_shape),
602 span.lo(),
603 span.hi(),
604 true,
605 );
606 let mut list_items: Vec<_> = items.collect();
607
608 debug!("items: {list_items:?}");
609
610 // Try letting the last argument overflow to the next line with block
611 // indentation. If its first line fits on one line with the other arguments,
612 // we format the function arguments horizontally.
613 let tactic = self.try_overflow_last_item(&mut list_items);
614 let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
615 tactic
616 } else if !self.context.use_block_indent() {
617 SeparatorTactic::Never
618 } else {
619 self.context.config.trailing_comma()
620 };
621 let ends_with_newline = match tactic {
622 DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
623 self.context.use_block_indent()
624 }
625 _ => false,
626 };
627
628 let fmt = ListFormatting::new(self.nested_shape, self.context.config)
629 .tactic(tactic)
630 .trailing_separator(trailing_separator)
631 .ends_with_newline(ends_with_newline);
632
633 write_list(&list_items, &fmt)
634 .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
635 }
636
wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String637 fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
638 let shape = Shape {
639 width: shape.width.saturating_sub(last_line_width(self.ident)),
640 ..shape
641 };
642
643 let (prefix, suffix) = match self.custom_delims {
644 Some((lhs, rhs)) => (lhs, rhs),
645 _ => (self.prefix, self.suffix),
646 };
647
648 let extend_width = if items_str.is_empty() {
649 2
650 } else {
651 first_line_width(items_str) + 1
652 };
653 let nested_indent_str = self
654 .nested_shape
655 .indent
656 .to_string_with_newline(self.context.config);
657 let indent_str = shape
658 .block()
659 .indent
660 .to_string_with_newline(self.context.config);
661 let mut result = String::with_capacity(
662 self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
663 );
664 result.push_str(self.ident);
665 result.push_str(prefix);
666 let force_single_line = if self.context.config.version() == Version::Two {
667 !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
668 } else {
669 // 2 = `()`
670 let fits_one_line = items_str.len() + 2 <= shape.width;
671 !self.context.use_block_indent()
672 || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
673 || (is_extendable && extend_width <= shape.width)
674 };
675 if force_single_line {
676 result.push_str(items_str);
677 } else {
678 if !items_str.is_empty() {
679 result.push_str(&nested_indent_str);
680 result.push_str(items_str);
681 }
682 result.push_str(&indent_str);
683 }
684 result.push_str(suffix);
685 result
686 }
687
rewrite(&self, shape: Shape) -> Option<String>688 fn rewrite(&self, shape: Shape) -> Option<String> {
689 let (extendable, items_str) = self.rewrite_items()?;
690
691 // If we are using visual indent style and failed to format, retry with block indent.
692 if !self.context.use_block_indent()
693 && need_block_indent(&items_str, self.nested_shape)
694 && !extendable
695 {
696 self.context.use_block.replace(true);
697 let result = self.rewrite(shape);
698 self.context.use_block.replace(false);
699 return result;
700 }
701
702 Some(self.wrap_items(&items_str, shape, extendable))
703 }
704 }
705
need_block_indent(s: &str, shape: Shape) -> bool706 fn need_block_indent(s: &str, shape: Shape) -> bool {
707 s.lines().skip(1).any(|s| {
708 s.find(|c| !char::is_whitespace(c))
709 .map_or(false, |w| w + 1 < shape.indent.width())
710 })
711 }
712
can_be_overflowed(context: &RewriteContext<'_>, items: &[OverflowableItem<'_>]) -> bool713 fn can_be_overflowed(context: &RewriteContext<'_>, items: &[OverflowableItem<'_>]) -> bool {
714 items
715 .last()
716 .map_or(false, |x| x.can_be_overflowed(context, items.len()))
717 }
718
719 /// Returns a shape for the last argument which is going to be overflowed.
last_item_shape( lists: &[OverflowableItem<'_>], items: &[ListItem], shape: Shape, args_max_width: usize, ) -> Option<Shape>720 fn last_item_shape(
721 lists: &[OverflowableItem<'_>],
722 items: &[ListItem],
723 shape: Shape,
724 args_max_width: usize,
725 ) -> Option<Shape> {
726 if items.len() == 1 && !lists.get(0)?.is_nested_call() {
727 return Some(shape);
728 }
729 let offset = items
730 .iter()
731 .dropping_back(1)
732 .map(|i| {
733 // 2 = ", "
734 2 + i.inner_as_ref().len()
735 })
736 .sum();
737 Shape {
738 width: min(args_max_width, shape.width),
739 ..shape
740 }
741 .offset_left(offset)
742 }
743
shape_from_indent_style( context: &RewriteContext<'_>, shape: Shape, overhead: usize, offset: usize, ) -> Shape744 fn shape_from_indent_style(
745 context: &RewriteContext<'_>,
746 shape: Shape,
747 overhead: usize,
748 offset: usize,
749 ) -> Shape {
750 let (shape, overhead) = if context.use_block_indent() {
751 let shape = shape
752 .block()
753 .block_indent(context.config.tab_spaces())
754 .with_max_width(context.config);
755 (shape, 1) // 1 = ","
756 } else {
757 (shape.visual_indent(offset), overhead)
758 };
759 Shape {
760 width: shape.width.saturating_sub(overhead),
761 ..shape
762 }
763 }
764
no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool765 fn no_long_items(list: &[ListItem], short_array_element_width_threshold: usize) -> bool {
766 list.iter()
767 .all(|item| item.inner_as_ref().len() <= short_array_element_width_threshold)
768 }
769
770 /// In case special-case style is required, returns an offset from which we start horizontal layout.
maybe_get_args_offset( callee_str: &str, args: &[OverflowableItem<'_>], ) -> Option<(bool, usize)>771 pub(crate) fn maybe_get_args_offset(
772 callee_str: &str,
773 args: &[OverflowableItem<'_>],
774 ) -> Option<(bool, usize)> {
775 if let Some(&(_, num_args_before)) = args
776 .get(0)?
777 .special_cases()
778 .iter()
779 .find(|&&(s, _)| s == callee_str)
780 {
781 let all_simple = args.len() > num_args_before
782 && is_every_expr_simple(&args[0..num_args_before])
783 && is_every_expr_simple(&args[num_args_before + 1..]);
784
785 Some((all_simple, num_args_before))
786 } else {
787 None
788 }
789 }
790