Skip to content

Commit 5e1c00c

Browse files
committed
Implement wrapping rules to force else on a newline in let-else
1 parent b5b1fa4 commit 5e1c00c

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

Diff for: src/expr.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1027,12 +1027,14 @@ impl<'a> ControlFlow<'a> {
10271027

10281028
/// Rewrite the `else` keyword with surrounding comments.
10291029
///
1030+
/// force_newline_else: whether or not to rewrite the `else` keyword on a newline.
10301031
/// is_last: true if this is an `else` and `false` if this is an `else if` block.
10311032
/// context: rewrite context
10321033
/// span: Span between the end of the last expression and the start of the else block,
10331034
/// which contains the `else` keyword
10341035
/// shape: Shape
10351036
pub(crate) fn rewrite_else_kw_with_comments(
1037+
force_newline_else: bool,
10361038
is_last: bool,
10371039
context: &RewriteContext<'_>,
10381040
span: Span,
@@ -1048,6 +1050,7 @@ pub(crate) fn rewrite_else_kw_with_comments(
10481050

10491051
let newline_sep = &shape.indent.to_string_with_newline(context.config);
10501052
let before_sep = match context.config.control_brace_style() {
1053+
_ if force_newline_else => newline_sep.as_ref(),
10511054
ControlBraceStyle::AlwaysNextLine | ControlBraceStyle::ClosingNextLine => {
10521055
newline_sep.as_ref()
10531056
}
@@ -1132,6 +1135,7 @@ impl<'a> Rewrite for ControlFlow<'a> {
11321135
};
11331136

11341137
let else_kw = rewrite_else_kw_with_comments(
1138+
false,
11351139
last_in_chain,
11361140
context,
11371141
self.block.span.between(else_block.span),

Diff for: src/items.rs

+46-1
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,14 @@ impl Rewrite for ast::Local {
126126
)?;
127127

128128
if let Some(block) = else_block {
129+
let else_kw_span = init.span.between(block.span);
130+
let force_newline_else =
131+
!same_line_else_kw_and_brace(&result, context, else_kw_span, nested_shape);
129132
let else_kw = rewrite_else_kw_with_comments(
133+
force_newline_else,
130134
true,
131135
context,
132-
init.span.between(block.span),
136+
else_kw_span,
133137
shape,
134138
);
135139
result.push_str(&else_kw);
@@ -148,6 +152,47 @@ impl Rewrite for ast::Local {
148152
}
149153
}
150154

155+
/// When the initializer expression is multi-lined, then the else keyword and opening brace of the
156+
/// block ( i.e. "else {") should be put on the same line as the end of the initializer expression
157+
/// if all the following are true:
158+
///
159+
/// 1. The initializer expression ends with one or more closing parentheses, square brackets,
160+
/// or braces
161+
/// 2. There is nothing else on that line
162+
/// 3. That line is not indented beyond the indent on the first line of the let keyword
163+
fn same_line_else_kw_and_brace(
164+
init_str: &str,
165+
context: &RewriteContext<'_>,
166+
else_kw_span: Span,
167+
init_shape: Shape,
168+
) -> bool {
169+
if !init_str.contains('\n') {
170+
// initializer expression is single lined so the "else {" should be placed on the same line
171+
return true;
172+
}
173+
174+
// 1. The initializer expression ends with one or more `)`, `]`, `}`.
175+
if !init_str.ends_with([')', ']', '}']) {
176+
return false;
177+
}
178+
179+
// 2. There is nothing else on that line
180+
// For example, there are no comments
181+
let else_kw_snippet = context.snippet(else_kw_span).trim();
182+
if else_kw_snippet != "else" {
183+
return false;
184+
}
185+
186+
// 3. The last line of the initializer expression is not indented beyond the `let` keyword
187+
let indent = init_shape.indent.to_string(context.config);
188+
init_str
189+
.lines()
190+
.last()
191+
.expect("initializer expression is multi-lined")
192+
.strip_prefix(indent.as_ref())
193+
.map_or(false, |l| !l.starts_with(char::is_whitespace))
194+
}
195+
151196
// FIXME convert to using rewrite style rather than visitor
152197
// FIXME format modules in this style
153198
#[allow(dead_code)]

Diff for: tests/source/let_else.rs

+4
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ fn main() {
77
// nope
88
return;
99
};
10+
11+
let Some(x) = y.foo("abc", fairly_long_identifier, "def", "123456", "string", "cheese") else { bar() };
12+
13+
let Some(x) = abcdef().foo("abc", some_really_really_really_long_ident, "ident", "123456").bar().baz().qux("fffffffffffffffff") else { foo_bar() };
1014
}

Diff for: tests/target/let_else.rs

+25
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,29 @@ fn main() {
99
// nope
1010
return;
1111
};
12+
13+
let Some(x) = y.foo(
14+
"abc",
15+
fairly_long_identifier,
16+
"def",
17+
"123456",
18+
"string",
19+
"cheese",
20+
) else {
21+
bar()
22+
};
23+
24+
let Some(x) = abcdef()
25+
.foo(
26+
"abc",
27+
some_really_really_really_long_ident,
28+
"ident",
29+
"123456",
30+
)
31+
.bar()
32+
.baz()
33+
.qux("fffffffffffffffff")
34+
else {
35+
foo_bar()
36+
};
1237
}

0 commit comments

Comments
 (0)