Skip to content

Commit 116a9ec

Browse files
authored
fix(fmt): preserve indexed callee when it fits (#12270)
* fix(fmt): preserve indexed callee when it fits * style: typos
1 parent 8dadf61 commit 116a9ec

File tree

5 files changed

+76
-11
lines changed

5 files changed

+76
-11
lines changed

crates/fmt/src/state/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ pub(super) struct State<'sess, 'ast> {
126126
return_bin_expr: bool,
127127
// Whether inside a call with call options and at least one argument.
128128
call_with_opts_and_args: bool,
129+
// Whether to skip the index soft breaks because the callee fits inline.
130+
skip_index_break: bool,
129131
// Whether inside an `emit` or `revert` call with a qualified path, or not.
130132
emit_or_revert: bool,
131133
// Whether inside a variable initialization expression, or not.
@@ -219,6 +221,7 @@ impl<'sess> State<'sess, '_> {
219221
contract: None,
220222
single_line_stmt: None,
221223
call_with_opts_and_args: false,
224+
skip_index_break: false,
222225
binary_expr: None,
223226
return_bin_expr: false,
224227
emit_or_revert: false,

crates/fmt/src/state/sol.rs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,11 +1274,10 @@ impl<'ast> State<'_, 'ast> {
12741274
MemberOrCallArgs::Member(self.estimate_size(ident.span)),
12751275
|s| {
12761276
s.print_trailing_comment(member_expr.span.hi(), Some(ident.span.lo()));
1277-
if !matches!(
1278-
member_expr.kind,
1279-
ast::ExprKind::Ident(_) | ast::ExprKind::Type(_)
1280-
) {
1281-
s.zerobreak();
1277+
match member_expr.kind {
1278+
ast::ExprKind::Ident(_) | ast::ExprKind::Type(_) => (),
1279+
ast::ExprKind::Index(..) if s.skip_index_break => (),
1280+
_ => s.zerobreak(),
12821281
}
12831282
s.word(".");
12841283
s.print_ident(ident);
@@ -1442,10 +1441,16 @@ impl<'ast> State<'_, 'ast> {
14421441
self.s.cbox(self.ind);
14431442

14441443
let mut skip_break = false;
1445-
1444+
let mut zerobreak = |this: &mut Self| {
1445+
if this.skip_index_break {
1446+
skip_break = true;
1447+
} else {
1448+
this.zerobreak();
1449+
}
1450+
};
14461451
match kind {
14471452
ast::IndexKind::Index(Some(inner_expr)) => {
1448-
self.zerobreak();
1453+
zerobreak(self);
14491454
self.print_expr(inner_expr);
14501455
}
14511456
ast::IndexKind::Index(None) => {}
@@ -1455,19 +1460,19 @@ impl<'ast> State<'_, 'ast> {
14551460
.print_comments(start_expr.span.lo(), CommentConfig::skip_ws())
14561461
.is_none_or(|s| s.is_mixed())
14571462
{
1458-
self.zerobreak();
1463+
zerobreak(self);
14591464
}
14601465
self.print_expr(start_expr);
14611466
} else {
1462-
self.zerobreak();
1467+
zerobreak(self);
14631468
}
14641469

14651470
self.word(":");
14661471

14671472
if let Some(end_expr) = end {
14681473
self.s.ibox(self.ind);
14691474
if start.is_some() {
1470-
self.zerobreak();
1475+
zerobreak(self);
14711476
}
14721477
self.print_comments(
14731478
end_expr.span.lo(),
@@ -1596,7 +1601,7 @@ impl<'ast> State<'_, 'ast> {
15961601
}
15971602
}
15981603

1599-
let mut extra_box = false;
1604+
let (mut extra_box, skip_cache) = (false, self.skip_index_break);
16001605
let parent_is_chain = self.call_stack.last().copied().is_some_and(|call| call.is_chained());
16011606
if !parent_is_chain {
16021607
// Estimate sizes of callee and optional member
@@ -1626,6 +1631,7 @@ impl<'ast> State<'_, 'ast> {
16261631
// calls with cmnts between the args always break
16271632
|| (total_fits_line && !member_or_args.has_comments()))
16281633
{
1634+
self.skip_index_break = true;
16291635
self.cbox(0);
16301636
} else {
16311637
self.s.ibox(self.ind);
@@ -1650,6 +1656,11 @@ impl<'ast> State<'_, 'ast> {
16501656
}
16511657
self.end();
16521658
}
1659+
1660+
// Restore cache
1661+
if self.skip_index_break {
1662+
self.skip_index_break = skip_cache;
1663+
}
16531664
}
16541665

16551666
fn print_call_args(
@@ -2859,6 +2870,16 @@ pub(super) fn get_callee_head_size(callee: &ast::Expr<'_>) -> usize {
28592870
ast::ExprKind::Type(ast::Type { kind: ast::TypeKind::Elementary(ty), .. }) => {
28602871
ty.to_abi_str().len()
28612872
}
2873+
ast::ExprKind::Index(base, idx) => {
2874+
let idx_len = match idx {
2875+
ast::IndexKind::Index(expr) => expr.as_ref().map_or(0, |e| get_callee_head_size(e)),
2876+
ast::IndexKind::Range(e1, e2) => {
2877+
1 + e1.as_ref().map_or(0, |e| get_callee_head_size(e))
2878+
+ e2.as_ref().map_or(0, |e| get_callee_head_size(e))
2879+
}
2880+
};
2881+
get_callee_head_size(base) + 2 + idx_len
2882+
}
28622883
ast::ExprKind::Member(base, member_ident) => {
28632884
match &base.kind {
28642885
ast::ExprKind::Ident(..) | ast::ExprKind::Type(..) => {

crates/fmt/testdata/VariableAssignment/bracket-spacing.fmt.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,21 @@ contract TestContract {
3838
"0x0000000000000000000000000000000000000000000000000000000000000000,"
3939
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
4040
}
41+
42+
function test_longIndexedCall() {
43+
// https://github.com/foundry-rs/foundry/issues/12254
44+
bytes memory message = mailboxes[destinationDomain].buildMessage(
45+
originDomain,
46+
bytes32(0),
47+
address(inbox).toBytes32(),
48+
abi.encode(orderId, bytes32(0), address(0))
49+
);
50+
// should have identicall behavior when call of the same size without indexing
51+
bytes memory message = mailboxes_destinationDomains.buildMessage(
52+
originDomain,
53+
bytes32(0),
54+
address(inbox).toBytes32(),
55+
abi.encode(orderId, bytes32(0), address(0))
56+
);
57+
}
4158
}

crates/fmt/testdata/VariableAssignment/fmt.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,21 @@ contract TestContract {
3737
"0x0000000000000000000000000000000000000000000000000000000000000000,"
3838
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
3939
}
40+
41+
function test_longIndexedCall() {
42+
// https://github.com/foundry-rs/foundry/issues/12254
43+
bytes memory message = mailboxes[destinationDomain].buildMessage(
44+
originDomain,
45+
bytes32(0),
46+
address(inbox).toBytes32(),
47+
abi.encode(orderId, bytes32(0), address(0))
48+
);
49+
// should have identicall behavior when call of the same size without indexing
50+
bytes memory message = mailboxes_destinationDomains.buildMessage(
51+
originDomain,
52+
bytes32(0),
53+
address(inbox).toBytes32(),
54+
abi.encode(orderId, bytes32(0), address(0))
55+
);
56+
}
4057
}

crates/fmt/testdata/VariableAssignment/original.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,11 @@ contract TestContract {
3636
"0x0000000000000000000000000000000000000000000000000000000000000000,"
3737
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
3838
}
39+
40+
function test_longIndexedCall() {
41+
// https://github.com/foundry-rs/foundry/issues/12254
42+
bytes memory message = mailboxes[destinationDomain].buildMessage(originDomain, bytes32(0), address(inbox).toBytes32(), abi.encode(orderId, bytes32(0), address(0)));
43+
// should have identicall behavior when call of the same size without indexing
44+
bytes memory message = mailboxes_destinationDomains.buildMessage(originDomain, bytes32(0), address(inbox).toBytes32(), abi.encode(orderId, bytes32(0), address(0)));
45+
}
3946
}

0 commit comments

Comments
 (0)