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.
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.
11 //! Rewrite a list some items with overflow.
15 use syntax::parse::token::DelimToken;
16 use syntax::source_map::Span;
17 use syntax::{ast, ptr};
20 use crate::config::lists::*;
21 use crate::config::Version;
23 can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr,
27 definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator,
29 use crate::macros::MacroArg;
30 use crate::patterns::{can_be_overflowed_pat, TuplePatField};
31 use crate::rewrite::{Rewrite, RewriteContext};
32 use crate::shape::Shape;
33 use crate::source_map::SpanUtils;
34 use crate::spanned::Spanned;
35 use crate::types::{can_be_overflowed_type, SegmentParam};
36 use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
38 const SHORT_ITEM_THRESHOLD: usize = 10;
40 /// A list of `format!`-like macros, that take a long format string and a list of arguments to
43 /// Organized as a list of `(&str, usize)` tuples, giving the name of the macro and the number of
44 /// arguments before the format string (none for `format!("format", ...)`, one for `assert!(result,
45 /// "format", ...)`, two for `assert_eq!(left, right, "format", ...)`).
46 const SPECIAL_MACRO_WHITELIST: &[(&str, usize)] = &[
47 // format! like macros
48 // From the Rust Standard Library.
57 // From the `log` crate.
67 // assert_eq! like macros
70 ("debug_assert_eq!", 2),
71 ("debug_assert_ne!", 2),
74 const SPECIAL_ATTR_WHITELIST: &[(&str, usize)] = &[
75 // From the `failure` crate.
80 pub enum OverflowableItem<'a> {
82 GenericParam(&'a ast::GenericParam),
83 MacroArg(&'a MacroArg),
84 NestedMetaItem(&'a ast::NestedMetaItem),
85 SegmentParam(&'a SegmentParam<'a>),
86 StructField(&'a ast::StructField),
87 TuplePatField(&'a TuplePatField<'a>),
91 impl<'a> Rewrite for OverflowableItem<'a> {
92 fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
93 self.map(|item| item.rewrite(context, shape))
97 impl<'a> Spanned for OverflowableItem<'a> {
98 fn span(&self) -> Span {
99 self.map(|item| item.span())
103 impl<'a> OverflowableItem<'a> {
104 pub fn map<F, T>(&self, f: F) -> T
106 F: Fn(&dyn IntoOverflowableItem<'a>) -> T,
109 OverflowableItem::Expr(expr) => f(*expr),
110 OverflowableItem::GenericParam(gp) => f(*gp),
111 OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
112 OverflowableItem::NestedMetaItem(nmi) => f(*nmi),
113 OverflowableItem::SegmentParam(sp) => f(*sp),
114 OverflowableItem::StructField(sf) => f(*sf),
115 OverflowableItem::TuplePatField(pat) => f(*pat),
116 OverflowableItem::Ty(ty) => f(*ty),
120 pub fn is_simple(&self) -> bool {
122 OverflowableItem::Expr(expr) => is_simple_expr(expr),
123 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
124 OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item.node {
125 ast::NestedMetaItemKind::Literal(..) => true,
126 ast::NestedMetaItemKind::MetaItem(ref meta_item) => match meta_item.node {
127 ast::MetaItemKind::Word => true,
135 pub fn is_expr(&self) -> bool {
137 OverflowableItem::Expr(..) => true,
138 OverflowableItem::MacroArg(MacroArg::Expr(..)) => true,
143 pub fn is_nested_call(&self) -> bool {
145 OverflowableItem::Expr(expr) => is_nested_call(expr),
146 OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_nested_call(expr),
151 pub fn to_expr(&self) -> Option<&'a ast::Expr> {
153 OverflowableItem::Expr(expr) => Some(expr),
154 OverflowableItem::MacroArg(macro_arg) => match macro_arg {
155 MacroArg::Expr(ref expr) => Some(expr),
162 pub fn can_be_overflowed(&self, context: &RewriteContext<'_>, len: usize) -> bool {
164 OverflowableItem::Expr(expr) => can_be_overflowed_expr(context, expr, len),
165 OverflowableItem::MacroArg(macro_arg) => match macro_arg {
166 MacroArg::Expr(ref expr) => can_be_overflowed_expr(context, expr, len),
167 MacroArg::Ty(ref ty) => can_be_overflowed_type(context, ty, len),
168 MacroArg::Pat(..) => false,
169 MacroArg::Item(..) => len == 1,
170 MacroArg::Keyword(..) => false,
172 OverflowableItem::NestedMetaItem(nested_meta_item) if len == 1 => {
173 match nested_meta_item.node {
174 ast::NestedMetaItemKind::Literal(..) => false,
175 ast::NestedMetaItemKind::MetaItem(..) => true,
178 OverflowableItem::SegmentParam(seg) => match seg {
179 SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len),
182 OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
183 OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
188 fn whitelist(&self) -> &'static [(&'static str, usize)] {
190 OverflowableItem::MacroArg(..) => SPECIAL_MACRO_WHITELIST,
191 OverflowableItem::NestedMetaItem(..) => SPECIAL_ATTR_WHITELIST,
197 pub trait IntoOverflowableItem<'a>: Rewrite + Spanned {
198 fn into_overflowable_item(&'a self) -> OverflowableItem<'a>;
201 impl<'a, T: 'a + IntoOverflowableItem<'a>> IntoOverflowableItem<'a> for ptr::P<T> {
202 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
203 (**self).into_overflowable_item()
207 macro_rules! impl_into_overflowable_item_for_ast_node {
208 ($($ast_node:ident),*) => {
210 impl<'a> IntoOverflowableItem<'a> for ast::$ast_node {
211 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
212 OverflowableItem::$ast_node(self)
219 macro_rules! impl_into_overflowable_item_for_rustfmt_types {
220 ([$($ty:ident),*], [$($ty_with_lifetime:ident),*]) => {
222 impl<'a> IntoOverflowableItem<'a> for $ty {
223 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
224 OverflowableItem::$ty(self)
229 impl<'a> IntoOverflowableItem<'a> for $ty_with_lifetime<'a> {
230 fn into_overflowable_item(&'a self) -> OverflowableItem<'a> {
231 OverflowableItem::$ty_with_lifetime(self)
238 impl_into_overflowable_item_for_ast_node!(Expr, GenericParam, NestedMetaItem, StructField, Ty);
239 impl_into_overflowable_item_for_rustfmt_types!([MacroArg], [SegmentParam, TuplePatField]);
241 pub fn into_overflowable_list<'a, T>(
242 iter: impl Iterator<Item = &'a T>,
243 ) -> impl Iterator<Item = OverflowableItem<'a>>
245 T: 'a + IntoOverflowableItem<'a>,
247 iter.map(|x| IntoOverflowableItem::into_overflowable_item(x))
250 pub fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>(
251 context: &'a RewriteContext<'_>,
253 items: impl Iterator<Item = &'a T>,
256 item_max_width: usize,
257 force_separator_tactic: Option<SeparatorTactic>,
258 ) -> Option<String> {
268 force_separator_tactic,
274 pub fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
275 context: &'a RewriteContext<'_>,
277 items: impl Iterator<Item = &'a T>,
280 ) -> Option<String> {
289 context.config.max_width(),
296 pub fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>>(
297 context: &'a RewriteContext<'_>,
299 items: impl Iterator<Item = &'a T>,
302 force_separator_tactic: Option<SeparatorTactic>,
303 delim_token: Option<DelimToken>,
304 ) -> Option<String> {
305 let (lhs, rhs) = match delim_token {
306 Some(DelimToken::Paren) => ("(", ")"),
307 Some(DelimToken::Brace) => ("{", "}"),
318 context.config.width_heuristics().array_width,
319 force_separator_tactic,
326 context: &'a RewriteContext<'a>,
327 items: Vec<OverflowableItem<'a>>,
329 prefix: &'static str,
330 suffix: &'static str,
331 one_line_shape: Shape,
334 item_max_width: usize,
335 one_line_width: usize,
336 force_separator_tactic: Option<SeparatorTactic>,
337 custom_delims: Option<(&'a str, &'a str)>,
340 impl<'a> Context<'a> {
341 pub fn new<T: 'a + IntoOverflowableItem<'a>>(
342 context: &'a RewriteContext<'_>,
343 items: impl Iterator<Item = &'a T>,
347 prefix: &'static str,
348 suffix: &'static str,
349 item_max_width: usize,
350 force_separator_tactic: Option<SeparatorTactic>,
351 custom_delims: Option<(&'a str, &'a str)>,
353 let used_width = extra_offset(ident, shape);
355 let one_line_width = shape.width.saturating_sub(used_width + 2);
358 let one_line_shape = shape
359 .offset_left(last_line_width(ident) + 1)
360 .and_then(|shape| shape.sub_width(1))
361 .unwrap_or(Shape { width: 0, ..shape });
362 let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
365 items: into_overflowable_list(items).collect(),
374 force_separator_tactic,
379 fn last_item(&self) -> Option<&OverflowableItem<'_>> {
383 fn items_span(&self) -> Span {
387 .span_after(self.span, self.prefix);
388 mk_sp(span_lo, self.span.hi())
391 fn rewrite_last_item_with_overflow(
393 last_list_item: &mut ListItem,
395 ) -> Option<String> {
396 let last_item = self.last_item()?;
397 let rewrite = match last_item {
398 OverflowableItem::Expr(ref expr) => {
400 // When overflowing the closure which consists of a single control flow
401 // expression, force to use block if its condition uses multi line.
402 ast::ExprKind::Closure(..) => {
403 // If the argument consists of multiple closures, we do not overflow
405 if closures::args_have_many_closure(&self.items) {
408 closures::rewrite_last_closure(self.context, expr, shape)
412 // When overflowing the expressions which consists of a control flow
413 // expression, avoid condition to use multi line.
414 ast::ExprKind::If(..)
415 | ast::ExprKind::IfLet(..)
416 | ast::ExprKind::ForLoop(..)
417 | ast::ExprKind::Loop(..)
418 | ast::ExprKind::While(..)
419 | ast::ExprKind::WhileLet(..)
420 | ast::ExprKind::Match(..) => {
421 let multi_line = rewrite_cond(self.context, expr, shape)
422 .map_or(false, |cond| cond.contains('\n'));
427 expr.rewrite(self.context, shape)
431 _ => expr.rewrite(self.context, shape),
434 item => item.rewrite(self.context, shape),
437 if let Some(rewrite) = rewrite {
438 // splitn(2, *).next().unwrap() is always safe.
439 let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned());
440 last_list_item.item = rewrite_first_line;
447 fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
450 ListTactic::LimitedHorizontalVertical(self.item_max_width),
456 fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
458 let combine_arg_with_callee = self.items.len() == 1
459 && self.items[0].is_expr()
460 && self.ident.len() < self.context.config.tab_spaces();
461 let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, &self.items);
463 // Replace the last item with its first line to see if it fits with
465 let placeholder = if overflow_last {
466 let old_value = *self.context.force_one_line_chain.borrow();
467 match self.last_item() {
468 Some(OverflowableItem::Expr(expr))
469 if !combine_arg_with_callee && is_method_call(expr) =>
471 self.context.force_one_line_chain.replace(true);
473 Some(OverflowableItem::MacroArg(MacroArg::Expr(expr)))
474 if !combine_arg_with_callee
475 && is_method_call(expr)
476 && self.context.config.version() == Version::Two =>
478 self.context.force_one_line_chain.replace(true);
482 let result = last_item_shape(
488 .and_then(|arg_shape| {
489 self.rewrite_last_item_with_overflow(
490 &mut list_items[self.items.len() - 1],
494 self.context.force_one_line_chain.replace(old_value);
500 let mut tactic = definitive_tactic(
502 ListTactic::LimitedHorizontalVertical(self.item_max_width),
507 // Replace the stub with the full overflowing last argument if the rewrite
508 // succeeded and its first line fits with the other arguments.
509 match (overflow_last, tactic, placeholder) {
510 (true, DefinitiveListTactic::Horizontal, Some(ref overflowed))
511 if self.items.len() == 1 =>
513 // When we are rewriting a nested function call, we restrict the
514 // budget for the inner function to avoid them being deeply nested.
515 // However, when the inner function has a prefix or a suffix
516 // (e.g. `foo() as u32`), this budget reduction may produce poorly
517 // formatted code, where a prefix or a suffix being left on its own
518 // line. Here we explicitlly check those cases.
519 if count_newlines(overflowed) == 1 {
523 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
524 let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
526 list_items[self.items.len() - 1].item = rw;
528 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
531 list_items[self.items.len() - 1].item = Some(overflowed.to_owned());
534 (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => {
535 list_items[self.items.len() - 1].item = placeholder;
537 _ if !self.items.is_empty() => {
538 list_items[self.items.len() - 1].item = self
541 .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
543 // Use horizontal layout for a function with a single argument as long as
544 // everything fits in a single line.
545 // `self.one_line_width == 0` means vertical layout is forced.
546 if self.items.len() == 1
547 && self.one_line_width != 0
548 && !list_items[0].has_comment()
549 && !list_items[0].inner_as_ref().contains('\n')
550 && crate::lists::total_item_width(&list_items[0]) <= self.one_line_width
552 tactic = DefinitiveListTactic::Horizontal;
554 tactic = self.default_tactic(list_items);
556 if tactic == DefinitiveListTactic::Vertical {
557 if let Some((all_simple, num_args_before)) =
558 maybe_get_args_offset(self.ident, &self.items)
560 let one_line = all_simple
561 && definitive_tactic(
562 &list_items[..num_args_before],
563 ListTactic::HorizontalVertical,
565 self.nested_shape.width,
566 ) == DefinitiveListTactic::Horizontal
567 && definitive_tactic(
568 &list_items[num_args_before + 1..],
569 ListTactic::HorizontalVertical,
571 self.nested_shape.width,
572 ) == DefinitiveListTactic::Horizontal;
575 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
577 } else if is_every_expr_simple(&self.items) && no_long_items(list_items) {
578 tactic = DefinitiveListTactic::Mixed;
589 fn rewrite_items(&self) -> Option<(bool, String)> {
590 let span = self.items_span();
591 let items = itemize_list(
592 self.context.snippet_provider,
596 |item| item.span().lo(),
597 |item| item.span().hi(),
598 |item| item.rewrite(self.context, self.nested_shape),
603 let mut list_items: Vec<_> = items.collect();
605 // Try letting the last argument overflow to the next line with block
606 // indentation. If its first line fits on one line with the other arguments,
607 // we format the function arguments horizontally.
608 let tactic = self.try_overflow_last_item(&mut list_items);
609 let trailing_separator = if let Some(tactic) = self.force_separator_tactic {
611 } else if !self.context.use_block_indent() {
612 SeparatorTactic::Never
614 self.context.config.trailing_comma()
616 let ends_with_newline = match tactic {
617 DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
618 self.context.use_block_indent()
623 let fmt = ListFormatting::new(self.nested_shape, self.context.config)
625 .trailing_separator(trailing_separator)
626 .ends_with_newline(ends_with_newline);
628 write_list(&list_items, &fmt)
629 .map(|items_str| (tactic == DefinitiveListTactic::Horizontal, items_str))
632 fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
634 width: shape.width.saturating_sub(last_line_width(self.ident)),
638 let (prefix, suffix) = match self.custom_delims {
639 Some((lhs, rhs)) => (lhs, rhs),
640 _ => (self.prefix, self.suffix),
643 let extend_width = if items_str.is_empty() {
646 first_line_width(items_str) + 1
648 let nested_indent_str = self
651 .to_string_with_newline(self.context.config);
652 let indent_str = shape
655 .to_string_with_newline(self.context.config);
656 let mut result = String::with_capacity(
657 self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
659 result.push_str(self.ident);
660 result.push_str(prefix);
661 let force_single_line = if self.context.config.version() == Version::Two {
662 !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width)
665 let fits_one_line = items_str.len() + 2 <= shape.width;
666 !self.context.use_block_indent()
667 || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
668 || (is_extendable && extend_width <= shape.width)
670 if force_single_line {
671 result.push_str(items_str);
673 if !items_str.is_empty() {
674 result.push_str(&nested_indent_str);
675 result.push_str(items_str);
677 result.push_str(&indent_str);
679 result.push_str(suffix);
683 fn rewrite(&self, shape: Shape) -> Option<String> {
684 let (extendable, items_str) = self.rewrite_items()?;
686 // If we are using visual indent style and failed to format, retry with block indent.
687 if !self.context.use_block_indent()
688 && need_block_indent(&items_str, self.nested_shape)
691 self.context.use_block.replace(true);
692 let result = self.rewrite(shape);
693 self.context.use_block.replace(false);
697 Some(self.wrap_items(&items_str, shape, extendable))
701 fn need_block_indent(s: &str, shape: Shape) -> bool {
702 s.lines().skip(1).any(|s| {
703 s.find(|c| !char::is_whitespace(c))
704 .map_or(false, |w| w + 1 < shape.indent.width())
708 fn can_be_overflowed(context: &RewriteContext<'_>, items: &[OverflowableItem<'_>]) -> bool {
711 .map_or(false, |x| x.can_be_overflowed(context, items.len()))
714 /// Returns a shape for the last argument which is going to be overflowed.
716 lists: &[OverflowableItem<'_>],
719 args_max_width: usize,
721 if items.len() == 1 && !lists.get(0)?.is_nested_call() {
724 let offset = items.iter().rev().skip(1).fold(0, |acc, i| {
726 acc + 2 + i.inner_as_ref().len()
729 width: min(args_max_width, shape.width),
735 fn shape_from_indent_style(
736 context: &RewriteContext<'_>,
741 let (shape, overhead) = if context.use_block_indent() {
744 .block_indent(context.config.tab_spaces())
745 .with_max_width(context.config);
746 (shape, 1) // 1 = ","
748 (shape.visual_indent(offset), overhead)
751 width: shape.width.saturating_sub(overhead),
756 fn no_long_items(list: &[ListItem]) -> bool {
758 .all(|item| item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
761 /// In case special-case style is required, returns an offset from which we start horizontal layout.
762 pub fn maybe_get_args_offset(
764 args: &[OverflowableItem<'_>],
765 ) -> Option<(bool, usize)> {
766 if let Some(&(_, num_args_before)) = args
770 .find(|&&(s, _)| s == callee_str)
772 let all_simple = args.len() > num_args_before
773 && is_every_expr_simple(&args[0..num_args_before])
774 && is_every_expr_simple(&args[num_args_before + 1..]);
776 Some((all_simple, num_args_before))