1 //! Rewrite a list some items with overflow.
5 use itertools::Itertools;
6 use rustc_ast::token::DelimToken;
7 use rustc_ast::{ast, ptr};
11 use crate::config::lists::*;
12 use crate::config::Version;
14 can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
18 definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
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};
29 const SHORT_ITEM_THRESHOLD: usize = 10;
31 /// A list of `format!`-like macros, that take a long format string and a list of arguments to
34 /// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of
35 /// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result,
36 /// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`).
37 const SPECIAL_MACRO_WHITELIST: &[(&str, usize)] = &[
38 // format! like macros
39 // From the Rust Standard Library.
48 // From the `log` crate.
58 // assert_eq! like macros
61 ("debug_assert_eq!", 2),
62 ("debug_assert_ne!", 2),
65 const SPECIAL_ATTR_WHITELIST: &[(&str, usize)] = &[
66 // From the `failure` crate.
71 pub(crate) enum OverflowableItem<'a> {
73 GenericParam(&'a ast::GenericParam),
74 MacroArg(&'a MacroArg),
75 NestedMetaItem(&'a ast::NestedMetaItem),
76 SegmentParam(&'a SegmentParam<'a>),
77 FieldDef(&'a ast::FieldDef),
78 TuplePatField(&'a TuplePatField<'a>),
82 impl<'a> Rewrite for OverflowableItem<'a> {
83 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
84 self.map(|item| item.rewrite(context, shape))
88 impl<'a> Spanned for OverflowableItem<'a> {
89 fn span(&self) -> Span {
90 self.map(|item| item.span())
94 impl<'a> OverflowableItem<'a> {
95 fn has_attrs(&self) -> bool {
97 OverflowableItem::Expr(ast::Expr { attrs, .. })
98 | OverflowableItem::GenericParam(ast::GenericParam { attrs, .. }) => !attrs.is_empty(),
99 OverflowableItem::FieldDef(ast::FieldDef { attrs, .. }) => !attrs.is_empty(),
100 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => !expr.attrs.is_empty(),
101 OverflowableItem::MacroArg(MacroArg::Item(item)) => !item.attrs.is_empty(),
106 pub(crate) fn map<F, T>(&self, f: F) -> T
108 F: Fn(&dyn IntoOverflowableItem<'a>) -> T,
111 OverflowableItem::Expr(expr) => f(*expr),
112 OverflowableItem::GenericParam(gp) => f(*gp),
113 OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
114 OverflowableItem::NestedMetaItem(nmi) => f(*nmi),
115 OverflowableItem::SegmentParam(sp) => f(*sp),
116 OverflowableItem::FieldDef(sf) => f(*sf),
117 OverflowableItem::TuplePatField(pat) => f(*pat),
118 OverflowableItem::Ty(ty) => f(*ty),
122 pub(crate) fn is_simple(&self) -> bool {
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::Literal(..) => true,
129 ast::NestedMetaItem::MetaItem(ref meta_item) => match meta_item.kind {
130 ast::MetaItemKind::Word => true,
138 pub(crate) fn is_expr(&self) -> bool {
140 OverflowableItem::Expr(..) => true,
141 OverflowableItem::MacroArg(MacroArg::Expr(..)) => true,
146 pub(crate) fn is_nested_call(&self) -> bool {
148 OverflowableItem::Expr(expr) => is_nested_call(expr),
149 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
154 pub(crate) fn to_expr(&self) -> Option<&'a ast::Expr> {
156 OverflowableItem::Expr(expr) => Some(expr),
157 OverflowableItem::MacroArg(macro_arg) => match macro_arg {
158 MacroArg::Expr(ref expr) => Some(expr),
165 pub(crate) fn can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize) -> bool {
167 OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
168 OverflowableItem::MacroArg(macro_arg) => match macro_arg {
169 MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
170 MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
171 MacroArg::Pat(..) => false,
172 MacroArg::Item(..) => len == 1,
173 MacroArg::Keyword(..) => false,
175 OverflowableItem::NestedMetaItem(nested_meta_item) if len == 1 => {
176 match nested_meta_item {
177 ast::NestedMetaItem::Literal(..) => false,
178 ast::NestedMetaItem::MetaItem(..) => true,
181 OverflowableItem::SegmentParam(seg) => match seg {
182 SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len),
185 OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
186 OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
191 fn whitelist(&self) -> &'static [(&'static str, usize)] {
193 OverflowableItem::MacroArg(..) => SPECIAL_MACRO_WHITELIST,
194 OverflowableItem::NestedMetaItem(..) => SPECIAL_ATTR_WHITELIST,
200 pub(crate) trait IntoOverflowableItem<'a>: Rewrite + Spanned {
201 fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
204 impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
205 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
206 (**self).into_overflowable_item()
210 macro_rules! impl_into_overflowable_item_for_ast_node {
211 ($($ast_node:ident),*) => {
213 impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
214 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
215 OverflowableItem::$ast_node(self)
222 macro_rules! impl_into_overflowable_item_for_rustfmt_types {
223 ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
225 impl<'a> IntoOverflowableItem<'a> for $ty {
226 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
227 OverflowableItem::$ty(self)
232 impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
233 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
234 OverflowableItem::$ty_with_lifetime(self)
241 impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, FieldDef, Ty);
242 impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
244 pub(crate) fn into_overflowable_list<'a, T>(
245 iter: impl Iterator<Item = &'a T>,
246 ) -> impl Iterator<Item = OverflowableItem<'a>>
248 T: 'a + IntoOverflowableItem<'a>,
250 iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
253 pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
254 context: &'a RewriteContext<'_>,
256 items: impl Iterator<Item = &'a T>,
259 item_max_width: usize,
260 force_separator_tactic: Option<SeparatorTactic>,
261 ) -> Option<String> {
271 force_separator_tactic,
277 pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
278 context: &'a RewriteContext<'_>,
280 items: impl Iterator<Item = &'a T>,
283 ) -> Option<String> {
292 context.config.max_width(),
299 pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
300 context: &'a RewriteContext<'_>,
302 items: impl Iterator<Item = &'a T>,
305 force_separator_tactic: Option<SeparatorTactic>,
306 delim_token: Option<DelimToken>,
307 ) -> Option<String> {
308 let (lhs, rhs) = match delim_token {
309 Some(DelimToken::Paren) => ("(", ")"),
310 Some(DelimToken::Brace) => ("{", "}"),
321 context.config.array_width(),
322 force_separator_tactic,
329 context: &'a RewriteContext<'a>,
330 items: Vec<OverflowableItem<'a>>,
332 prefix: &'static str,
333 suffix: &'static str,
334 one_line_shape: Shape,
337 item_max_width: usize,
338 one_line_width: usize,
339 force_separator_tactic: Option<SeparatorTactic>,
340 custom_delims: Option<(&'a str, &'a str)>,
343 impl<'a> Context<'a> {
344 fn new<T: 'a + IntoOverflowableItem<'a>>(
345 context: &'a RewriteContext<'_>,
346 items: impl Iterator<Item = &'a T>,
350 prefix: &'static str,
351 suffix: &'static str,
352 item_max_width: usize,
353 force_separator_tactic: Option<SeparatorTactic>,
354 custom_delims: Option<(&'a str, &'a str)>,
356 let used_width = extra_offset(ident, shape);
358 let one_line_width = shape.width.saturating_sub(used_width + 2);
361 let one_line_shape = shape
362 .offset_left(last_line_width(ident) + 1)
363 .and_then(|shape| shape.sub_width(1))
364 .unwrap_or(Shape { width: 0, ..shape });
365 let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
368 items: into_overflowable_list(items).collect(),
377 force_separator_tactic,
382 fn last_item(&self) -> Option<&OverflowableItem<'_>> {
386 fn items_span(&self) -> Span {
390 .span_after(self.span, self.prefix);
391 mk_sp(span_lo, self.span.hi())
394 fn rewrite_last_item_with_overflow(
396 last_list_item: &mut ListItem,
398 ) -> Option<String> {
399 let last_item = self.last_item()?;
400 let rewrite = match last_item {
401 OverflowableItem::Expr(ref expr) => {
403 // When overflowing the closure which consists of a single control flow
404 // expression, force to use block if its condition uses multi line.
405 ast::ExprKind::Closure(..) => {
406 // If the argument consists of multiple closures, we do not overflow
408 if closures::args_have_many_closure(&self.items) {
411 closures::rewrite_last_closure(self.context, expr, shape)
415 // When overflowing the expressions which consists of a control flow
416 // expression, avoid condition to use multi line.
417 ast::ExprKind::If(..)
418 | ast::ExprKind::ForLoop(..)
419 | ast::ExprKind::Loop(..)
420 | ast::ExprKind::While(..)
421 | ast::ExprKind::Match(..) => {
422 let multi_line = rewrite_cond(self.context, expr, shape)
423 .map_or(false, |cond| cond.contains('\n'));
428 expr.rewrite(self.context, shape)
432 _ => expr.rewrite(self.context, shape),
435 item => item.rewrite(self.context, shape),
438 if let Some(rewrite) = rewrite {
439 // splitn(2, *).next().unwrap() is always safe.
440 let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned());
441 last_list_item.item = rewrite_first_line;
448 fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
451 ListTactic::LimitedHorizontalVertical(self.item_max_width),
457 fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
459 let combine_arg_with_callee = self.items.len() == 1
460 && self.items[0].is_expr()
461 && !self.items[0].has_attrs()
462 && self.ident.len() < self.context.config.tab_spaces();
463 let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
465 // Replace the last item with its first line to see if it fits with
467 let placeholder = if overflow_last {
468 let old_value = self.context.force_one_line_chain.get();
469 match self.last_item() {
470 Some(OverflowableItem::Expr(expr))
471 if !combine_arg_with_callee && is_method_call(expr) =>
473 self.context.force_one_line_chain.replace(true);
475 Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
476 if !combine_arg_with_callee
477 && is_method_call(expr)
478 && self.context.config.version() == Version::Two =>
480 self.context.force_one_line_chain.replace(true);
484 let result = last_item_shape(
490 .and_then(|arg_shape| {
491 self.rewrite_last_item_with_overflow(
492 &mut list_items[self.items.len() - 1],
496 self.context.force_one_line_chain.replace(old_value);
502 let mut tactic = definitive_tactic(
504 ListTactic::LimitedHorizontalVertical(self.item_max_width),
509 // Replace the stub with the full overflowing last argument if the rewrite
510 // succeeded and its first line fits with the other arguments.
511 match (overflow_last, tactic, placeholder) {
512 (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
513 if self.items.len() == 1 =>
515 // When we are rewriting a nested function call, we restrict the
516 // budget for the inner function to avoid them being deeply nested.
517 // However, when the inner function has a prefix or a suffix
518 // (e.g., `foo() as u32`), this budget reduction may produce poorly
519 // formatted code, where a prefix or a suffix being left on its own
520 // line. Here we explicitlly check those cases.
521 if count_newlines(overflowed) == 1 {
525 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
526 let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
528 list_items[self.items.len() - 1].item = rw;
530 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
533 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
536 (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
537 list_items[self.items.len() - 1].item = placeholder;
539 _ if !self.items.is_empty() => {
540 list_items[self.items.len() - 1].item = self
543 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
545 // Use horizontal layout for a function with a single argument as long as
546 // everything fits in a single line.
547 // `self.one_line_width == 0` means vertical layout is forced.
548 if self.items.len() == 1
549 && self.one_line_width != 0
550 && !list_items[0].has_comment()
551 && !list_items[0].inner_as_ref().contains('\n')
552 && crate::lists::total_item_width(&list_items[0]) <= self.one_line_width
554 tactic = DefinitiveListTactic::Horizontal;
556 tactic = self.default_tactic(list_items);
558 if tactic == DefinitiveListTactic::Vertical {
559 if let Some((all_simple, num_args_before)) =
560 maybe_get_args_offset(self.ident, &self.items)
562 let one_line = all_simple
563 && definitive_tactic(
564 &list_items[..num_args_before],
565 ListTactic::HorizontalVertical,
567 self.nested_shape.width,
568 ) == DefinitiveListTactic::Horizontal
569 && definitive_tactic(
570 &list_items[num_args_before + 1..],
571 ListTactic::HorizontalVertical,
573 self.nested_shape.width,
574 ) == DefinitiveListTactic::Horizontal;
577 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
579 } else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
580 tactic = DefinitiveListTactic::Mixed;
591 fn rewrite_items(&self) -> Option<(bool, String)> {
592 let span = self.items_span();
593 let items = itemize_list(
594 self.context.snippet_provider,
598 |item| item.span().lo(),
599 |item| item.span().hi(),
600 |item| item.rewrite(self.context, self.nested_shape),
605 let mut list_items: Vec<_> = items.collect();
607 // Try letting the last argument overflow to the next line with block
608 // indentation. If its first line fits on one line with the other arguments,
609 // we format the function arguments horizontally.
610 let tactic = self.try_overflow_last_item(&mut list_items);
611 let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
613 } else if !self.context.use_block_indent() {
614 SeparatorTactic::Never
616 self.context.config.trailing_comma()
618 let ends_with_newline = match tactic {
619 DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
620 self.context.use_block_indent()
625 let fmt = ListFormatting::new(self.nested_shape, self.context.config)
627 .trailing_separator(trailing_separator)
628 .ends_with_newline(ends_with_newline);
630 write_list(&list_items, &fmt)
631 .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
634 fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
636 width: shape.width.saturating_sub(last_line_width(self.ident)),
640 let (prefix, suffix) = match self.custom_delims {
641 Some((lhs, rhs)) => (lhs, rhs),
642 _ => (self.prefix, self.suffix),
645 let extend_width = if items_str.is_empty() {
648 first_line_width(items_str) + 1
650 let nested_indent_str = self
653 .to_string_with_newline(self.context.config);
654 let indent_str = shape
657 .to_string_with_newline(self.context.config);
658 let mut result = String::with_capacity(
659 self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
661 result.push_str(self.ident);
662 result.push_str(prefix);
663 let force_single_line = if self.context.config.version() == Version::Two {
664 !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
667 let fits_one_line = items_str.len() + 2 <= shape.width;
668 !self.context.use_block_indent()
669 || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
670 || (is_extendable && extend_width <= shape.width)
672 if force_single_line {
673 result.push_str(items_str);
675 if !items_str.is_empty() {
676 result.push_str(&nested_indent_str);
677 result.push_str(items_str);
679 result.push_str(&indent_str);
681 result.push_str(suffix);
685 fn rewrite(&self, shape: Shape) -> Option<String> {
686 let (extendable, items_str) = self.rewrite_items()?;
688 // If we are using visual indent style and failed to format, retry with block indent.
689 if !self.context.use_block_indent()
690 && need_block_indent(&items_str, self.nested_shape)
693 self.context.use_block.replace(true);
694 let result = self.rewrite(shape);
695 self.context.use_block.replace(false);
699 Some(self.wrap_items(&items_str, shape, extendable))
703 fn need_block_indent(s: &str, shape: Shape) -> bool {
704 s.lines().skip(1).any(|s| {
705 s.find(|c| !char::is_whitespace(c))
706 .map_or(false, |w| w + 1 < shape.indent.width())
710 fn can_be_overflowed(context: &RewriteContext<'_>, items: &[OverflowableItem<'_>]) -> bool {
713 .map_or(false, |x| x.can_be_overflowed(context, items.len()))
716 /// Returns a shape for the last argument which is going to be overflowed.
718 lists: &[OverflowableItem<'_>],
721 args_max_width: usize,
723 if items.len() == 1 && !lists.get(0)?.is_nested_call() {
731 2 + i.inner_as_ref().len()
735 width: min(args_max_width, shape.width),
741 fn shape_from_indent_style(
742 context: &RewriteContext<'_>,
747 let (shape, overhead) = if context.use_block_indent() {
750 .block_indent(context.config.tab_spaces())
751 .with_max_width(context.config);
752 (shape, 1) // 1 = ","
754 (shape.visual_indent(offset), overhead)
757 width: shape.width.saturating_sub(overhead),
762 fn no_long_items(list: &[ListItem]) -> bool {
764 .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
767 /// In case special-case style is required, returns an offset from which we start horizontal layout.
768 pub(crate) fn maybe_get_args_offset(
770 args: &[OverflowableItem<'_>],
771 ) -> Option<(bool, usize)> {
772 if let Some(&(_, num_args_before)) = args
776 .find(|&&(s, _)| s == callee_str)
778 let all_simple = args.len() > num_args_before
779 && is_every_expr_simple(&args[0..num_args_before])
780 && is_every_expr_simple(&args[num_args_before + 1..]);
782 Some((all_simple, num_args_before))