Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 288d7db

Browse files
authored
Merge pull request rust-lang#3467 from topecongiro/issue-3465
Fix bad performance on deeply nested binary expressions
2 parents 929d8a9 + a931397 commit 288d7db

File tree

5 files changed

+139
-44
lines changed

5 files changed

+139
-44
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ matrix:
2828
- env: INTEGRATION=mdbook
2929
- env: INTEGRATION=packed_simd
3030
- env: INTEGRATION=rand
31-
- env: INTEGRATION=rust-clippy
3231
- env: INTEGRATION=rust-semverver
3332
- env: INTEGRATION=stdsimd TARGET=x86_64-unknown-linux-gnu
3433
- env: INTEGRATION=tempdir
3534
- env: INTEGRATION=futures-rs
3635
allow_failures:
36+
# Doesn't build - keep this in allow_failures as it's fragile to breaking changes of rustc.
37+
- env: INTEGRATION=rust-clippy
3738
# Doesn't build - seems to be because of an option
3839
- env: INTEGRATION=packed_simd
3940
# Doesn't build - a temporal build failure due to breaking changes in the nightly compilre
@@ -45,7 +46,7 @@ script:
4546
- |
4647
if [ -z ${INTEGRATION} ]; then
4748
cargo build
48-
cargo test
49+
RUST_MIN_STACK=8388608 cargo test
4950
else
5051
./ci/integration.sh
5152
fi

appveyor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# and modified (mainly removal of deployment) to suit rustfmt.
33

44
environment:
5+
RUST_MIN_STACK: 8388608
56
global:
67
PROJECT_NAME: rustfmt
78
matrix:

src/pairs.rs

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,11 @@ pub(crate) fn rewrite_all_pairs(
3434
shape: Shape,
3535
context: &RewriteContext<'_>,
3636
) -> Option<String> {
37-
// First we try formatting on one line.
38-
if let Some(list) = expr.flatten(false) {
39-
if let Some(r) = rewrite_pairs_one_line(&list, shape, context) {
40-
return Some(r);
41-
}
42-
}
43-
44-
// We can't format on line, so try many. When we flatten here we make sure
45-
// to only flatten pairs with the same operator, that way we don't
46-
// necessarily need one line per sub-expression, but we don't do anything
47-
// too funny wrt precedence.
48-
expr.flatten(true)
49-
.and_then(|list| rewrite_pairs_multiline(&list, shape, context))
37+
expr.flatten(context, shape).and_then(|list| {
38+
// First we try formatting on one line.
39+
rewrite_pairs_one_line(&list, shape, context)
40+
.or_else(|| rewrite_pairs_multiline(&list, shape, context))
41+
})
5042
}
5143

5244
// This may return a multi-line result since we allow the last expression to go
@@ -61,22 +53,23 @@ fn rewrite_pairs_one_line<T: Rewrite>(
6153
let mut result = String::new();
6254
let base_shape = shape.block();
6355

64-
for (e, s) in list.list.iter().zip(list.separators.iter()) {
65-
let cur_shape = base_shape.offset_left(last_line_width(&result))?;
66-
let rewrite = e.rewrite(context, cur_shape)?;
56+
for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) {
57+
if let Some(rewrite) = rewrite {
58+
if !is_single_line(&rewrite) || result.len() > shape.width {
59+
return None;
60+
}
6761

68-
if !is_single_line(&rewrite) || result.len() > shape.width {
62+
result.push_str(&rewrite);
63+
result.push(' ');
64+
result.push_str(s);
65+
result.push(' ');
66+
} else {
6967
return None;
7068
}
71-
72-
result.push_str(&rewrite);
73-
result.push(' ');
74-
result.push_str(s);
75-
result.push(' ');
7669
}
7770

7871
let prefix_len = result.len();
79-
let last = list.list.last().unwrap();
72+
let last = list.list.last()?.0;
8073
let cur_shape = base_shape.offset_left(last_line_width(&result))?;
8174
let last_rewrite = last.rewrite(context, cur_shape)?;
8275
result.push_str(&last_rewrite);
@@ -112,10 +105,9 @@ fn rewrite_pairs_multiline<T: Rewrite>(
112105
let indent_str = nested_shape.indent.to_string_with_newline(context.config);
113106
let mut result = String::new();
114107

115-
let rewrite = list.list[0].rewrite(context, shape)?;
116-
result.push_str(&rewrite);
108+
result.push_str(&list.list[0].1.as_ref()?);
117109

118-
for (e, s) in list.list[1..].iter().zip(list.separators.iter()) {
110+
for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) {
119111
// The following test checks if we should keep two subexprs on the same
120112
// line. We do this if not doing so would create an orphan and there is
121113
// enough space to do so.
@@ -139,24 +131,20 @@ fn rewrite_pairs_multiline<T: Rewrite>(
139131
}
140132
}
141133

142-
let nested_overhead = s.len() + 1;
143-
let line_shape = match context.config.binop_separator() {
134+
match context.config.binop_separator() {
144135
SeparatorPlace::Back => {
145136
result.push(' ');
146137
result.push_str(s);
147138
result.push_str(&indent_str);
148-
nested_shape.sub_width(nested_overhead)?
149139
}
150140
SeparatorPlace::Front => {
151141
result.push_str(&indent_str);
152142
result.push_str(s);
153143
result.push(' ');
154-
nested_shape.offset_left(nested_overhead)?
155144
}
156-
};
145+
}
157146

158-
let rewrite = e.rewrite(context, line_shape)?;
159-
result.push_str(&rewrite);
147+
result.push_str(&default_rw.as_ref()?);
160148
}
161149
Some(result)
162150
}
@@ -250,27 +238,46 @@ where
250238

251239
// A pair which forms a tree and can be flattened (e.g., binops).
252240
trait FlattenPair: Rewrite + Sized {
253-
// If `_same_op` is `true`, then we only combine binops with the same
254-
// operator into the list. E.g,, if the source is `a * b + c`, if `_same_op`
255-
// is true, we make `[(a * b), c]` if `_same_op` is false, we make
256-
// `[a, b, c]`
257-
fn flatten(&self, _same_op: bool) -> Option<PairList<'_, '_, Self>> {
241+
fn flatten(&self, _: &RewriteContext<'_>, _: Shape) -> Option<PairList<'_, '_, Self>> {
258242
None
259243
}
260244
}
261245

262246
struct PairList<'a, 'b, T: Rewrite> {
263-
list: Vec<&'b T>,
247+
list: Vec<(&'b T, Option<String>)>,
264248
separators: Vec<&'a str>,
265249
}
266250

267251
impl FlattenPair for ast::Expr {
268-
fn flatten(&self, same_op: bool) -> Option<PairList<'_, '_, ast::Expr>> {
252+
fn flatten(
253+
&self,
254+
context: &RewriteContext<'_>,
255+
shape: Shape,
256+
) -> Option<PairList<'_, '_, ast::Expr>> {
269257
let top_op = match self.node {
270258
ast::ExprKind::Binary(op, _, _) => op.node,
271259
_ => return None,
272260
};
273261

262+
let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| {
263+
if is_first {
264+
return node.rewrite(context, shape);
265+
}
266+
let nested_overhead = sep + 1;
267+
let rhs_offset = shape.rhs_overhead(&context.config);
268+
let nested_shape = (match context.config.indent_style() {
269+
IndentStyle::Visual => shape.visual_indent(0),
270+
IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
271+
})
272+
.with_max_width(&context.config)
273+
.sub_width(rhs_offset)?;
274+
let default_shape = match context.config.binop_separator() {
275+
SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?,
276+
SeparatorPlace::Front => nested_shape.offset_left(nested_overhead)?,
277+
};
278+
node.rewrite(context, default_shape)
279+
};
280+
274281
// Turn a tree of binop expressions into a list using a depth-first,
275282
// in-order traversal.
276283
let mut stack = vec![];
@@ -279,12 +286,14 @@ impl FlattenPair for ast::Expr {
279286
let mut node = self;
280287
loop {
281288
match node.node {
282-
ast::ExprKind::Binary(op, ref lhs, _) if !same_op || op.node == top_op => {
289+
ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => {
283290
stack.push(node);
284291
node = lhs;
285292
}
286293
_ => {
287-
list.push(node);
294+
let op_len = separators.last().map_or(0, |s: &&str| s.len());
295+
let rw = default_rewrite(node, op_len, list.is_empty());
296+
list.push((node, rw));
288297
if let Some(pop) = stack.pop() {
289298
match pop.node {
290299
ast::ExprKind::Binary(op, _, ref rhs) => {

tests/source/issue-3465.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
fn main() {
2+
((((((((((((((((((((((((((((((((((((((((((0) + 1) + 1)
3+
+ 1)
4+
+ 1)
5+
+ 1)
6+
+ 1)
7+
+ 1)
8+
+ 1)
9+
+ 1)
10+
+ 1)
11+
+ 1)
12+
+ 1)
13+
+ 1)
14+
+ 1)
15+
+ 1)
16+
+ 1)
17+
+ 1)
18+
+ 1)
19+
+ 1)
20+
+ 1)
21+
+ 1)
22+
+ 1)
23+
+ 1)
24+
+ 1)
25+
+ 1)
26+
+ 1)
27+
+ 1)
28+
+ 1)
29+
+ 1)
30+
+ 1)
31+
+ 1)
32+
+ 1)
33+
+ 1)
34+
+ 1)
35+
+ 1)
36+
+ 1)
37+
+ 1)
38+
+ 1)
39+
+ 1)
40+
+ 1)
41+
+ 1);
42+
}

tests/target/issue-3465.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
fn main() {
2+
((((((((((((((((((((((((((((((((((((((((((0) + 1) + 1)
3+
+ 1)
4+
+ 1)
5+
+ 1)
6+
+ 1)
7+
+ 1)
8+
+ 1)
9+
+ 1)
10+
+ 1)
11+
+ 1)
12+
+ 1)
13+
+ 1)
14+
+ 1)
15+
+ 1)
16+
+ 1)
17+
+ 1)
18+
+ 1)
19+
+ 1)
20+
+ 1)
21+
+ 1)
22+
+ 1)
23+
+ 1)
24+
+ 1)
25+
+ 1)
26+
+ 1)
27+
+ 1)
28+
+ 1)
29+
+ 1)
30+
+ 1)
31+
+ 1)
32+
+ 1)
33+
+ 1)
34+
+ 1)
35+
+ 1)
36+
+ 1)
37+
+ 1)
38+
+ 1)
39+
+ 1)
40+
+ 1)
41+
+ 1);
42+
}

0 commit comments

Comments
 (0)