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