diff --git a/src/expr.rs b/src/expr.rs index 6e740f43f82..0e6b8f1bc1e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1698,7 +1698,8 @@ fn rewrite_match_body( ); match (orig_body, next_line_body) { (Some(ref orig_str), Some(ref next_line_str)) - if forbid_same_line || prefer_next_line(orig_str, next_line_str) => + if forbid_same_line + || prefer_next_line(orig_str, next_line_str, RhsTactics::Default) => { combine_next_line_body(next_line_str) } @@ -2514,6 +2515,15 @@ fn rewrite_assignment( rewrite_assign_rhs(context, lhs_str, rhs, shape) } +/// Controls where to put the rhs. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RhsTactics { + /// Use heuristics. + Default, + /// Put the rhs on the next line if it uses multiple line. + ForceNextLine, +} + // The left hand side must contain everything up to, and including, the // assignment operator. pub fn rewrite_assign_rhs, R: Rewrite>( @@ -2521,6 +2531,16 @@ pub fn rewrite_assign_rhs, R: Rewrite>( lhs: S, ex: &R, shape: Shape, +) -> Option { + rewrite_assign_rhs_with(context, lhs, ex, shape, RhsTactics::Default) +} + +pub fn rewrite_assign_rhs_with, R: Rewrite>( + context: &RewriteContext, + lhs: S, + ex: &R, + shape: Shape, + rhs_tactics: RhsTactics, ) -> Option { let lhs = lhs.into(); let last_line_width = last_line_width(&lhs) @@ -2536,15 +2556,22 @@ pub fn rewrite_assign_rhs, R: Rewrite>( offset: shape.offset + last_line_width + 1, ..shape }); - let rhs = choose_rhs(context, ex, orig_shape, ex.rewrite(context, orig_shape))?; + let rhs = choose_rhs( + context, + ex, + orig_shape, + ex.rewrite(context, orig_shape), + rhs_tactics, + )?; Some(lhs + &rhs) } -pub fn choose_rhs( +fn choose_rhs( context: &RewriteContext, expr: &R, shape: Shape, orig_rhs: Option, + rhs_tactics: RhsTactics, ) -> Option { match orig_rhs { Some(ref new_str) if !new_str.contains('\n') && new_str.len() <= shape.width => { @@ -2566,7 +2593,9 @@ pub fn choose_rhs( { Some(format!(" {}", orig_rhs)) } - (Some(ref orig_rhs), Some(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs) => { + (Some(ref orig_rhs), Some(ref new_rhs)) + if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => + { Some(format!("{}{}", new_indent_str, new_rhs)) } (None, Some(ref new_rhs)) => Some(format!("{}{}", new_indent_str, new_rhs)), @@ -2577,8 +2606,9 @@ pub fn choose_rhs( } } -fn prefer_next_line(orig_rhs: &str, next_line_rhs: &str) -> bool { - !next_line_rhs.contains('\n') || count_newlines(orig_rhs) > count_newlines(next_line_rhs) + 1 +fn prefer_next_line(orig_rhs: &str, next_line_rhs: &str, rhs_tactics: RhsTactics) -> bool { + rhs_tactics == RhsTactics::ForceNextLine || !next_line_rhs.contains('\n') + || count_newlines(orig_rhs) > count_newlines(next_line_rhs) + 1 } fn rewrite_expr_addrof( diff --git a/src/items.rs b/src/items.rs index d3286864ce7..b639697194b 100644 --- a/src/items.rs +++ b/src/items.rs @@ -23,13 +23,14 @@ use codemap::{LineRangeUtils, SpanUtils}; use comment::{combine_strs_with_missing_comments, contains_comment, recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment, FindUncommented}; use config::{BraceStyle, Config, Density, IndentStyle}; -use expr::{format_expr, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, ExprType}; +use expr::{format_expr, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, + rewrite_assign_rhs_with, ExprType, RhsTactics}; use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator}; use rewrite::{Rewrite, RewriteContext}; use overflow; use shape::{Indent, Shape}; use spanned::Spanned; -use types::join_bounds; +use types::TraitTyParamBounds; use utils::{colon_spaces, contains_skip, first_line_width, format_abi, format_constness, format_defaultness, format_mutability, format_unsafety, format_visibility, is_attributes_extendable, last_line_contains_single_line_comment, @@ -919,60 +920,55 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) return None; } } - let trait_bound_str = rewrite_trait_bounds( - context, - type_param_bounds, - Shape::indented(offset, context.config), - )?; - // If the trait, generics, and trait bound cannot fit on the same line, - // put the trait bounds on an indented new line - if offset.width() + last_line_width(&result) + trait_bound_str.len() - > context.config.comment_width() - { - let trait_indent = offset.block_only().block_indent(context.config); - result.push_str(&trait_indent.to_string_with_newline(context.config)); + if !type_param_bounds.is_empty() { + result = rewrite_assign_rhs_with( + context, + result + ":", + &TraitTyParamBounds::new(type_param_bounds), + shape, + RhsTactics::ForceNextLine, + )?; } - result.push_str(&trait_bound_str); - let where_density = - if context.config.indent_style() == IndentStyle::Block && result.is_empty() { + // Rewrite where clause. + if !generics.where_clause.predicates.is_empty() { + let where_density = if context.config.indent_style() == IndentStyle::Block { Density::Compressed } else { Density::Tall }; - let where_budget = context.budget(last_line_width(&result)); - let pos_before_where = if type_param_bounds.is_empty() { - generics.where_clause.span.lo() + let where_budget = context.budget(last_line_width(&result)); + let pos_before_where = if type_param_bounds.is_empty() { + generics.where_clause.span.lo() + } else { + type_param_bounds[type_param_bounds.len() - 1].span().hi() + }; + let option = WhereClauseOption::snuggled(&generics_str); + let where_clause_str = rewrite_where_clause( + context, + &generics.where_clause, + context.config.brace_style(), + Shape::legacy(where_budget, offset.block_only()), + where_density, + "{", + None, + pos_before_where, + option, + false, + )?; + // If the where clause cannot fit on the same line, + // put the where clause on a new line + if !where_clause_str.contains('\n') + && last_line_width(&result) + where_clause_str.len() + offset.width() + > context.config.comment_width() + { + let width = offset.block_indent + context.config.tab_spaces() - 1; + let where_indent = Indent::new(0, width); + result.push_str(&where_indent.to_string_with_newline(context.config)); + } + result.push_str(&where_clause_str); } else { - type_param_bounds[type_param_bounds.len() - 1].span().hi() - }; - let option = WhereClauseOption::snuggled(&generics_str); - let where_clause_str = rewrite_where_clause( - context, - &generics.where_clause, - context.config.brace_style(), - Shape::legacy(where_budget, offset.block_only()), - where_density, - "{", - None, - pos_before_where, - option, - false, - )?; - // If the where clause cannot fit on the same line, - // put the where clause on a new line - if !where_clause_str.contains('\n') - && last_line_width(&result) + where_clause_str.len() + offset.width() - > context.config.comment_width() - { - let width = offset.block_indent + context.config.tab_spaces() - 1; - let where_indent = Indent::new(0, width); - result.push_str(&where_indent.to_string_with_newline(context.config)); - } - result.push_str(&where_clause_str); - - if generics.where_clause.predicates.is_empty() { let item_snippet = context.snippet(item.span); if let Some(lo) = item_snippet.chars().position(|c| c == '/') { // 1 = `{` @@ -995,7 +991,9 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) } match context.config.brace_style() { - _ if last_line_contains_single_line_comment(&result) => { + _ if last_line_contains_single_line_comment(&result) + || last_line_width(&result) + 2 > context.budget(offset.width()) => + { result.push_str(&offset.to_string_with_newline(context.config)); } BraceStyle::AlwaysNextLine => { @@ -1003,8 +1001,8 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) } BraceStyle::PreferSameLine => result.push(' '), BraceStyle::SameLineWhere => { - if !where_clause_str.is_empty() - && (!trait_items.is_empty() || result.contains('\n')) + if result.contains('\n') + || (!generics.where_clause.predicates.is_empty() && !trait_items.is_empty()) { result.push_str(&offset.to_string_with_newline(context.config)); } else { @@ -1585,16 +1583,12 @@ pub fn rewrite_associated_type( let prefix = format!("type {}", ident); let type_bounds_str = if let Some(bounds) = ty_param_bounds_opt { - // 2 = ": ".len() - let shape = Shape::indented(indent, context.config).offset_left(prefix.len() + 2)?; - let bound_str = bounds - .iter() - .map(|ty_bound| ty_bound.rewrite(context, shape)) - .collect::>>()?; - if !bounds.is_empty() { - format!(": {}", join_bounds(context, shape, &bound_str)) - } else { + if bounds.is_empty() { String::new() + } else { + // 2 = ": ".len() + let shape = Shape::indented(indent, context.config).offset_left(prefix.len() + 2)?; + bounds.rewrite(context, shape).map(|s| format!(": {}", s))? } } else { String::new() @@ -2329,21 +2323,6 @@ pub fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) } } -fn rewrite_trait_bounds( - context: &RewriteContext, - bounds: &[ast::TyParamBound], - shape: Shape, -) -> Option { - if bounds.is_empty() { - return Some(String::new()); - } - let bound_str = bounds - .iter() - .map(|ty_bound| ty_bound.rewrite(context, shape)) - .collect::>>()?; - Some(format!(": {}", join_bounds(context, shape, &bound_str))) -} - fn rewrite_where_clause_rfc_style( context: &RewriteContext, where_clause: &ast::WhereClause, diff --git a/src/types.rs b/src/types.rs index ac7990b9c0b..676095ff4ce 100644 --- a/src/types.rs +++ b/src/types.rs @@ -18,7 +18,8 @@ use syntax::symbol::keywords; use codemap::SpanUtils; use config::{IndentStyle, TypeDensity}; -use expr::{rewrite_pair, rewrite_tuple, rewrite_unary_prefix, PairParts, ToExpr}; +use expr::{rewrite_assign_rhs, rewrite_pair, rewrite_tuple, rewrite_unary_prefix, PairParts, + ToExpr}; use lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; use macros::{rewrite_macro, MacroPosition}; use overflow; @@ -431,64 +432,35 @@ impl Rewrite for ast::WherePredicate { .. }) => { let type_str = bounded_ty.rewrite(context, shape)?; - - let colon = type_bound_colon(context); - - if let Some(lifetime_str) = + let colon = type_bound_colon(context).trim_right(); + let lhs = if let Some(lifetime_str) = rewrite_lifetime_param(context, shape, bound_generic_params) { - // 6 = "for<> ".len() - let used_width = lifetime_str.len() + type_str.len() + colon.len() + 6; - let ty_shape = shape.offset_left(used_width)?; - let bounds = 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_parens_and_brackets() && !lifetime_str.is_empty() { - format!( - "for< {} > {}{}{}", - lifetime_str, type_str, colon, bounds_str - ) + format!("for< {} > {}{}", lifetime_str, type_str, colon) } else { - format!("for<{}> {}{}{}", lifetime_str, type_str, colon, bounds_str) + format!("for<{}> {}{}", lifetime_str, type_str, colon) } } else { - let used_width = type_str.len() + colon.len(); - let ty_shape = match context.config.indent_style() { - IndentStyle::Visual => shape.block_left(used_width)?, - IndentStyle::Block => shape, - }; - let bounds = bounds - .iter() - .map(|ty_bound| ty_bound.rewrite(context, ty_shape)) - .collect::>>()?; - let overhead = type_str.len() + colon.len(); - let bounds_str = join_bounds(context, ty_shape.sub_width(overhead)?, &bounds); + format!("{}{}", type_str, colon) + }; - format!("{}{}{}", type_str, colon, bounds_str) - } + rewrite_assign_rhs(context, lhs, bounds, shape)? } ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, ref bounds, .. - }) => rewrite_bounded_lifetime(lifetime, bounds.iter(), context, shape)?, + }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?, ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { - let lhs_ty_str = lhs_ty.rewrite(context, shape)?; - // 3 = " = ".len() - let used_width = 3 + lhs_ty_str.len(); - let budget = shape.width.checked_sub(used_width)?; - let rhs_ty_str = - rhs_ty.rewrite(context, Shape::legacy(budget, shape.indent + used_width))?; - format!("{} = {}", lhs_ty_str, rhs_ty_str) + let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; + rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, shape)? } }; @@ -498,35 +470,28 @@ impl Rewrite for ast::WherePredicate { impl Rewrite for ast::LifetimeDef { fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { - rewrite_bounded_lifetime(&self.lifetime, self.bounds.iter(), context, shape) + rewrite_bounded_lifetime(&self.lifetime, &self.bounds, context, shape) } } -fn rewrite_bounded_lifetime<'b, I>( +fn rewrite_bounded_lifetime( lt: &ast::Lifetime, - bounds: I, + bounds: &[ast::Lifetime], context: &RewriteContext, shape: Shape, -) -> Option -where - I: ExactSizeIterator, -{ +) -> Option { let result = lt.rewrite(context, shape)?; if bounds.len() == 0 { Some(result) } else { - let appendix = bounds - .into_iter() - .map(|b| b.rewrite(context, shape)) - .collect::>>()?; let colon = type_bound_colon(context); let overhead = last_line_width(&result) + colon.len(); let result = format!( "{}{}{}", result, colon, - join_bounds(context, shape.sub_width(overhead)?, &appendix) + join_bounds(context, shape.sub_width(overhead)?, bounds, true)? ); Some(result) } @@ -552,12 +517,21 @@ impl Rewrite for ast::Lifetime { } } +/// A simple wrapper over type param bounds in trait. +#[derive(new)] +pub struct TraitTyParamBounds<'a> { + inner: &'a ast::TyParamBounds, +} + +impl<'a> Rewrite for TraitTyParamBounds<'a> { + fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { + join_bounds(context, shape, self.inner, false) + } +} + impl Rewrite for ast::TyParamBounds { fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { - let strs = self.iter() - .map(|b| b.rewrite(context, shape)) - .collect::>>()?; - Some(join_bounds(context, shape, &strs)) + join_bounds(context, shape, self, true) } } @@ -572,11 +546,7 @@ impl Rewrite for ast::TyParam { result.push_str(&self.ident.to_string()); if !self.bounds.is_empty() { result.push_str(type_bound_colon(context)); - let strs = self.bounds - .iter() - .map(|ty_bound| ty_bound.rewrite(context, shape)) - .collect::>>()?; - result.push_str(&join_bounds(context, shape, &strs)); + result.push_str(&self.bounds.rewrite(context, shape)?) } if let Some(ref def) = self.default { let eq_str = match context.config.type_punctuation_density() { @@ -794,20 +764,44 @@ fn rewrite_bare_fn( Some(result) } -pub fn join_bounds(context: &RewriteContext, shape: Shape, type_strs: &[String]) -> String { +fn join_bounds( + context: &RewriteContext, + shape: Shape, + items: &[T], + need_indent: bool, +) -> Option +where + T: Rewrite, +{ // Try to join types in a single line let joiner = match context.config.type_punctuation_density() { TypeDensity::Compressed => "+", TypeDensity::Wide => " + ", }; + let type_strs = items + .iter() + .map(|item| item.rewrite(context, shape)) + .collect::>>()?; 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!("{}+ ", joiner_indent.to_string_with_newline(context.config)); - type_strs.join(&joiner) - } else { - result + if items.len() == 1 || (!result.contains('\n') && result.len() <= shape.width) { + return Some(result); } + + // We need to use multiple lines. + let (type_strs, offset) = if need_indent { + // Rewrite with additional indentation. + let nested_shape = shape.block_indent(context.config.tab_spaces()); + let type_strs = items + .iter() + .map(|item| item.rewrite(context, nested_shape)) + .collect::>>()?; + (type_strs, nested_shape.indent) + } else { + (type_strs, shape.indent) + }; + + let joiner = format!("{}+ ", offset.to_string_with_newline(context.config)); + Some(type_strs.join(&joiner)) } pub fn can_be_overflowed_type(context: &RewriteContext, ty: &ast::Ty, len: usize) -> bool { diff --git a/tests/source/issue-2506.rs b/tests/source/issue-2506.rs index fd0ecc9c661..94a927580f6 100644 --- a/tests/source/issue-2506.rs +++ b/tests/source/issue-2506.rs @@ -7,7 +7,7 @@ fn main() { fn f1(a: Box) {} // checks if line wrap works correctly - trait Very_______________________Long__________________Name____________________Trait { + trait Very_______________________Long__________________Name_______________________________Trait { fn method(&self) -> u64; } diff --git a/tests/source/where-clause-rfc.rs b/tests/source/where-clause-rfc.rs index e41e9a6cea1..d915fccf1a0 100644 --- a/tests/source/where-clause-rfc.rs +++ b/tests/source/where-clause-rfc.rs @@ -56,3 +56,20 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // ... } } + +// #2497 +fn handle_update<'a, Tab, Conn, R, C>(executor: &Executor>>, change_set: &'a C) -> ExecutionResult +where &'a C: Identifiable + AsChangeset + HasTable, + <&'a C as AsChangeset>::Changeset: QueryFragment, + Tab: Table + HasTable
, + Tab::PrimaryKey: EqAll<<&'a C as Identifiable>::Id>, + Tab::FromClause: QueryFragment, + Tab: FindDsl<<&'a C as Identifiable>::Id>, + Find::Id>: IntoUpdateTarget
, + ::Id> as IntoUpdateTarget>::WhereClause: QueryFragment, + Tab::Query: FilterDsl<::Id>>::Output>, + Filter::Id>>::Output>: LimitDsl, + Limit::Id>>::Output>>: QueryDsl + BoxedDsl< 'a, Conn::Backend, Output = BoxedSelectStatement<'a, R::SqlType, Tab, Conn::Backend>>, + R: LoadingHandler + GraphQLType, { + unimplemented!() +} diff --git a/tests/target/impls.rs b/tests/target/impls.rs index 0bc28acf7ea..2175b5d7bd9 100644 --- a/tests/target/impls.rs +++ b/tests/target/impls.rs @@ -174,8 +174,8 @@ impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { } // #1168 -pub trait Number - : Copy +pub trait Number: + Copy + Eq + Not + Shl @@ -183,14 +183,15 @@ pub trait Number + BitAnd + BitOr + BitAndAssign - + BitOrAssign { + + BitOrAssign +{ // test fn zero() -> Self; } // #1642 -pub trait SomeTrait - : Clone +pub trait SomeTrait: + Clone + Eq + PartialEq + Ord @@ -201,7 +202,8 @@ pub trait SomeTrait + Display + Write + Read - + FromStr { + + FromStr +{ // comment } diff --git a/tests/target/issue-2506.rs b/tests/target/issue-2506.rs index ee2debee9fb..1e1971db85f 100644 --- a/tests/target/issue-2506.rs +++ b/tests/target/issue-2506.rs @@ -7,8 +7,8 @@ fn main() { fn f1(a: Box) {} // checks if line wrap works correctly - trait Very_______________________Long__________________Name____________________Trait - { + trait Very_______________________Long__________________Name_______________________________Trait + { fn method(&self) -> u64; } diff --git a/tests/target/trait.rs b/tests/target/trait.rs index 133e8babedb..d1ee43f4e3f 100644 --- a/tests/target/trait.rs +++ b/tests/target/trait.rs @@ -65,8 +65,7 @@ where { } -trait FooBar - : Tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +trait FooBar: Tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt where J: Bar, { @@ -106,7 +105,8 @@ trait MyTrait< BBBBBBBBBBBBBBBBBBBB, CCCCCCCCCCCCCCCCCCCC, DDDDDDDDDDDDDDDDDDDD, -> { +> +{ fn foo() {} } diff --git a/tests/target/where-clause-rfc.rs b/tests/target/where-clause-rfc.rs index 1e83d5b3abc..a41d82c8e41 100644 --- a/tests/target/where-clause-rfc.rs +++ b/tests/target/where-clause-rfc.rs @@ -126,3 +126,33 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // ... } } + +// #2497 +fn handle_update<'a, Tab, Conn, R, C>( + executor: &Executor>>, + change_set: &'a C, +) -> ExecutionResult +where + &'a C: Identifiable + AsChangeset + HasTable
, + <&'a C as AsChangeset>::Changeset: QueryFragment, + Tab: Table + HasTable
, + Tab::PrimaryKey: EqAll<<&'a C as Identifiable>::Id>, + Tab::FromClause: QueryFragment, + Tab: FindDsl<<&'a C as Identifiable>::Id>, + Find::Id>: IntoUpdateTarget
, + ::Id> as IntoUpdateTarget>::WhereClause: + QueryFragment, + Tab::Query: FilterDsl<::Id>>::Output>, + Filter::Id>>::Output>: LimitDsl, + Limit::Id>>::Output>>: + QueryDsl + + BoxedDsl< + 'a, + Conn::Backend, + Output = BoxedSelectStatement<'a, R::SqlType, Tab, Conn::Backend>, + >, + R: LoadingHandler + + GraphQLType, +{ + unimplemented!() +} diff --git a/tests/target/where-clause.rs b/tests/target/where-clause.rs index def18a0c11a..eb2f8d5e6e8 100644 --- a/tests/target/where-clause.rs +++ b/tests/target/where-clause.rs @@ -81,17 +81,17 @@ struct AlwaysOnNextLine pub trait SomeTrait where T: Something - + Sync - + Send - + Display - + Debug - + Copy - + Hash - + Debug - + Display - + Write - + Read - + FromStr + + Sync + + Send + + Display + + Debug + + Copy + + Hash + + Debug + + Display + + Write + + Read + + FromStr { }