]> git.lizzy.rs Git - rust.git/blobdiff - src/overflow.rs
Disallow combining a method call with prefix or suffix
[rust.git] / src / overflow.rs
index 294c257d31f49205214ceaf7b6466bfba11ab70d..f2f05d835ff0b6eed2571a453ba6491b4a23f074 100644 (file)
 use config::lists::*;
 use syntax::ast;
 use syntax::codemap::Span;
+use syntax::parse::token::DelimToken;
 
 use closures;
 use codemap::SpanUtils;
+use expr::{is_every_expr_simple, is_method_call, is_nested_call, maybe_get_args_offset, ToExpr};
 use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator};
 use rewrite::{Rewrite, RewriteContext};
-use expr::{is_nested_call, maybe_get_args_offset, ToExpr};
 use shape::Shape;
 use spanned::Spanned;
-use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp, paren_overhead};
+use utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp};
 
 use std::cmp::min;
 
+const SHORT_ITEM_THRESHOLD: usize = 10;
+
 pub fn rewrite_with_parens<T>(
     context: &RewriteContext,
     ident: &str,
@@ -48,6 +51,7 @@ pub fn rewrite_with_parens<T>(
         ")",
         item_max_width,
         force_separator_tactic,
+        None,
     ).rewrite(shape)
 }
 
@@ -71,6 +75,38 @@ pub fn rewrite_with_angle_brackets<T>(
         ">",
         context.config.max_width(),
         None,
+        None,
+    ).rewrite(shape)
+}
+
+pub fn rewrite_with_square_brackets<T>(
+    context: &RewriteContext,
+    name: &str,
+    items: &[&T],
+    shape: Shape,
+    span: Span,
+    force_separator_tactic: Option<SeparatorTactic>,
+    delim_token: Option<DelimToken>,
+) -> Option<String>
+where
+    T: Rewrite + ToExpr + Spanned,
+{
+    let (lhs, rhs) = match delim_token {
+        Some(DelimToken::Paren) => ("(", ")"),
+        Some(DelimToken::Brace) => ("{", "}"),
+        _ => ("[", "]"),
+    };
+    Context::new(
+        context,
+        items,
+        name,
+        shape,
+        span,
+        lhs,
+        rhs,
+        context.config.width_heuristics().array_width,
+        force_separator_tactic,
+        Some(("[", "]")),
     ).rewrite(shape)
 }
 
@@ -86,6 +122,7 @@ struct Context<'a, T: 'a> {
     item_max_width: usize,
     one_line_width: usize,
     force_separator_tactic: Option<SeparatorTactic>,
+    custom_delims: Option<(&'a str, &'a str)>,
 }
 
 impl<'a, T: 'a + Rewrite + ToExpr + Spanned> Context<'a, T> {
@@ -99,30 +136,18 @@ pub fn new(
         suffix: &'static str,
         item_max_width: usize,
         force_separator_tactic: Option<SeparatorTactic>,
+        custom_delims: Option<(&'a str, &'a str)>,
     ) -> Context<'a, T> {
-        // 2 = `( `, 1 = `(`
-        let paren_overhead = if context.config.spaces_within_parens_and_brackets() {
-            2
-        } else {
-            1
-        };
         let used_width = extra_offset(ident, shape);
-        let one_line_width = shape
-            .width
-            .checked_sub(used_width + 2 * paren_overhead)
-            .unwrap_or(0);
+        // 1 = `()`
+        let one_line_width = shape.width.saturating_sub(used_width + 2);
 
         // 1 = "(" or ")"
         let one_line_shape = shape
             .offset_left(last_line_width(ident) + 1)
             .and_then(|shape| shape.sub_width(1))
             .unwrap_or(Shape { width: 0, ..shape });
-        let nested_shape = shape_from_indent_style(
-            context,
-            shape,
-            used_width + 2 * paren_overhead,
-            used_width + paren_overhead,
-        );
+        let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1);
         Context {
             context,
             items,
@@ -135,6 +160,7 @@ pub fn new(
             item_max_width,
             one_line_width,
             force_separator_tactic,
+            custom_delims,
         }
     }
 
@@ -143,7 +169,8 @@ fn last_item(&self) -> Option<&&T> {
     }
 
     fn items_span(&self) -> Span {
-        let span_lo = self.context
+        let span_lo = self
+            .context
             .snippet_provider
             .span_after(self.span, self.prefix);
         mk_sp(span_lo, self.span.hi())
@@ -183,9 +210,19 @@ fn rewrite_last_item_with_overflow(
         }
     }
 
+    fn default_tactic(&self, list_items: &[ListItem]) -> DefinitiveListTactic {
+        definitive_tactic(
+            list_items,
+            ListTactic::LimitedHorizontalVertical(self.item_max_width),
+            Separator::Comma,
+            self.one_line_width,
+        )
+    }
+
     fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveListTactic {
         // 1 = "("
-        let combine_arg_with_callee = self.items.len() == 1 && self.items[0].to_expr().is_some()
+        let combine_arg_with_callee = self.items.len() == 1
+            && self.items[0].to_expr().is_some()
             && self.ident.len() + 1 <= self.context.config.tab_spaces();
         let overflow_last = combine_arg_with_callee || can_be_overflowed(self.context, self.items);
 
@@ -194,8 +231,8 @@ fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveLi
         let placeholder = if overflow_last {
             let old_value = *self.context.force_one_line_chain.borrow();
             if !combine_arg_with_callee {
-                if let Some(expr) = self.last_item().and_then(|item| item.to_expr()) {
-                    if let ast::ExprKind::MethodCall(..) = expr.node {
+                if let Some(ref expr) = self.last_item().and_then(|item| item.to_expr()) {
+                    if is_method_call(expr) {
                         self.context.force_one_line_chain.replace(true);
                     }
                 }
@@ -231,13 +268,14 @@ fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveLi
                 if self.items.len() == 1 =>
             {
                 // When we are rewriting a nested function call, we restrict the
-                // bugdet for the inner function to avoid them being deeply nested.
+                // budget for the inner function to avoid them being deeply nested.
                 // However, when the inner function has a prefix or a suffix
                 // (e.g. `foo() as u32`), this budget reduction may produce poorly
                 // formatted code, where a prefix or a suffix being left on its own
                 // line. Here we explicitlly check those cases.
                 if count_newlines(overflowed) == 1 {
-                    let rw = self.items
+                    let rw = self
+                        .items
                         .last()
                         .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
                     let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n'));
@@ -254,30 +292,23 @@ fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveLi
                 list_items[self.items.len() - 1].item = placeholder;
             }
             _ if self.items.len() >= 1 => {
-                list_items[self.items.len() - 1].item = self.items
+                list_items[self.items.len() - 1].item = self
+                    .items
                     .last()
                     .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape));
 
-                let default_tactic = || {
-                    definitive_tactic(
-                        &*list_items,
-                        ListTactic::LimitedHorizontalVertical(self.item_max_width),
-                        Separator::Comma,
-                        self.one_line_width,
-                    )
-                };
-
                 // Use horizontal layout for a function with a single argument as long as
                 // everything fits in a single line.
+                // `self.one_line_width == 0` means vertical layout is forced.
                 if self.items.len() == 1
-                && self.one_line_width != 0 // Vertical layout is forced.
-                && !list_items[0].has_comment()
+                    && self.one_line_width != 0
+                    && !list_items[0].has_comment()
                     && !list_items[0].inner_as_ref().contains('\n')
                     && ::lists::total_item_width(&list_items[0]) <= self.one_line_width
                 {
                     tactic = DefinitiveListTactic::Horizontal;
                 } else {
-                    tactic = default_tactic();
+                    tactic = self.default_tactic(list_items);
 
                     if tactic == DefinitiveListTactic::Vertical {
                         if let Some((all_simple, num_args_before)) =
@@ -302,6 +333,8 @@ fn try_overflow_last_item(&self, list_items: &mut Vec<ListItem>) -> DefinitiveLi
                             if one_line {
                                 tactic = DefinitiveListTactic::SpecialMacro(num_args_before);
                             };
+                        } else if is_every_expr_simple(self.items) && no_long_items(list_items) {
+                            tactic = DefinitiveListTactic::Mixed;
                         }
                     }
                 }
@@ -340,13 +373,20 @@ fn rewrite_items(&self) -> Option<(bool, String)> {
                 tactic
             } else if !self.context.use_block_indent() {
                 SeparatorTactic::Never
+            } else if tactic == DefinitiveListTactic::Mixed {
+                // We are using mixed layout because everything did not fit within a single line.
+                SeparatorTactic::Always
             } else {
                 self.context.config.trailing_comma()
             },
             separator_place: SeparatorPlace::Back,
             shape: self.nested_shape,
-            ends_with_newline: self.context.use_block_indent()
-                && tactic == DefinitiveListTactic::Vertical,
+            ends_with_newline: match tactic {
+                DefinitiveListTactic::Vertical | DefinitiveListTactic::Mixed => {
+                    self.context.use_block_indent()
+                }
+                _ => false,
+            },
             preserve_newline: false,
             config: self.context.config,
         };
@@ -357,21 +397,24 @@ fn rewrite_items(&self) -> Option<(bool, String)> {
 
     fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String {
         let shape = Shape {
-            width: shape
-                .width
-                .checked_sub(last_line_width(self.ident))
-                .unwrap_or(0),
+            width: shape.width.saturating_sub(last_line_width(self.ident)),
             ..shape
         };
 
-        let paren_overhead = paren_overhead(self.context);
-        let fits_one_line = items_str.len() + paren_overhead <= shape.width;
+        let (prefix, suffix) = match self.custom_delims {
+            Some((lhs, rhs)) => (lhs, rhs),
+            _ => (self.prefix, self.suffix),
+        };
+
+        // 2 = `()`
+        let fits_one_line = items_str.len() + 2 <= shape.width;
         let extend_width = if items_str.is_empty() {
-            paren_overhead
+            2
         } else {
-            paren_overhead / 2
+            first_line_width(items_str) + 1
         };
-        let nested_indent_str = self.nested_shape
+        let nested_indent_str = self
+            .nested_shape
             .indent
             .to_string_with_newline(self.context.config);
         let indent_str = shape
@@ -382,18 +425,12 @@ fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> Stri
             self.ident.len() + items_str.len() + 2 + indent_str.len() + nested_indent_str.len(),
         );
         result.push_str(self.ident);
-        result.push_str(self.prefix);
+        result.push_str(prefix);
         if !self.context.use_block_indent()
-            || (self.context.inside_macro && !items_str.contains('\n') && fits_one_line)
+            || (self.context.inside_macro() && !items_str.contains('\n') && fits_one_line)
             || (is_extendable && extend_width <= shape.width)
         {
-            if self.context.config.spaces_within_parens_and_brackets() && !items_str.is_empty() {
-                result.push(' ');
-                result.push_str(items_str);
-                result.push(' ');
-            } else {
-                result.push_str(items_str);
-            }
+            result.push_str(items_str);
         } else {
             if !items_str.is_empty() {
                 result.push_str(&nested_indent_str);
@@ -401,7 +438,7 @@ fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> Stri
             }
             result.push_str(&indent_str);
         }
-        result.push_str(self.suffix);
+        result.push_str(suffix);
         result
     }
 
@@ -409,7 +446,8 @@ fn rewrite(&self, shape: Shape) -> Option<String> {
         let (extendable, items_str) = self.rewrite_items()?;
 
         // If we are using visual indent style and failed to format, retry with block indent.
-        if !self.context.use_block_indent() && need_block_indent(&items_str, self.nested_shape)
+        if !self.context.use_block_indent()
+            && need_block_indent(&items_str, self.nested_shape)
             && !extendable
         {
             self.context.use_block.replace(true);
@@ -489,3 +527,8 @@ fn shape_from_indent_style(
         }
     }
 }
+
+fn no_long_items(list: &[ListItem]) -> bool {
+    list.iter()
+        .all(|item| !item.has_comment() && item.inner_as_ref().len() <= SHORT_ITEM_THRESHOLD)
+}