Skip to content

Commit 547577f

Browse files
ytmimicalebcartwright
authored andcommitted
implement sinlge line let-chain rules
for now, let-chains can only be formatted on a single line if the chain consits of 2 expressions where the first is an identifier proceeded by any number of unary operators and the second is a let-expr.
1 parent 457dc79 commit 547577f

File tree

4 files changed

+236
-6
lines changed

4 files changed

+236
-6
lines changed

src/expr.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1842,11 +1842,14 @@ fn rewrite_let(
18421842
) -> Option<String> {
18431843
let mut result = "let ".to_owned();
18441844

1845+
// TODO(ytmimi) comments could appear between `let` and the `pat`
1846+
18451847
// 4 = "let ".len()
18461848
let pat_shape = shape.offset_left(4)?;
18471849
let pat_str = pat.rewrite(context, pat_shape)?;
18481850
result.push_str(&pat_str);
18491851

1852+
// TODO(ytmimi) comments could appear between `pat` and `=`
18501853
result.push_str(" =");
18511854

18521855
let comments_lo = context

src/pairs.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,13 @@ pub(crate) fn rewrite_all_pairs(
4242
context: &RewriteContext<'_>,
4343
) -> Option<String> {
4444
expr.flatten(context, shape).and_then(|list| {
45-
// First we try formatting on one line.
46-
rewrite_pairs_one_line(&list, shape, context)
47-
.or_else(|| rewrite_pairs_multiline(&list, shape, context))
45+
if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
46+
rewrite_pairs_multiline(&list, shape, context)
47+
} else {
48+
// First we try formatting on one line.
49+
rewrite_pairs_one_line(&list, shape, context)
50+
.or_else(|| rewrite_pairs_multiline(&list, shape, context))
51+
}
4852
})
4953
}
5054

@@ -255,6 +259,37 @@ struct PairList<'a, 'b, T: Rewrite> {
255259
separators: Vec<&'a str>,
256260
}
257261

262+
fn is_ident(expr: &ast::Expr) -> bool {
263+
match &expr.kind {
264+
ast::ExprKind::Path(None, path) if path.segments.len() == 1 => true,
265+
ast::ExprKind::Unary(_, expr)
266+
| ast::ExprKind::AddrOf(_, _, expr)
267+
| ast::ExprKind::Paren(expr)
268+
| ast::ExprKind::Try(expr) => is_ident(expr),
269+
_ => false,
270+
}
271+
}
272+
273+
impl<'a, 'b> PairList<'a, 'b, ast::Expr> {
274+
fn let_chain_count(&self) -> usize {
275+
self.list
276+
.iter()
277+
.filter(|(expr, _)| matches!(expr.kind, ast::ExprKind::Let(_, _, _)))
278+
.count()
279+
}
280+
281+
fn can_rewrite_let_chain_single_line(&self) -> bool {
282+
if self.list.len() != 2 {
283+
return false;
284+
}
285+
286+
let fist_item_is_ident = is_ident(self.list[0].0);
287+
let second_item_is_let_chain = matches!(self.list[1].0.kind, ast::ExprKind::Let(_, _, _));
288+
289+
fist_item_is_ident && second_item_is_let_chain
290+
}
291+
}
292+
258293
impl FlattenPair for ast::Expr {
259294
fn flatten(
260295
&self,

tests/source/let_chains.rs

+106-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,109 @@ fn main() {
1313

1414
if let XXXXXXXXX { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, yyyyyyyyyyyyy, zzzzzzzzzzzzz} = xxxxxxx()
1515
&& let Foo = bar() { todo!() }
16-
}
16+
}
17+
18+
fn test_single_line_let_chain() {
19+
// first item in let-chain is an ident
20+
if a && let Some(b) = foo() {
21+
}
22+
23+
// first item in let-chain is a unary ! with an ident
24+
let unary_not = if !from_hir_call
25+
&& let Some(p) = parent
26+
{
27+
};
28+
29+
// first item in let-chain is a unary * with an ident
30+
let unary_deref = if *some_deref
31+
&& let Some(p) = parent
32+
{
33+
};
34+
35+
// first item in let-chain is a unary - (neg) with an ident
36+
let unary_neg = if -some_ident
37+
&& let Some(p) = parent
38+
{
39+
};
40+
41+
// first item in let-chain is a try (?) with an ident
42+
let try_ = if some_try?
43+
&& let Some(p) = parent
44+
{
45+
};
46+
47+
// first item in let-chain is an ident wrapped in parens
48+
let in_parens = if (some_ident)
49+
&& let Some(p) = parent
50+
{
51+
};
52+
53+
// first item in let-chain is a ref & with an ident
54+
let _ref = if &some_ref
55+
&& let Some(p) = parent
56+
{
57+
};
58+
59+
// first item in let-chain is a ref &mut with an ident
60+
let mut_ref = if &mut some_ref
61+
&& let Some(p) = parent
62+
{
63+
};
64+
65+
// chain unary ref and try
66+
let chain_of_unary_ref_and_try = if !&*some_ref?
67+
&& let Some(p) = parent {
68+
};
69+
}
70+
71+
fn test_multi_line_let_chain() {
72+
// Can only single line the let-chain if the first item is an ident
73+
if let Some(x) = y && a {
74+
75+
}
76+
77+
// More than one let-chain must be formatted on multiple lines
78+
if let Some(x) = y && let Some(a) = b {
79+
80+
}
81+
82+
// The ident isn't long enough so we don't wrap the first let-chain
83+
if a && let Some(x) = y && let Some(a) = b {
84+
85+
}
86+
87+
// The ident is long enough so both let-chains are wrapped
88+
if aaa && let Some(x) = y && let Some(a) = b {
89+
90+
}
91+
92+
// function call
93+
if a() && let Some(x) = y {
94+
95+
}
96+
97+
// bool literal
98+
if true && let Some(x) = y {
99+
100+
}
101+
102+
// cast to a bool
103+
if 1 as bool && let Some(x) = y {
104+
105+
}
106+
107+
// matches! macro call
108+
if matches!(a, some_type) && let Some(x) = y {
109+
110+
}
111+
112+
// block expression returning bool
113+
if { true } && let Some(x) = y {
114+
115+
}
116+
117+
// field access
118+
if a.x && let Some(x) = y {
119+
120+
}
121+
}

tests/target/let_chains.rs

+89-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
fn main() {
2-
if let x = x && x {}
2+
if let x = x
3+
&& x
4+
{}
35

46
if xxx && let x = x {}
57

@@ -12,7 +14,10 @@ fn main() {
1214
{}
1315

1416
if aaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaa
15-
|| aaaaaaaaa && let Some(x) = xxxxxxxxxxxx && aaaaaaa && let None = aaaaaaaaaa
17+
|| aaaaaaaaa
18+
&& let Some(x) = xxxxxxxxxxxx
19+
&& aaaaaaa
20+
&& let None = aaaaaaaaaa
1621
{}
1722

1823
if let Some(Struct { x: TS(1, 2) }) = path::to::<_>(hehe)
@@ -39,3 +44,85 @@ fn main() {
3944
todo!()
4045
}
4146
}
47+
48+
fn test_single_line_let_chain() {
49+
// first item in let-chain is an ident
50+
if a && let Some(b) = foo() {}
51+
52+
// first item in let-chain is a unary ! with an ident
53+
let unary_not = if !from_hir_call && let Some(p) = parent {};
54+
55+
// first item in let-chain is a unary * with an ident
56+
let unary_deref = if *some_deref && let Some(p) = parent {};
57+
58+
// first item in let-chain is a unary - (neg) with an ident
59+
let unary_neg = if -some_ident && let Some(p) = parent {};
60+
61+
// first item in let-chain is a try (?) with an ident
62+
let try_ = if some_try? && let Some(p) = parent {};
63+
64+
// first item in let-chain is an ident wrapped in parens
65+
let in_parens = if (some_ident) && let Some(p) = parent {};
66+
67+
// first item in let-chain is a ref & with an ident
68+
let _ref = if &some_ref && let Some(p) = parent {};
69+
70+
// first item in let-chain is a ref &mut with an ident
71+
let mut_ref = if &mut some_ref && let Some(p) = parent {};
72+
73+
// chain unary ref and try
74+
let chain_of_unary_ref_and_try = if !&*some_ref? && let Some(p) = parent {};
75+
}
76+
77+
fn test_multi_line_let_chain() {
78+
// Can only single line the let-chain if the first item is an ident
79+
if let Some(x) = y
80+
&& a
81+
{}
82+
83+
// More than one let-chain must be formatted on multiple lines
84+
if let Some(x) = y
85+
&& let Some(a) = b
86+
{}
87+
88+
// The ident isn't long enough so we don't wrap the first let-chain
89+
if a && let Some(x) = y
90+
&& let Some(a) = b
91+
{}
92+
93+
// The ident is long enough so both let-chains are wrapped
94+
if aaa
95+
&& let Some(x) = y
96+
&& let Some(a) = b
97+
{}
98+
99+
// function call
100+
if a()
101+
&& let Some(x) = y
102+
{}
103+
104+
// bool literal
105+
if true
106+
&& let Some(x) = y
107+
{}
108+
109+
// cast to a bool
110+
if 1 as bool
111+
&& let Some(x) = y
112+
{}
113+
114+
// matches! macro call
115+
if matches!(a, some_type)
116+
&& let Some(x) = y
117+
{}
118+
119+
// block expression returning bool
120+
if { true }
121+
&& let Some(x) = y
122+
{}
123+
124+
// field access
125+
if a.x
126+
&& let Some(x) = y
127+
{}
128+
}

0 commit comments

Comments
 (0)