1 //! Rewrite a list some items with overflow.
5 use itertools
::Itertools
;
6 use rustc_ast
::token
::Delimiter
;
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 /// A list of `format!`-like macros, that take a long format string and a list of arguments to
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.
46 // From the `log` crate.
56 // assert_eq! like macros
59 ("debug_assert_eq!", 2),
60 ("debug_assert_ne!", 2),
63 const SPECIAL_CASE_ATTR
: &[(&str, usize)] = &[
64 // From the `failure` crate.
69 pub(crate) enum OverflowableItem
<'a
> {
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
>),
81 impl<'a
> Rewrite
for OverflowableItem
<'a
> {
82 fn rewrite(&self, context
: &RewriteContext
<'_
>, shape
: Shape
) -> Option
<String
> {
83 self.map(|item
| item
.rewrite(context
, shape
))
87 impl<'a
> Spanned
for OverflowableItem
<'a
> {
88 fn span(&self) -> Span
{
89 self.map(|item
| item
.span())
93 impl<'a
> OverflowableItem
<'a
> {
94 fn has_attrs(&self) -> bool
{
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(),
105 pub(crate) fn map
<F
, T
>(&self, f
: F
) -> T
107 F
: Fn(&dyn IntoOverflowableItem
<'a
>) -> T
,
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
),
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
) => {
130 matches
!(meta_item
.kind
, ast
::MetaItemKind
::Word
)
137 pub(crate) fn is_expr(&self) -> bool
{
140 OverflowableItem
::Expr(..) | OverflowableItem
::MacroArg(MacroArg
::Expr(..))
144 pub(crate) fn is_nested_call(&self) -> bool
{
146 OverflowableItem
::Expr(expr
) => is_nested_call(expr
),
147 OverflowableItem
::MacroArg(MacroArg
::Expr(expr
)) => is_nested_call(expr
),
152 pub(crate) fn to_expr(&self) -> Option
<&'a ast
::Expr
> {
154 OverflowableItem
::Expr(expr
) => Some(expr
),
155 OverflowableItem
::MacroArg(MacroArg
::Expr(ref expr
)) => Some(expr
),
160 pub(crate) fn can_be_overflowed(&self, context
: &RewriteContext
<'_
>, len
: usize) -> bool
{
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,
170 OverflowableItem
::NestedMetaItem(nested_meta_item
) if len
== 1 => {
171 match nested_meta_item
{
172 ast
::NestedMetaItem
::Literal(..) => false,
173 ast
::NestedMetaItem
::MetaItem(..) => true,
176 OverflowableItem
::SegmentParam(SegmentParam
::Type(ty
)) => {
177 can_be_overflowed_type(context
, ty
, len
)
179 OverflowableItem
::TuplePatField(pat
) => can_be_overflowed_pat(context
, pat
, len
),
180 OverflowableItem
::Ty(ty
) => can_be_overflowed_type(context
, ty
, len
),
185 fn special_cases(&self) -> &'
static [(&'
static str, usize)] {
187 OverflowableItem
::MacroArg(..) => SPECIAL_CASE_MACROS
,
188 OverflowableItem
::NestedMetaItem(..) => SPECIAL_CASE_ATTR
,
194 pub(crate) trait IntoOverflowableItem
<'a
>: Rewrite
+ Spanned
{
195 fn into_overflowable_item(&'a
self) -> OverflowableItem
<'a
>;
198 impl<'a
, T
: 'a
+ IntoOverflowableItem
<'a
>> IntoOverflowableItem
<'a
> for ptr
::P
<T
> {
199 fn into_overflowable_item(&'a
self) -> OverflowableItem
<'a
> {
200 (**self).into_overflowable_item()
204 macro_rules
! impl_into_overflowable_item_for_ast_node
{
205 ($
($ast_node
:ident
),*) => {
207 impl<'a
> IntoOverflowableItem
<'a
> for ast
::$ast_node
{
208 fn into_overflowable_item(&'a
self) -> OverflowableItem
<'a
> {
209 OverflowableItem
::$
ast_node(self)
216 macro_rules
! impl_into_overflowable_item_for_rustfmt_types
{
217 ([$
($ty
:ident
),*], [$
($ty_with_lifetime
:ident
),*]) => {
219 impl<'a
> IntoOverflowableItem
<'a
> for $ty
{
220 fn into_overflowable_item(&'a
self) -> OverflowableItem
<'a
> {
221 OverflowableItem
::$
ty(self)
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)
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
]);
238 pub(crate) fn into_overflowable_list
<'a
, T
>(
239 iter
: impl Iterator
<Item
= &'a T
>,
240 ) -> impl Iterator
<Item
= OverflowableItem
<'a
>>
242 T
: 'a
+ IntoOverflowableItem
<'a
>,
244 iter
.map(|x
| IntoOverflowableItem
::into_overflowable_item(x
))
247 pub(crate) fn rewrite_with_parens
<'a
, T
: 'a
+ IntoOverflowableItem
<'a
>>(
248 context
: &'a RewriteContext
<'_
>,
250 items
: impl Iterator
<Item
= &'a T
>,
253 item_max_width
: usize,
254 force_separator_tactic
: Option
<SeparatorTactic
>,
255 ) -> Option
<String
> {
265 force_separator_tactic
,
271 pub(crate) fn rewrite_with_angle_brackets
<'a
, T
: 'a
+ IntoOverflowableItem
<'a
>>(
272 context
: &'a RewriteContext
<'_
>,
274 items
: impl Iterator
<Item
= &'a T
>,
277 ) -> Option
<String
> {
286 context
.config
.max_width(),
293 pub(crate) fn rewrite_with_square_brackets
<'a
, T
: 'a
+ IntoOverflowableItem
<'a
>>(
294 context
: &'a RewriteContext
<'_
>,
296 items
: impl Iterator
<Item
= &'a T
>,
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
) => ("{", "}"),
315 context
.config
.array_width(),
316 force_separator_tactic
,
323 context
: &'a RewriteContext
<'a
>,
324 items
: Vec
<OverflowableItem
<'a
>>,
326 prefix
: &'
static str,
327 suffix
: &'
static str,
328 one_line_shape
: Shape
,
331 item_max_width
: usize,
332 one_line_width
: usize,
333 force_separator_tactic
: Option
<SeparatorTactic
>,
334 custom_delims
: Option
<(&'a
str, &'a
str)>,
337 impl<'a
> Context
<'a
> {
338 fn new
<T
: 'a
+ IntoOverflowableItem
<'a
>>(
339 context
: &'a RewriteContext
<'_
>,
340 items
: impl Iterator
<Item
= &'a T
>,
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)>,
350 let used_width
= extra_offset(ident
, shape
);
352 let one_line_width
= shape
.width
.saturating_sub(used_width
+ 2);
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);
362 items
: into_overflowable_list(items
).collect(),
371 force_separator_tactic
,
376 fn last_item(&self) -> Option
<&OverflowableItem
<'_
>> {
380 fn items_span(&self) -> Span
{
384 .span_after(self.span
, self.prefix
);
385 mk_sp(span_lo
, self.span
.hi())
388 fn rewrite_last_item_with_overflow(
390 last_list_item
: &mut ListItem
,
392 ) -> Option
<String
> {
393 let last_item
= self.last_item()?
;
394 let rewrite
= match last_item
{
395 OverflowableItem
::Expr(expr
) => {
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
402 if closures
::args_have_many_closure(&self.items
) {
405 closures
::rewrite_last_closure(self.context
, expr
, shape
)
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'
));
422 expr
.rewrite(self.context
, shape
)
426 _
=> expr
.rewrite(self.context
, shape
),
429 item
=> item
.rewrite(self.context
, shape
),
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
;
442 fn default_tactic(&self, list_items
: &[ListItem
]) -> DefinitiveListTactic
{
445 ListTactic
::LimitedHorizontalVertical(self.item_max_width
),
451 fn try_overflow_last_item(&self, list_items
: &mut Vec
<ListItem
>) -> DefinitiveListTactic
{
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
);
459 // Replace the last item with its first line to see if it fits with
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
) =>
467 self.context
.force_one_line_chain
.replace(true);
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
=>
474 self.context
.force_one_line_chain
.replace(true);
478 let result
= last_item_shape(
484 .and_then(|arg_shape
| {
485 self.rewrite_last_item_with_overflow(
486 &mut list_items
[self.items
.len() - 1],
490 self.context
.force_one_line_chain
.replace(old_value
);
496 let mut tactic
= definitive_tactic(
498 ListTactic
::LimitedHorizontalVertical(self.item_max_width
),
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 =>
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 {
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'
));
522 list_items
[self.items
.len() - 1].item
= rw
;
524 list_items
[self.items
.len() - 1].item
= Some(overflowed
.to_owned());
527 list_items
[self.items
.len() - 1].item
= Some(overflowed
.to_owned());
530 (true, DefinitiveListTactic
::Horizontal
, placeholder @
Some(..)) => {
531 list_items
[self.items
.len() - 1].item
= placeholder
;
533 _
if !self.items
.is_empty() => {
534 list_items
[self.items
.len() - 1].item
= self
537 .and_then(|last_item
| last_item
.rewrite(self.context
, self.nested_shape
));
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
548 tactic
= DefinitiveListTactic
::Horizontal
;
550 tactic
= self.default_tactic(list_items
);
552 if tactic
== DefinitiveListTactic
::Vertical
{
553 if let Some((all_simple
, num_args_before
)) =
554 maybe_get_args_offset(self.ident
, &self.items
)
556 let one_line
= all_simple
557 && definitive_tactic(
558 &list_items
[..num_args_before
],
559 ListTactic
::HorizontalVertical
,
561 self.nested_shape
.width
,
562 ) == DefinitiveListTactic
::Horizontal
563 && definitive_tactic(
564 &list_items
[num_args_before
+ 1..],
565 ListTactic
::HorizontalVertical
,
567 self.nested_shape
.width
,
568 ) == DefinitiveListTactic
::Horizontal
;
571 tactic
= DefinitiveListTactic
::SpecialMacro(num_args_before
);
573 } else if is_every_expr_simple(&self.items
)
576 self.context
.config
.short_array_element_width_threshold(),
579 tactic
= DefinitiveListTactic
::Mixed
;
590 fn rewrite_items(&self) -> Option
<(bool
, String
)> {
591 let span
= self.items_span();
592 let items
= itemize_list(
593 self.context
.snippet_provider
,
597 |item
| item
.span().lo(),
598 |item
| item
.span().hi(),
599 |item
| item
.rewrite(self.context
, self.nested_shape
),
604 let mut list_items
: Vec
<_
> = items
.collect();
606 // Try letting the last argument overflow to the next line with block
607 // indentation. If its first line fits on one line with the other arguments,
608 // we format the function arguments horizontally.
609 let tactic
= self.try_overflow_last_item(&mut list_items
);
610 let trailing_separator
= if let Some(tactic
) = self.force_separator_tactic
{
612 } else if !self.context
.use_block_indent() {
613 SeparatorTactic
::Never
615 self.context
.config
.trailing_comma()
617 let ends_with_newline
= match tactic
{
618 DefinitiveListTactic
::Vertical
| DefinitiveListTactic
::Mixed
=> {
619 self.context
.use_block_indent()
624 let fmt
= ListFormatting
::new(self.nested_shape
, self.context
.config
)
626 .trailing_separator(trailing_separator
)
627 .ends_with_newline(ends_with_newline
);
629 write_list(&list_items
, &fmt
)
630 .map(|items_str
| (tactic
== DefinitiveListTactic
::Horizontal
, items_str
))
633 fn wrap_items(&self, items_str
: &str, shape
: Shape
, is_extendable
: bool
) -> String
{
635 width
: shape
.width
.saturating_sub(last_line_width(self.ident
)),
639 let (prefix
, suffix
) = match self.custom_delims
{
640 Some((lhs
, rhs
)) => (lhs
, rhs
),
641 _
=> (self.prefix
, self.suffix
),
644 let extend_width
= if items_str
.is_empty() {
647 first_line_width(items_str
) + 1
649 let nested_indent_str
= self
652 .to_string_with_newline(self.context
.config
);
653 let indent_str
= shape
656 .to_string_with_newline(self.context
.config
);
657 let mut result
= String
::with_capacity(
658 self.ident
.len() + items_str
.len() + 2 + indent_str
.len() + nested_indent_str
.len(),
660 result
.push_str(self.ident
);
661 result
.push_str(prefix
);
662 let force_single_line
= if self.context
.config
.version() == Version
::Two
{
663 !self.context
.use_block_indent() || (is_extendable
&& extend_width
<= shape
.width
)
666 let fits_one_line
= items_str
.len() + 2 <= shape
.width
;
667 !self.context
.use_block_indent()
668 || (self.context
.inside_macro() && !items_str
.contains('
\n'
) && fits_one_line
)
669 || (is_extendable
&& extend_width
<= shape
.width
)
671 if force_single_line
{
672 result
.push_str(items_str
);
674 if !items_str
.is_empty() {
675 result
.push_str(&nested_indent_str
);
676 result
.push_str(items_str
);
678 result
.push_str(&indent_str
);
680 result
.push_str(suffix
);
684 fn rewrite(&self, shape
: Shape
) -> Option
<String
> {
685 let (extendable
, items_str
) = self.rewrite_items()?
;
687 // If we are using visual indent style and failed to format, retry with block indent.
688 if !self.context
.use_block_indent()
689 && need_block_indent(&items_str
, self.nested_shape
)
692 self.context
.use_block
.replace(true);
693 let result
= self.rewrite(shape
);
694 self.context
.use_block
.replace(false);
698 Some(self.wrap_items(&items_str
, shape
, extendable
))
702 fn need_block_indent(s
: &str, shape
: Shape
) -> bool
{
703 s
.lines().skip(1).any(|s
| {
704 s
.find(|c
| !char::is_whitespace(c
))
705 .map_or(false, |w
| w
+ 1 < shape
.indent
.width())
709 fn can_be_overflowed(context
: &RewriteContext
<'_
>, items
: &[OverflowableItem
<'_
>]) -> bool
{
712 .map_or(false, |x
| x
.can_be_overflowed(context
, items
.len()))
715 /// Returns a shape for the last argument which is going to be overflowed.
717 lists
: &[OverflowableItem
<'_
>],
720 args_max_width
: usize,
722 if items
.len() == 1 && !lists
.get(0)?
.is_nested_call() {
730 2 + i
.inner_as_ref().len()
734 width
: min(args_max_width
, shape
.width
),
740 fn shape_from_indent_style(
741 context
: &RewriteContext
<'_
>,
746 let (shape
, overhead
) = if context
.use_block_indent() {
749 .block_indent(context
.config
.tab_spaces())
750 .with_max_width(context
.config
);
751 (shape
, 1) // 1 = ","
753 (shape
.visual_indent(offset
), overhead
)
756 width
: shape
.width
.saturating_sub(overhead
),
761 fn no_long_items(list
: &[ListItem
], short_array_element_width_threshold
: usize) -> bool
{
763 .all(|item
| item
.inner_as_ref().len() <= short_array_element_width_threshold
)
766 /// In case special-case style is required, returns an offset from which we start horizontal layout.
767 pub(crate) fn maybe_get_args_offset(
769 args
: &[OverflowableItem
<'_
>],
770 ) -> Option
<(bool
, usize)> {
771 if let Some(&(_
, num_args_before
)) = args
775 .find(|&&(s
, _
)| s
== callee_str
)
777 let all_simple
= args
.len() > num_args_before
778 && is_every_expr_simple(&args
[0..num_args_before
])
779 && is_every_expr_simple(&args
[num_args_before
+ 1..]);
781 Some((all_simple
, num_args_before
))