]> git.lizzy.rs Git - rust.git/commitdiff
Merge pull request #1647 from topecongiro/refactor-impl-format
authorNick Cameron <nrc@ncameron.org>
Mon, 12 Jun 2017 23:55:33 +0000 (11:55 +1200)
committerGitHub <noreply@github.com>
Mon, 12 Jun 2017 23:55:33 +0000 (11:55 +1200)
Refactor impl format

12 files changed:
src/expr.rs
src/items.rs
src/types.rs
tests/source/big-impl-rfc.rs [new file with mode: 0644]
tests/source/big-impl.rs [new file with mode: 0644]
tests/source/impls.rs
tests/target/big-impl-rfc.rs [new file with mode: 0644]
tests/target/big-impl.rs [new file with mode: 0644]
tests/target/configs-generics_indent-block.rs
tests/target/fn-custom-2.rs
tests/target/fn-custom-3.rs
tests/target/impls.rs

index cbe8671fd92252bf3d2bd2e2ee030151ebc2ff2b..40c678174a8c08698a86f50c21bed0a847ca8ffa 100644 (file)
@@ -2089,12 +2089,12 @@ enum StructLitField<'a> {
     // FIXME if context.config.struct_lit_style() == Visual, but we run out
     // of space, we should fall back to BlockIndent.
 }
+
 pub fn struct_lit_field_separator(config: &Config) -> &str {
     colon_spaces(config.space_before_struct_lit_field_colon(),
                  config.space_after_struct_lit_field_colon())
 }
 
-
 fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) -> Option<String> {
     let name = &field.ident.node.to_string();
     if field.is_shorthand {
index ac2f36ad76a0575997a1a87c7555be1e4849bc4d..020f04e30f392fa5ad79a9deb917d472534c91c9 100644 (file)
             last_line_width, format_unsafety, trim_newlines, stmt_expr, semicolon_for_expr,
             trimmed_last_line_width, colon_spaces, mk_sp};
 use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, list_helper,
-            DefinitiveListTactic, ListTactic, definitive_tactic, format_item_list};
+            DefinitiveListTactic, ListTactic, definitive_tactic};
 use expr::{is_empty_block, is_simple_block_stmt, rewrite_assign_rhs};
 use comment::{FindUncommented, contains_comment, rewrite_comment, recover_comment_removed};
 use visitor::FmtVisitor;
 use rewrite::{Rewrite, RewriteContext};
-use config::{Config, IndentStyle, Density, ReturnIndent, BraceStyle, Style, TypeDensity};
+use config::{Config, IndentStyle, Density, ReturnIndent, BraceStyle, Style};
+use types::join_bounds;
 
 use syntax::{ast, abi, ptr, symbol};
 use syntax::codemap::{Span, BytePos};
@@ -527,23 +528,20 @@ pub fn format_impl(context: &RewriteContext,
                    offset: Indent,
                    where_span_end: Option<BytePos>)
                    -> Option<String> {
-    if let ast::ItemKind::Impl(_, _, _, ref generics, ref trait_ref, _, ref items) = item.node {
+    if let ast::ItemKind::Impl(_, _, _, ref generics, _, _, ref items) = item.node {
         let mut result = String::new();
-        // First try to format the ref and type without a split at the 'for'.
-        let mut ref_and_type = try_opt!(format_impl_ref_and_type(context, item, offset, false));
-
-        // If there is a line break present in the first result format it again
-        // with a split at the 'for'. Skip this if there is no trait ref and
-        // therefore no 'for'.
-        if ref_and_type.contains('\n') && trait_ref.is_some() {
-            ref_and_type = try_opt!(format_impl_ref_and_type(context, item, offset, true));
-        }
+        let ref_and_type = try_opt!(format_impl_ref_and_type(context, item, offset));
         result.push_str(&ref_and_type);
 
-        let where_budget = try_opt!(context
-                                        .config
-                                        .max_width()
-                                        .checked_sub(last_line_width(&result)));
+        let where_budget = if result.contains('\n') {
+            context.config.max_width()
+        } else {
+            context
+                .config
+                .max_width()
+                .checked_sub(last_line_width(&result))
+                .unwrap_or(0)
+        };
         let where_clause_str = try_opt!(rewrite_where_clause(context,
                                                              &generics.where_clause,
                                                              context.config.item_brace_style(),
@@ -644,8 +642,7 @@ fn is_impl_single_line(context: &RewriteContext,
 
 fn format_impl_ref_and_type(context: &RewriteContext,
                             item: &ast::Item,
-                            offset: Indent,
-                            split_at_for: bool)
+                            offset: Indent)
                             -> Option<String> {
     if let ast::ItemKind::Impl(unsafety,
                                polarity,
@@ -665,69 +662,133 @@ fn format_impl_ref_and_type(context: &RewriteContext,
             Some(ref tr) => tr.path.span.lo,
             None => self_ty.span.lo,
         };
-        let generics_indent = offset + last_line_width(&result);
-        let shape = Shape::indented(generics_indent, context.config);
-        let generics_str = try_opt!(rewrite_generics(context, generics, shape, mk_sp(lo, hi)));
-        result.push_str(&generics_str);
-
-        if polarity == ast::ImplPolarity::Negative {
-            result.push_str(" !");
-        }
-        if let Some(ref trait_ref) = *trait_ref {
-            if polarity != ast::ImplPolarity::Negative {
-                result.push_str(" ");
-            }
-            let used_space = last_line_width(&result);
-            let budget = try_opt!(context.config.max_width().checked_sub(used_space));
-            let indent = offset + used_space;
-            result.push_str(&*try_opt!(trait_ref.rewrite(context, Shape::legacy(budget, indent))));
-
-            if split_at_for {
-                result.push('\n');
+        let shape = generics_shape_from_config(context.config,
+                                               Shape::indented(offset + last_line_width(&result),
+                                                               context.config),
+                                               0);
+        let generics_str =
+            try_opt!(rewrite_generics_inner(context, generics, shape, shape.width, mk_sp(lo, hi)));
 
-                // Add indentation of one additional tab.
-                let width = offset.block_indent + context.config.tab_spaces();
-                let for_indent = Indent::new(0, width);
-                result.push_str(&for_indent.to_string(context.config));
+        let polarity_str = if polarity == ast::ImplPolarity::Negative {
+            "!"
+        } else {
+            ""
+        };
 
-                result.push_str("for");
+        if let Some(ref trait_ref) = *trait_ref {
+            let result_len = result.len();
+            if let Some(trait_ref_str) =
+                rewrite_trait_ref(context,
+                                  &trait_ref,
+                                  offset,
+                                  &generics_str,
+                                  true,
+                                  polarity_str,
+                                  result_len) {
+                result.push_str(&trait_ref_str);
             } else {
-                result.push_str(" for");
+                let generics_str =
+                    try_opt!(rewrite_generics_inner(context, generics, shape, 0, mk_sp(lo, hi)));
+                result.push_str(&try_opt!(rewrite_trait_ref(context,
+                                                            &trait_ref,
+                                                            offset,
+                                                            &generics_str,
+                                                            false,
+                                                            polarity_str,
+                                                            result_len)));
             }
+        } else {
+            result.push_str(&generics_str);
         }
 
-        let mut used_space = last_line_width(&result);
-        if generics.where_clause.predicates.is_empty() {
+        // Try to put the self type in a single line.
+        // ` for`
+        let trait_ref_overhead = if trait_ref.is_some() { 4 } else { 0 };
+        let curly_brace_overhead = if generics.where_clause.predicates.is_empty() {
             // If there is no where clause adapt budget for type formatting to take space and curly
             // brace into account.
             match context.config.item_brace_style() {
-                BraceStyle::AlwaysNextLine => {}
-                BraceStyle::PreferSameLine => used_space += 2,
-                BraceStyle::SameLineWhere => used_space += 2,
+                BraceStyle::AlwaysNextLine => 0,
+                _ => 2,
             }
-        }
-
+        } else {
+            0
+        };
+        let used_space = last_line_width(&result) + trait_ref_overhead + curly_brace_overhead;
         // 1 = space before the type.
-        let budget = try_opt!(context.config.max_width().checked_sub(used_space + 1));
-        let indent = offset + result.len() + 1;
-        let self_ty_str = self_ty.rewrite(context, Shape::legacy(budget, indent));
-        if let Some(self_ty_str) = self_ty_str {
-            result.push_str(" ");
-            result.push_str(&self_ty_str);
-            return Some(result);
+        let budget = context
+            .config
+            .max_width()
+            .checked_sub(used_space + 1)
+            .unwrap_or(0);
+        if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) {
+            if !self_ty_str.contains('\n') {
+                if trait_ref.is_some() {
+                    result.push_str(" for ");
+                } else {
+                    result.push(' ');
+                }
+                result.push_str(&self_ty_str);
+                return Some(result);
+            }
         }
 
-        // Can't fit the self type on what's left of the line, so start a new one.
-        let indent = offset.block_indent(context.config);
-        result.push_str(&format!("\n{}", indent.to_string(context.config)));
-        result.push_str(&*try_opt!(self_ty.rewrite(context,
-                                                   Shape::indented(indent, context.config))));
+        // Couldn't fit the self type on a single line, put it on a new line.
+        result.push('\n');
+        // Add indentation of one additional tab.
+        let new_line_offset = offset.block_indent(context.config);
+        result.push_str(&new_line_offset.to_string(context.config));
+        if trait_ref.is_some() {
+            result.push_str("for ");
+        }
+        let budget = context.config.max_width() - last_line_width(&result);
+        let type_offset = match context.config.where_style() {
+            Style::Legacy => new_line_offset + trait_ref_overhead,
+            Style::Rfc => new_line_offset,
+        };
+        result.push_str(&*try_opt!(self_ty.rewrite(context, Shape::legacy(budget, type_offset))));
         Some(result)
     } else {
         unreachable!();
     }
 }
 
+fn rewrite_trait_ref(context: &RewriteContext,
+                     trait_ref: &ast::TraitRef,
+                     offset: Indent,
+                     generics_str: &str,
+                     retry: bool,
+                     polarity_str: &str,
+                     result_len: usize)
+                     -> Option<String> {
+    // 1 = space between generics and trait_ref
+    let used_space = 1 + polarity_str.len() +
+                     if generics_str.contains('\n') {
+                         last_line_width(&generics_str)
+                     } else {
+                         result_len + generics_str.len()
+                     };
+    let shape = Shape::indented(offset + used_space, context.config);
+    if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) {
+        if !(retry && trait_ref_str.contains('\n')) {
+            return Some(format!("{} {}{}", generics_str, polarity_str, &trait_ref_str));
+        }
+    }
+    // We could not make enough space for trait_ref, so put it on new line.
+    if !retry {
+        let offset = offset.block_indent(context.config);
+        let shape = Shape::indented(offset, context.config);
+        let trait_ref_str = try_opt!(trait_ref.rewrite(context, shape));
+        Some(format!("{}\n{}{}{}",
+                     generics_str,
+                     &offset.to_string(context.config),
+                     polarity_str,
+                     &trait_ref_str))
+    } else {
+        None
+    }
+}
+
 pub fn format_struct(context: &RewriteContext,
                      item_name: &str,
                      ident: ast::Ident,
@@ -777,8 +838,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent)
 
         let body_lo = context.codemap.span_after(item.span, "{");
 
-        let generics_indent = offset + last_line_width(&result);
-        let shape = Shape::indented(generics_indent, context.config);
+        let shape = Shape::indented(offset + last_line_width(&result), context.config);
         let generics_str =
             try_opt!(rewrite_generics(context, generics, shape, mk_sp(item.span.lo, body_lo)));
         result.push_str(&generics_str);
@@ -1024,10 +1084,9 @@ fn format_tuple_struct(context: &RewriteContext,
 
     let where_clause_str = match generics {
         Some(generics) => {
-            let generics_indent = offset + last_line_width(&header_str);
-            let shape = Shape::indented(generics_indent, context.config);
-            let generics_str =
-                try_opt!(rewrite_generics(context, generics, shape, mk_sp(span.lo, body_lo)));
+            let shape = Shape::indented(offset + last_line_width(&header_str), context.config);
+            let g_span = mk_sp(span.lo, body_lo);
+            let generics_str = try_opt!(rewrite_generics(context, generics, shape, g_span));
             result.push_str(&generics_str);
 
             let where_budget = try_opt!(context
@@ -1154,11 +1213,10 @@ pub fn rewrite_type_alias(context: &RewriteContext,
     result.push_str("type ");
     result.push_str(&ident.to_string());
 
-    let generics_indent = indent + result.len();
-    let generics_span = mk_sp(context.codemap.span_after(span, "type"), ty.span.lo);
-    let shape = try_opt!(Shape::indented(generics_indent, context.config).sub_width(" =".len()));
-    let generics_str = try_opt!(rewrite_generics(context, generics, shape, generics_span));
-
+    // 2 = `= `
+    let shape = try_opt!(Shape::indented(indent + result.len(), context.config).sub_width(2));
+    let g_span = mk_sp(context.codemap.span_after(span, "type"), ty.span.lo);
+    let generics_str = try_opt!(rewrite_generics(context, generics, shape, g_span));
     result.push_str(&generics_str);
 
     let where_budget = try_opt!(context
@@ -1392,23 +1450,14 @@ pub fn rewrite_associated_type(ident: ast::Ident,
     let prefix = format!("type {}", ident);
 
     let type_bounds_str = if let Some(ty_param_bounds) = ty_param_bounds_opt {
-        let joiner = match context.config.type_punctuation_density() {
-            TypeDensity::Compressed => "+",
-            TypeDensity::Wide => " + ",
-        };
+        let shape = Shape::legacy(context.config.max_width(), indent);
         let bounds: &[_] = ty_param_bounds;
-        let bound_str =
-            try_opt!(bounds
-                         .iter()
-                         .map(|ty_bound| {
-                                  ty_bound.rewrite(context,
-                                                   Shape::legacy(context.config.max_width(),
-                                                                 indent))
-                              })
-                         .collect::<Option<Vec<_>>>())
-                .join(joiner);
+        let bound_str = try_opt!(bounds
+                                     .iter()
+                                     .map(|ty_bound| ty_bound.rewrite(context, shape))
+                                     .collect::<Option<Vec<_>>>());
         if bounds.len() > 0 {
-            format!(": {}", bound_str)
+            format!(": {}", join_bounds(context, shape, &bound_str))
         } else {
             String::new()
         }
@@ -1633,10 +1682,9 @@ fn rewrite_fn_base(context: &RewriteContext,
     result.push_str(&ident.to_string());
 
     // Generics.
-    let generics_indent = indent + last_line_width(&result);
-    let generics_span = mk_sp(span.lo, span_for_return(&fd.output).lo);
-    let shape = Shape::indented(generics_indent, context.config);
-    let generics_str = try_opt!(rewrite_generics(context, generics, shape, generics_span));
+    let shape = Shape::indented(indent + last_line_width(&result), context.config);
+    let g_span = mk_sp(span.lo, span_for_return(&fd.output).lo);
+    let generics_str = try_opt!(rewrite_generics(context, generics, shape, g_span));
     result.push_str(&generics_str);
 
     let snuggle_angle_bracket = generics_str
@@ -2109,6 +2157,17 @@ fn rewrite_generics(context: &RewriteContext,
                     shape: Shape,
                     span: Span)
                     -> Option<String> {
+    let shape = generics_shape_from_config(context.config, shape, 0);
+    rewrite_generics_inner(context, generics, shape, shape.width, span)
+        .or_else(|| rewrite_generics_inner(context, generics, shape, 0, span))
+}
+
+fn rewrite_generics_inner(context: &RewriteContext,
+                          generics: &ast::Generics,
+                          shape: Shape,
+                          one_line_width: usize,
+                          span: Span)
+                          -> Option<String> {
     // FIXME: convert bounds to where clauses where they get too big or if
     // there is a where clause at all.
     let lifetimes: &[_] = &generics.lifetimes;
@@ -2117,21 +2176,9 @@ fn rewrite_generics(context: &RewriteContext,
         return Some(String::new());
     }
 
-    let offset = match context.config.generics_indent() {
-        IndentStyle::Block => shape.indent.block_only().block_indent(context.config),
-        // 1 = <
-        IndentStyle::Visual => shape.indent + 1,
-    };
-
-    let h_budget = try_opt!(shape.width.checked_sub(2));
-    // FIXME: might need to insert a newline if the generics are really long.
-
     // Strings for the generics.
-    let lt_strs = lifetimes
-        .iter()
-        .map(|lt| lt.rewrite(context, Shape::legacy(h_budget, offset)));
-    let ty_strs = tys.iter()
-        .map(|ty_param| ty_param.rewrite(context, Shape::legacy(h_budget, offset)));
+    let lt_strs = lifetimes.iter().map(|lt| lt.rewrite(context, shape));
+    let ty_strs = tys.iter().map(|ty_param| ty_param.rewrite(context, shape));
 
     // Extract comments between generics.
     let lt_spans = lifetimes.iter().map(|l| {
@@ -2153,21 +2200,64 @@ fn rewrite_generics(context: &RewriteContext,
                              |&(_, ref str)| str.clone(),
                              context.codemap.span_after(span, "<"),
                              span.hi);
-    let list_str =
-        try_opt!(format_item_list(items, Shape::legacy(h_budget, offset), context.config));
+    format_generics_item_list(context, items, shape, one_line_width)
+}
+
+pub fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) -> Shape {
+    Shape {
+        // 2 = `<>`
+        width: shape.width.checked_sub(offset + 2).unwrap_or(0),
+        ..match config.generics_indent() {
+              IndentStyle::Visual => shape.visual_indent(1 + offset),
+              IndentStyle::Block => shape.block().block_indent(config.tab_spaces()),
+          }
+    }
+}
+
+pub fn format_generics_item_list<I>(context: &RewriteContext,
+                                    items: I,
+                                    shape: Shape,
+                                    one_line_budget: usize)
+                                    -> Option<String>
+    where I: Iterator<Item = ListItem>
+{
+    let item_vec = items.collect::<Vec<_>>();
+
+    let fmt = ListFormatting {
+        tactic: definitive_tactic(&item_vec, ListTactic::HorizontalVertical, one_line_budget),
+        separator: ",",
+        trailing_separator: if context.config.generics_indent() == IndentStyle::Visual {
+            SeparatorTactic::Never
+        } else {
+            context.config.trailing_comma()
+        },
+        shape: shape,
+        ends_with_newline: false,
+        config: context.config,
+    };
 
-    let result = if context.config.generics_indent() != IndentStyle::Visual &&
-                    list_str.contains('\n') {
+    let list_str = try_opt!(write_list(&item_vec, &fmt));
+
+    Some(wrap_generics_with_angle_brackets(context, &list_str, shape.indent))
+}
+
+pub fn wrap_generics_with_angle_brackets(context: &RewriteContext,
+                                         list_str: &str,
+                                         list_offset: Indent)
+                                         -> String {
+    if context.config.generics_indent() == IndentStyle::Block &&
+       (list_str.contains('\n') || list_str.ends_with(',')) {
         format!("<\n{}{}\n{}>",
-                offset.to_string(context.config),
+                list_offset.to_string(context.config),
                 list_str,
-                shape.indent.block_only().to_string(context.config))
+                list_offset
+                    .block_unindent(context.config)
+                    .to_string(context.config))
     } else if context.config.spaces_within_angle_brackets() {
         format!("< {} >", list_str)
     } else {
         format!("<{}>", list_str)
-    };
-    Some(result)
+    }
 }
 
 fn rewrite_trait_bounds(context: &RewriteContext,
@@ -2179,20 +2269,11 @@ fn rewrite_trait_bounds(context: &RewriteContext,
     if bounds.is_empty() {
         return Some(String::new());
     }
-    let joiner = match context.config.type_punctuation_density() {
-        TypeDensity::Compressed => "+",
-        TypeDensity::Wide => " + ",
-    };
     let bound_str = try_opt!(bounds
                                  .iter()
                                  .map(|ty_bound| ty_bound.rewrite(&context, shape))
-                                 .collect::<Option<Vec<_>>>())
-        .join(joiner);
-
-    let mut result = String::new();
-    result.push_str(": ");
-    result.push_str(&bound_str);
-    Some(result)
+                                 .collect::<Option<Vec<_>>>());
+    Some(format!(": {}", join_bounds(context, shape, &bound_str)))
 }
 
 fn rewrite_where_clause_rfc_style(context: &RewriteContext,
index 7dfc4337178999076a416d58c659aca24824efb8..d2405f390f1d2e0d93a9fc07793c9d1b030c49e3 100644 (file)
 
 use {Shape, Spanned};
 use codemap::SpanUtils;
-use lists::{format_item_list, itemize_list, format_fn_args};
+use items::{format_generics_item_list, generics_shape_from_config};
+use lists::{itemize_list, format_fn_args};
 use rewrite::{Rewrite, RewriteContext};
-use utils::{extra_offset, format_mutability, colon_spaces, wrap_str, mk_sp};
+use utils::{extra_offset, format_mutability, colon_spaces, wrap_str, mk_sp, last_line_width};
 use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple_type};
 use config::TypeDensity;
 
@@ -213,31 +214,25 @@ fn rewrite_segment(path_context: PathContext,
                     ""
                 };
 
-                // 1 for <
-                let extra_offset = 1 + separator.len();
-                // 1 for >
-                // TODO bad visual indent
-                let list_shape = try_opt!(try_opt!(shape.shrink_left(extra_offset)).sub_width(1))
-                    .visual_indent(0);
-
+                let generics_shape =
+                    generics_shape_from_config(context.config, shape, separator.len());
                 let items = itemize_list(context.codemap,
                                          param_list.into_iter(),
                                          ">",
                                          |param| param.get_span().lo,
                                          |param| param.get_span().hi,
-                                         |seg| seg.rewrite(context, list_shape),
+                                         |seg| seg.rewrite(context, generics_shape),
                                          list_lo,
                                          span_hi);
-                let list_str = try_opt!(format_item_list(items, list_shape, context.config));
+                let generics_str = try_opt!(format_generics_item_list(context,
+                                                                      items,
+                                                                      generics_shape,
+                                                                      generics_shape.width));
 
                 // Update position of last bracket.
                 *span_lo = next_span_lo;
 
-                if context.config.spaces_within_angle_brackets() && list_str.len() > 0 {
-                    format!("{}< {} >", separator, list_str)
-                } else {
-                    format!("{}<{}>", separator, list_str)
-                }
+                format!("{}{}", separator, generics_str)
             }
             ast::PathParameters::Parenthesized(ref data) => {
                 let output = match data.output {
@@ -367,22 +362,15 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
                                                             .collect::<Option<Vec<_>>>())
                         .join(", ");
 
-                    let joiner = match context.config.type_punctuation_density() {
-                        TypeDensity::Compressed => "+",
-                        TypeDensity::Wide => " + ",
-                    };
                     // 6 = "for<> ".len()
                     let used_width = lifetime_str.len() + type_str.len() + colon.len() + 6;
-                    let budget = try_opt!(shape.width.checked_sub(used_width));
-                    let bounds_str: String = try_opt!(
-                        bounds
-                            .iter()
-                            .map(|ty_bound| {
-                                ty_bound.rewrite(context,
-                                                 Shape::legacy(budget, shape.indent + used_width))
-                            })
-                            .collect::<Option<Vec<_>>>()
-                    ).join(joiner);
+                    let ty_shape = try_opt!(shape.block_left(used_width));
+                    let bounds: Vec<_> =
+                        try_opt!(bounds
+                                     .iter()
+                                     .map(|ty_bound| ty_bound.rewrite(context, ty_shape))
+                                     .collect());
+                    let bounds_str = join_bounds(context, ty_shape, &bounds);
 
                     if context.config.spaces_within_angle_brackets() && lifetime_str.len() > 0 {
                         format!("for< {} > {}{}{}",
@@ -394,21 +382,14 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
                         format!("for<{}> {}{}{}", lifetime_str, type_str, colon, bounds_str)
                     }
                 } else {
-                    let joiner = match context.config.type_punctuation_density() {
-                        TypeDensity::Compressed => "+",
-                        TypeDensity::Wide => " + ",
-                    };
                     let used_width = type_str.len() + colon.len();
-                    let budget = try_opt!(shape.width.checked_sub(used_width));
-                    let bounds_str: String = try_opt!(
-                        bounds
-                            .iter()
-                            .map(|ty_bound| {
-                                ty_bound.rewrite(context,
-                                                 Shape::legacy(budget, shape.indent + used_width))
-                            })
-                            .collect::<Option<Vec<_>>>()
-                    ).join(joiner);
+                    let ty_shape = try_opt!(shape.block_left(used_width));
+                    let bounds: Vec<_> =
+                        try_opt!(bounds
+                                     .iter()
+                                     .map(|ty_bound| ty_bound.rewrite(context, ty_shape))
+                                     .collect());
+                    let bounds_str = join_bounds(context, ty_shape, &bounds);
 
                     format!("{}{}{}", type_str, colon, bounds_str)
                 }
@@ -463,11 +444,11 @@ fn rewrite_bounded_lifetime<'b, I>(lt: &ast::Lifetime,
                                             .map(|b| b.rewrite(context, shape))
                                             .collect());
         let colon = type_bound_colon(context);
-        let joiner = match context.config.type_punctuation_density() {
-            TypeDensity::Compressed => "+",
-            TypeDensity::Wide => " + ",
-        };
-        let result = format!("{}{}{}", result, colon, appendix.join(joiner));
+        let overhead = last_line_width(&result) + colon.len();
+        let result = format!("{}{}{}",
+                             result,
+                             colon,
+                             join_bounds(context, try_opt!(shape.sub_width(overhead)), &appendix));
         wrap_str(result, context.config.max_width(), shape)
     }
 }
@@ -499,12 +480,8 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
 
 impl Rewrite for ast::TyParamBounds {
     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
-        let joiner = match context.config.type_punctuation_density() {
-            TypeDensity::Compressed => "+",
-            TypeDensity::Wide => " + ",
-        };
         let strs: Vec<_> = try_opt!(self.iter().map(|b| b.rewrite(context, shape)).collect());
-        wrap_str(strs.join(joiner), context.config.max_width(), shape)
+        join_bounds(context, shape, &strs).rewrite(context, shape)
     }
 }
 
@@ -519,24 +496,12 @@ fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
         result.push_str(&attr_str);
         result.push_str(&self.ident.to_string());
         if !self.bounds.is_empty() {
-            if context.config.space_before_bound() {
-                result.push_str(" ");
-            }
-            result.push_str(":");
-            if context.config.space_after_bound_colon() {
-                result.push_str(" ");
-            }
-            let joiner = match context.config.type_punctuation_density() {
-                TypeDensity::Compressed => "+",
-                TypeDensity::Wide => " + ",
-            };
-            let bounds: String = try_opt!(self.bounds
-                                              .iter()
-                                              .map(|ty_bound| ty_bound.rewrite(context, shape))
-                                              .collect::<Option<Vec<_>>>())
-                .join(joiner);
-
-            result.push_str(&bounds);
+            result.push_str(type_bound_colon(context));
+            let strs: Vec<_> = try_opt!(self.bounds
+                                            .iter()
+                                            .map(|ty_bound| ty_bound.rewrite(context, shape))
+                                            .collect());
+            result.push_str(&join_bounds(context, shape, &strs));
         }
         if let Some(ref def) = self.default {
 
@@ -737,3 +702,19 @@ fn rewrite_bare_fn(bare_fn: &ast::BareFnTy,
 
     Some(result)
 }
+
+pub fn join_bounds(context: &RewriteContext, shape: Shape, type_strs: &Vec<String>) -> String {
+    // Try to join types in a single line
+    let joiner = match context.config.type_punctuation_density() {
+        TypeDensity::Compressed => "+",
+        TypeDensity::Wide => " + ",
+    };
+    let result = type_strs.join(joiner);
+    if result.contains('\n') || result.len() > shape.width {
+        let joiner_indent = shape.indent.block_indent(context.config);
+        let joiner = format!("\n{}+ ", joiner_indent.to_string(context.config));
+        type_strs.join(&joiner)
+    } else {
+        result
+    }
+}
diff --git a/tests/source/big-impl-rfc.rs b/tests/source/big-impl-rfc.rs
new file mode 100644 (file)
index 0000000..167f654
--- /dev/null
@@ -0,0 +1,114 @@
+// rustfmt-fn_args_layout: Block
+// rustfmt-fn_call_style: Block
+// rustfmt-generics_indent: Block
+// rustfmt-where_style: Rfc
+
+// #1357
+impl<
+    'a,
+    Select,
+    From,
+    Distinct,
+    Where,
+    Order,
+    Limit,
+    Offset,
+    Groupby,
+    DB,
+> InternalBoxedDsl<'a, DB>
+    for SelectStatement<
+        Select,
+        From,
+        Distinct,
+        Where,
+        Order,
+        Limit,
+        Offset,
+        GroupBy,
+    > where
+        DB: Backend,
+        Select: QueryFragment<DB> + SelectableExpression<From> + 'a,
+        Distinct: QueryFragment<DB> + 'a,
+        Where: Into<Option<Box<QueryFragment<DB> + 'a>>>,
+        Order: QueryFragment<DB> + 'a,
+        Limit: QueryFragment<DB> + 'a,
+        Offset: QueryFragment<DB> + 'a,
+{
+    type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>;
+
+    fn internal_into_boxed(self) -> Self::Output {
+        BoxedSelectStatement::new(
+            Box::new(self.select),
+            self.from,
+            Box::new(self.distinct),
+            self.where_clause.into(),
+            Box::new(self.order),
+            Box::new(self.limit),
+            Box::new(self.offset),
+        )
+    }
+}
+
+// #1369
+impl<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo for Bar {
+    fn foo() {}
+}
+impl Foo<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> for Bar {
+    fn foo() {}
+}
+impl<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> for Bar {
+    fn foo() {}
+}
+impl<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo for Bar<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> {
+    fn foo() {}
+}
+impl Foo<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> for Bar<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> {
+    fn foo() {}
+}
+impl<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> for Bar<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> {
+    fn foo() {}
+}
diff --git a/tests/source/big-impl.rs b/tests/source/big-impl.rs
new file mode 100644 (file)
index 0000000..c36b7e6
--- /dev/null
@@ -0,0 +1,104 @@
+// #1357
+impl<
+    'a,
+    Select,
+    From,
+    Distinct,
+    Where,
+    Order,
+    Limit,
+    Offset,
+    Groupby,
+    DB,
+> InternalBoxedDsl<'a, DB>
+    for SelectStatement<
+        Select,
+        From,
+        Distinct,
+        Where,
+        Order,
+        Limit,
+        Offset,
+        GroupBy,
+    > where
+        DB: Backend,
+        Select: QueryFragment<DB> + SelectableExpression<From> + 'a,
+        Distinct: QueryFragment<DB> + 'a,
+        Where: Into<Option<Box<QueryFragment<DB> + 'a>>>,
+        Order: QueryFragment<DB> + 'a,
+        Limit: QueryFragment<DB> + 'a,
+        Offset: QueryFragment<DB> + 'a,
+{
+    type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>;
+
+    fn internal_into_boxed(self) -> Self::Output {
+        BoxedSelectStatement::new(
+            Box::new(self.select),
+            self.from,
+            Box::new(self.distinct),
+            self.where_clause.into(),
+            Box::new(self.order),
+            Box::new(self.limit),
+            Box::new(self.offset),
+        )
+    }
+}
+
+// #1369
+impl<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo for Bar {
+    fn foo() {}
+}
+impl Foo<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> for Bar {
+    fn foo() {}
+}
+impl<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> for Bar {
+    fn foo() {}
+}
+impl<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo for Bar<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> {
+    fn foo() {}
+}
+impl Foo<
+    ExcessivelyLongGenericName,
+      ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> for Bar<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> {
+    fn foo() {}
+}
+impl<ExcessivelyLongGenericName,
+     ExcessivelyLongGenericName,
+     AnotherExcessivelyLongGenericName> Foo<ExcessivelyLongGenericName,
+                                            ExcessivelyLongGenericName,
+                                            AnotherExcessivelyLongGenericName>
+    for Bar<ExcessivelyLongGenericName,
+            ExcessivelyLongGenericName,
+            AnotherExcessivelyLongGenericName> {
+    fn foo() {}
+}
index 1c38a2ff28474d0dd8fd7ec1f8d1ea5fe4aa5b42..17588fb9d3500c87a2e317880f2cb45369a58024 100644 (file)
@@ -122,3 +122,20 @@ fn foo() {}
 impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
     fn drop() {}
 }
+
+// #1168
+pub trait Number: Copy + Eq +      Not<Output = Self> + Shl<u8, Output = Self> +
+    Shr<u8, Output = Self>       +
+    BitAnd<Self, Output=Self> +    BitOr<Self, Output=Self>  + BitAndAssign + BitOrAssign
+
+
+
+{
+    // test
+    fn zero() -> Self;
+}
+
+// #1642
+pub trait SomeTrait : Clone + Eq + PartialEq + Ord + PartialOrd + Default + Hash + Debug + Display + Write + Read + FromStr {
+    // comment
+}
diff --git a/tests/target/big-impl-rfc.rs b/tests/target/big-impl-rfc.rs
new file mode 100644 (file)
index 0000000..108968f
--- /dev/null
@@ -0,0 +1,77 @@
+// rustfmt-fn_args_layout: Block
+// rustfmt-fn_call_style: Block
+// rustfmt-generics_indent: Block
+// rustfmt-where_style: Rfc
+
+// #1357
+impl<'a, Select, From, Distinct, Where, Order, Limit, Offset, Groupby, DB> InternalBoxedDsl<'a, DB>
+    for SelectStatement<Select, From, Distinct, Where, Order, Limit, Offset, GroupBy>
+where
+    DB: Backend,
+    Select: QueryFragment<DB> + SelectableExpression<From> + 'a,
+    Distinct: QueryFragment<DB> + 'a,
+    Where: Into<Option<Box<QueryFragment<DB> + 'a>>>,
+    Order: QueryFragment<DB> + 'a,
+    Limit: QueryFragment<DB> + 'a,
+    Offset: QueryFragment<DB> + 'a,
+{
+    type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>;
+
+    fn internal_into_boxed(self) -> Self::Output {
+        BoxedSelectStatement::new(
+            Box::new(self.select),
+            self.from,
+            Box::new(self.distinct),
+            self.where_clause.into(),
+            Box::new(self.order),
+            Box::new(self.limit),
+            Box::new(self.offset),
+        )
+    }
+}
+
+// #1369
+impl<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName> Foo
+    for Bar {
+    fn foo() {}
+}
+impl Foo<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName>
+    for Bar {
+    fn foo() {}
+}
+impl<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName>
+    for Bar {
+    fn foo() {}
+}
+impl<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName> Foo
+    for Bar<
+        ExcessivelyLongGenericName,
+        ExcessivelyLongGenericName,
+        AnotherExcessivelyLongGenericName,
+    > {
+    fn foo() {}
+}
+impl Foo<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName>
+    for Bar<
+        ExcessivelyLongGenericName,
+        ExcessivelyLongGenericName,
+        AnotherExcessivelyLongGenericName,
+    > {
+    fn foo() {}
+}
+impl<
+    ExcessivelyLongGenericName,
+    ExcessivelyLongGenericName,
+    AnotherExcessivelyLongGenericName,
+> Foo<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName>
+    for Bar<
+        ExcessivelyLongGenericName,
+        ExcessivelyLongGenericName,
+        AnotherExcessivelyLongGenericName,
+    > {
+    fn foo() {}
+}
diff --git a/tests/target/big-impl.rs b/tests/target/big-impl.rs
new file mode 100644 (file)
index 0000000..afe2571
--- /dev/null
@@ -0,0 +1,62 @@
+// #1357
+impl<'a, Select, From, Distinct, Where, Order, Limit, Offset, Groupby, DB> InternalBoxedDsl<'a, DB>
+    for SelectStatement<Select, From, Distinct, Where, Order, Limit, Offset, GroupBy>
+    where DB: Backend,
+          Select: QueryFragment<DB> + SelectableExpression<From> + 'a,
+          Distinct: QueryFragment<DB> + 'a,
+          Where: Into<Option<Box<QueryFragment<DB> + 'a>>>,
+          Order: QueryFragment<DB> + 'a,
+          Limit: QueryFragment<DB> + 'a,
+          Offset: QueryFragment<DB> + 'a
+{
+    type Output = BoxedSelectStatement<'a, Select::SqlTypeForSelect, From, DB>;
+
+    fn internal_into_boxed(self) -> Self::Output {
+        BoxedSelectStatement::new(Box::new(self.select),
+                                  self.from,
+                                  Box::new(self.distinct),
+                                  self.where_clause.into(),
+                                  Box::new(self.order),
+                                  Box::new(self.limit),
+                                  Box::new(self.offset))
+    }
+}
+
+// #1369
+impl<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName> Foo
+    for Bar {
+    fn foo() {}
+}
+impl Foo<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName>
+    for Bar {
+    fn foo() {}
+}
+impl<ExcessivelyLongGenericName,
+     ExcessivelyLongGenericName,
+     AnotherExcessivelyLongGenericName> Foo<ExcessivelyLongGenericName,
+                                            ExcessivelyLongGenericName,
+                                            AnotherExcessivelyLongGenericName> for Bar {
+    fn foo() {}
+}
+impl<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName> Foo
+    for Bar<ExcessivelyLongGenericName,
+            ExcessivelyLongGenericName,
+            AnotherExcessivelyLongGenericName> {
+    fn foo() {}
+}
+impl Foo<ExcessivelyLongGenericName, ExcessivelyLongGenericName, AnotherExcessivelyLongGenericName>
+    for Bar<ExcessivelyLongGenericName,
+            ExcessivelyLongGenericName,
+            AnotherExcessivelyLongGenericName> {
+    fn foo() {}
+}
+impl<ExcessivelyLongGenericName,
+     ExcessivelyLongGenericName,
+     AnotherExcessivelyLongGenericName> Foo<ExcessivelyLongGenericName,
+                                            ExcessivelyLongGenericName,
+                                            AnotherExcessivelyLongGenericName>
+    for Bar<ExcessivelyLongGenericName,
+            ExcessivelyLongGenericName,
+            AnotherExcessivelyLongGenericName> {
+    fn foo() {}
+}
index 789243c02c3008456a71f4ef8aa3804eee80dc39..848e59c7c0ae05b1fbb3a09f50114307f0c70678 100644 (file)
@@ -8,7 +8,7 @@ fn lorem<
     Amet: Eq = usize,
     Adipiscing: Eq = usize,
     Consectetur: Eq = usize,
-    Elit: Eq = usize
+    Elit: Eq = usize,
 >(ipsum: Ipsum,
     dolor: Dolor,
     sit: Sit,
index e9ba39f666cba7ab18855023610b59bf282531b3..f0923bd1a853bf694af7d05ac0476bcc7b27328f 100644 (file)
@@ -16,7 +16,7 @@ fn foo(
 fn bar<
     'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb,
     TTTTTTTTTTTTT,
-    UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW
+    UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW,
 >(
     a: Aaaaaaaaaaaaaaa,
 ) {
@@ -51,7 +51,7 @@ fn foo(
     fn bar<
         'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb,
         TTTTTTTTTTTTT,
-        UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW
+        UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW,
     >(
         a: Aaaaaaaaaaaaaaa,
     ) {
@@ -69,7 +69,7 @@ struct Foo<
     TTTTTTTTTTTTTTTTTTTTTTTTTTTT,
     UUUUUUUUUUUUUUUUUUUUUU,
     VVVVVVVVVVVVVVVVVVVVVVVVVVV,
-    WWWWWWWWWWWWWWWWWWWWWWWW
+    WWWWWWWWWWWWWWWWWWWWWWWW,
 > {
     foo: Foo,
 }
index a29aac411ba96f1d92054d86a128a8e19641fae9..4d26c9b695192e2b01ddb58ed78178fa248fc882 100644 (file)
@@ -16,7 +16,7 @@ fn foo(
 fn bar<
     'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb,
     TTTTTTTTTTTTT,
-    UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW
+    UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW,
 >(
     a: Aaaaaaaaaaaaaaa,
 ) {
@@ -53,7 +53,7 @@ fn foo(
     fn bar<
         'a: 'bbbbbbbbbbbbbbbbbbbbbbbbbbb,
         TTTTTTTTTTTTT,
-        UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW
+        UUUUUUUUUUUUUUUUUUUU: WWWWWWWWWWWWWWWWWWWWWWWW,
     >(
         a: Aaaaaaaaaaaaaaa,
     ) {
@@ -65,7 +65,7 @@ struct Foo<
     TTTTTTTTTTTTTTTTTTTTTTTTTTTT,
     UUUUUUUUUUUUUUUUUUUUUU,
     VVVVVVVVVVVVVVVVVVVVVVVVVVV,
-    WWWWWWWWWWWWWWWWWWWWWWWW
+    WWWWWWWWWWWWWWWWWWWWWWWW,
 > {
     foo: Foo,
 }
index f8b9f43aeb00d7418b7bb0f545e7590bbcfdbfc1..654d4be8967b6bbbde6f817a04971f3544fc0775 100644 (file)
@@ -126,8 +126,8 @@ fn eq(&self, other: &Self) {
     impl<T> PartialEq for S<T> where T: PartialEq {}
 }
 
-impl<BorrowType, K, V, NodeType, HandleType> Handle<NodeRef<BorrowType, K, V, NodeType>,
-                                                    HandleType> {
+impl<BorrowType, K, V, NodeType, HandleType>
+    Handle<NodeRef<BorrowType, K, V, NodeType>, HandleType> {
 }
 
 impl<BorrowType, K, V, NodeType, HandleType> PartialEq
@@ -154,3 +154,35 @@ fn foo() {}
 impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable<K, V> {
     fn drop() {}
 }
+
+// #1168
+pub trait Number
+    : Copy
+    + Eq
+    + Not<Output = Self>
+    + Shl<u8, Output = Self>
+    + Shr<u8, Output = Self>
+    + BitAnd<Self, Output = Self>
+    + BitOr<Self, Output = Self>
+    + BitAndAssign
+    + BitOrAssign {
+    // test
+    fn zero() -> Self;
+}
+
+// #1642
+pub trait SomeTrait
+    : Clone
+    + Eq
+    + PartialEq
+    + Ord
+    + PartialOrd
+    + Default
+    + Hash
+    + Debug
+    + Display
+    + Write
+    + Read
+    + FromStr {
+    // comment
+}