Skip to content

Commit cf66692

Browse files
committed
Add "type_separator" option to control placement of "+" in types
Fixes rust-lang#4979 Fixes rust-lang#2055
1 parent 4b9d637 commit cf66692

23 files changed

+955
-113
lines changed

Configurations.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,56 @@ fn main() {
25732573
}
25742574
```
25752575

2576+
## `type_separator`
2577+
2578+
Where to put an operator when a type-level expression goes multiline.
2579+
2580+
- **Default value**: `"Front"`
2581+
- **Possible values**: `"Front"`, `"Back"`
2582+
- **Stable**: No
2583+
2584+
#### `"Front"` (default):
2585+
2586+
```rust
2587+
pub trait Foo:
2588+
Add
2589+
+ AddAssign
2590+
+ Clone
2591+
+ Copy
2592+
+ Debug
2593+
+ Default
2594+
+ Eq
2595+
+ Hash
2596+
+ Ord
2597+
+ PartialEq
2598+
+ PartialOrd
2599+
+ Sized
2600+
{
2601+
//
2602+
}
2603+
```
2604+
2605+
#### `"Back"`:
2606+
2607+
```rust
2608+
pub trait Foo:
2609+
Add +
2610+
AddAssign +
2611+
Clone +
2612+
Copy +
2613+
Debug +
2614+
Default +
2615+
Eq +
2616+
Hash +
2617+
Ord +
2618+
PartialEq +
2619+
PartialOrd +
2620+
Sized
2621+
{
2622+
//
2623+
}
2624+
```
2625+
25762626
## `use_small_heuristics`
25772627

25782628
This option can be used to simplify the management and bulk updates of the granular width configuration settings ([`fn_call_width`](#fn_call_width), [`attr_fn_like_width`](#attr_fn_like_width), [`struct_lit_width`](#struct_lit_width), [`struct_variant_width`](#struct_variant_width), [`array_width`](#array_width), [`chain_width`](#chain_width), [`single_line_if_else_max_width`](#single_line_if_else_max_width)), that respectively control when formatted constructs are multi-lined/vertical based on width.

src/comment.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,26 @@ pub(crate) fn combine_strs_with_missing_comments(
159159
span: Span,
160160
shape: Shape,
161161
allow_extend: bool,
162+
) -> Option<String> {
163+
combine_lines_with_missing_comments(
164+
context,
165+
prev_str,
166+
next_str,
167+
span,
168+
shape,
169+
false,
170+
allow_extend,
171+
)
172+
}
173+
174+
pub(crate) fn combine_lines_with_missing_comments(
175+
context: &RewriteContext<'_>,
176+
prev_str: &str,
177+
next_str: &str,
178+
span: Span,
179+
shape: Shape,
180+
allow_multiline_join: bool,
181+
allow_extend: bool,
162182
) -> Option<String> {
163183
trace!(
164184
"combine_strs_with_missing_comments `{}` `{}` {:?} {:?}",
@@ -171,7 +191,8 @@ pub(crate) fn combine_strs_with_missing_comments(
171191
let mut result =
172192
String::with_capacity(prev_str.len() + next_str.len() + shape.indent.width() + 128);
173193
result.push_str(prev_str);
174-
let mut allow_one_line = !prev_str.contains('\n') && !next_str.contains('\n');
194+
let contains_nl = prev_str.contains('\n') || next_str.contains('\n');
195+
let mut allow_line_join = allow_multiline_join || !contains_nl;
175196
let first_sep =
176197
if prev_str.is_empty() || next_str.is_empty() || trimmed_last_line_width(prev_str) == 0 {
177198
""
@@ -221,14 +242,16 @@ pub(crate) fn combine_strs_with_missing_comments(
221242
result.push_str(&first_sep);
222243
result.push_str(&missing_comment);
223244

224-
let second_sep = if missing_comment.is_empty() || next_str.is_empty() {
245+
let skip_second_sep =
246+
missing_comment.is_empty() || next_str.is_empty() || next_str.starts_with("\n");
247+
let second_sep = if skip_second_sep {
225248
Cow::from("")
226249
} else if missing_comment.starts_with("//") {
227250
indent.to_string_with_newline(config)
228251
} else {
229252
one_line_width += missing_comment.len() + first_sep.len() + 1;
230-
allow_one_line &= !missing_comment.starts_with("//") && !missing_comment.contains('\n');
231-
if prefer_same_line && allow_one_line && one_line_width <= shape.width {
253+
allow_line_join &= !missing_comment.starts_with("//") && !missing_comment.contains('\n');
254+
if prefer_same_line && allow_line_join && one_line_width <= shape.width {
232255
Cow::from(" ")
233256
} else {
234257
indent.to_string_with_newline(config)

src/config/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ create_config! {
102102
spaces_around_ranges: bool, false, false, "Put spaces around the .. and ..= range operators";
103103
binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
104104
"Where to put a binary operator when a binary expression goes multiline";
105+
type_separator: SeparatorPlace, SeparatorPlace::Front, false,
106+
"Where to put the operator when a type-level expression goes multiline";
105107

106108
// Misc.
107109
remove_nested_parens: bool, true, true, "Remove nested parens";
@@ -589,6 +591,7 @@ space_before_colon = false
589591
space_after_colon = true
590592
spaces_around_ranges = false
591593
binop_separator = "Front"
594+
type_separator = "Front"
592595
remove_nested_parens = true
593596
combine_control_expr = true
594597
overflow_delimited_expr = false

src/expr.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,18 +1941,36 @@ pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>(
19411941
) -> Option<String> {
19421942
let lhs = lhs.into();
19431943
let contains_comment = contains_comment(context.snippet(between_span));
1944-
let shape = if contains_comment {
1945-
shape.block_left(context.config.tab_spaces())?
1946-
} else {
1947-
shape
1948-
};
1949-
let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_tactics)?;
19501944

1951-
if contains_comment {
1952-
let rhs = rhs.trim_start();
1953-
combine_strs_with_missing_comments(context, &lhs, &rhs, between_span, shape, allow_extend)
1954-
} else {
1955-
Some(lhs + &rhs)
1945+
match (contains_comment, rhs_tactics) {
1946+
(true, RhsTactics::ForceNextLineWithoutIndent) => {
1947+
let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_tactics)?;
1948+
let rhs = rhs.trim_start();
1949+
1950+
combine_strs_with_missing_comments(
1951+
context,
1952+
&lhs,
1953+
&rhs,
1954+
between_span,
1955+
shape.block_left(context.config.tab_spaces())?,
1956+
allow_extend,
1957+
)
1958+
}
1959+
(true, RhsTactics::Default | RhsTactics::AllowOverflow) => {
1960+
let shape = shape.block_left(context.config.tab_spaces())?;
1961+
let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_tactics)?;
1962+
let rhs = rhs.trim_start();
1963+
1964+
combine_strs_with_missing_comments(
1965+
context,
1966+
&lhs,
1967+
&rhs,
1968+
between_span,
1969+
shape,
1970+
allow_extend,
1971+
)
1972+
}
1973+
(false, _) => rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_tactics),
19561974
}
19571975
}
19581976

@@ -2014,7 +2032,7 @@ fn shape_from_rhs_tactic(
20142032
match rhs_tactic {
20152033
RhsTactics::ForceNextLineWithoutIndent => shape
20162034
.with_max_width(context.config)
2017-
.sub_width(shape.indent.width()),
2035+
.sub_width(shape.indent.block_indent(context.config).width()),
20182036
RhsTactics::Default | RhsTactics::AllowOverflow => {
20192037
Shape::indented(shape.indent.block_indent(context.config), context.config)
20202038
.sub_width(shape.rhs_overhead(context.config))

src/items.rs

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,24 +1041,47 @@ pub(crate) fn format_trait(
10411041
rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?;
10421042
result.push_str(&generics_str);
10431043

1044-
// FIXME(#2055): rustfmt fails to format when there are comments between trait bounds.
1044+
let combine_comment_span =
1045+
|lo: BytePos, hi: BytePos, lhs: String, rhs: &str| -> Option<String> {
1046+
let span = mk_sp(lo, hi);
1047+
1048+
if contains_comment(context.snippet(span)) {
1049+
combine_strs_with_missing_comments(context, &lhs, rhs, span, shape, true)
1050+
} else {
1051+
Some(lhs + rhs)
1052+
}
1053+
};
1054+
10451055
if !generic_bounds.is_empty() {
1046-
let ident_hi = context
1047-
.snippet_provider
1048-
.span_after(item.span, &item.ident.as_str());
1049-
let bound_hi = generic_bounds.last().unwrap().span().hi();
1050-
let snippet = context.snippet(mk_sp(ident_hi, bound_hi));
1051-
if contains_comment(snippet) {
1052-
return None;
1053-
}
1056+
// Recover the comment before the colon.
1057+
let comment_lo = generics.span.hi();
1058+
let comment_hi = context.snippet_provider.span_before(item.span, ":");
1059+
result = combine_comment_span(comment_lo, comment_hi, result, ":")?;
1060+
1061+
// Recover the comment after the colon.
1062+
let comment_lo = context.snippet_provider.span_after(item.span, ":");
1063+
let comment_hi = generic_bounds[0].span().lo();
1064+
let comment_span = mk_sp(comment_lo, comment_hi);
10541065

1055-
result = rewrite_assign_rhs_with(
1066+
result = rewrite_assign_rhs_with_comments(
10561067
context,
1057-
result + ":",
1068+
&result,
10581069
generic_bounds,
10591070
shape,
10601071
RhsTactics::ForceNextLineWithoutIndent,
1072+
comment_span,
1073+
true,
10611074
)?;
1075+
1076+
// Recover the comment following the bounds.
1077+
let comment_lo = generic_bounds[generic_bounds.len() - 1].span().hi();
1078+
let comment_hi = if generics.where_clause.predicates.is_empty() {
1079+
body_lo - BytePos(1)
1080+
} else {
1081+
generics.where_clause.span.lo()
1082+
};
1083+
1084+
result = combine_comment_span(comment_lo, comment_hi, result, "")?;
10621085
}
10631086

10641087
// Rewrite where-clause.
@@ -1067,9 +1090,9 @@ pub(crate) fn format_trait(
10671090

10681091
let where_budget = context.budget(last_line_width(&result));
10691092
let pos_before_where = if generic_bounds.is_empty() {
1070-
generics.where_clause.span.lo()
1093+
generics.span.hi()
10711094
} else {
1072-
generic_bounds[generic_bounds.len() - 1].span().hi()
1095+
generics.where_clause.span.lo()
10731096
};
10741097
let option = WhereClauseOption::snuggled(&generics_str);
10751098
let where_clause_str = rewrite_where_clause(
@@ -1079,7 +1102,7 @@ pub(crate) fn format_trait(
10791102
Shape::legacy(where_budget, offset.block_only()),
10801103
where_on_new_line,
10811104
"{",
1082-
None,
1105+
Some(body_lo),
10831106
pos_before_where,
10841107
option,
10851108
)?;
@@ -1094,25 +1117,13 @@ pub(crate) fn format_trait(
10941117
result.push_str(&where_indent.to_string_with_newline(context.config));
10951118
}
10961119
result.push_str(&where_clause_str);
1097-
} else {
1120+
} else if generic_bounds.is_empty() {
10981121
let item_snippet = context.snippet(item.span);
10991122
if let Some(lo) = item_snippet.find('/') {
11001123
// 1 = `{`
11011124
let comment_hi = body_lo - BytePos(1);
11021125
let comment_lo = item.span.lo() + BytePos(lo as u32);
1103-
if comment_lo < comment_hi {
1104-
match recover_missing_comment_in_span(
1105-
mk_sp(comment_lo, comment_hi),
1106-
Shape::indented(offset, context.config),
1107-
context,
1108-
last_line_width(&result),
1109-
) {
1110-
Some(ref missing_comment) if !missing_comment.is_empty() => {
1111-
result.push_str(missing_comment);
1112-
}
1113-
_ => (),
1114-
}
1115-
}
1126+
result = combine_comment_span(comment_lo, comment_hi, result, "")?;
11161127
}
11171128
}
11181129

0 commit comments

Comments
 (0)