diff --git a/crates/config/src/fmt.rs b/crates/config/src/fmt.rs index 65235d4025804..15d4209da2bc2 100644 --- a/crates/config/src/fmt.rs +++ b/crates/config/src/fmt.rs @@ -39,8 +39,9 @@ pub struct FormatterConfig { pub sort_imports: bool, /// Whether to suppress spaces around the power operator (`**`). pub pow_no_space: bool, - /// Whether to compact call args in a single line when possible - pub call_compact_args: bool, + /// Style that determines if a broken list, should keep its elements together on their own + /// line, before breaking individually. + pub prefer_compact: PreferCompact, } /// Style of integer types. @@ -186,6 +187,40 @@ impl MultilineFuncHeaderStyle { } } +/// Style that determines if a broken list, should keep its elements together on their own line, +/// before breaking individually. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PreferCompact { + /// All elements are preferred consistent. + None, + /// Calls are preferred compact. Events and errors break consistently. + Calls, + /// Events are preferred compact. Calls and errors break consistently. + Events, + /// Errors are preferred compact. Calls and events break consistently. + Errors, + /// Events and errors are preferred compact. Calls break consistently. + EventsErrors, + /// All elements are preferred compact. + #[default] + All, +} + +impl PreferCompact { + pub fn calls(&self) -> bool { + matches!(self, Self::All | Self::Calls) + } + + pub fn events(&self) -> bool { + matches!(self, Self::All | Self::Events | Self::EventsErrors) + } + + pub fn errors(&self) -> bool { + matches!(self, Self::All | Self::Errors | Self::EventsErrors) + } +} + /// Style of indent #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] @@ -214,7 +249,7 @@ impl Default for FormatterConfig { contract_new_lines: false, sort_imports: false, pow_no_space: false, - call_compact_args: true, + prefer_compact: PreferCompact::default(), docs_style: DocCommentStyle::default(), } } diff --git a/crates/fmt/README.md b/crates/fmt/README.md index 1066c0253cefb..53d7e6d721472 100644 --- a/crates/fmt/README.md +++ b/crates/fmt/README.md @@ -116,6 +116,7 @@ The formatter supports multiple configuration options defined in `foundry.toml`. | `bracket_spacing` | `false` | Print spaces between brackets. | | `int_types` | `long` | Style for `uint256`/`int256` types. Options: `long`, `short`, `preserve`. | | `multiline_func_header` | `attributes_first` | The style of multiline function headers. Options: `attributes_first`, `params_always`, `params_first_multi`, `all`, `all_params`. | +| `prefer_compact` | `calls` | Style that determines if a broken list, should keep its elements together on their own line, before breaking individually. Options: `calls`, `events`, `errors`, `events_errors`, `all`. | | `quote_style` | `double` | The style of quotation marks. Options: `double`, `single`, `preserve`. | | `number_underscore` | `preserve` | The style of underscores in number literals. Options: `preserve`, `remove`, `thousands`. | | `hex_underscore` | `remove` | The style of underscores in hex literals. Options: `preserve`, `remove`, `bytes`. | diff --git a/crates/fmt/src/state/common.rs b/crates/fmt/src/state/common.rs index 9bab5e0bf3a08..085916e60a98e 100644 --- a/crates/fmt/src/state/common.rs +++ b/crates/fmt/src/state/common.rs @@ -524,7 +524,9 @@ impl<'ast> State<'_, 'ast> { for (pos, ident) in path.segments().iter().delimited() { self.print_ident(ident); if !pos.is_last { - self.zerobreak(); + if !self.emit_or_revert { + self.zerobreak(); + } self.word("."); } } diff --git a/crates/fmt/src/state/sol.rs b/crates/fmt/src/state/sol.rs index 54209e720e383..e24471d441a90 100644 --- a/crates/fmt/src/state/sol.rs +++ b/crates/fmt/src/state/sol.rs @@ -725,7 +725,15 @@ impl<'ast> State<'_, 'ast> { let ast::ItemError { name, parameters } = err; self.word("error "); self.print_ident(name); - self.print_parameter_list(parameters, parameters.span, ListFormat::compact()); + self.print_parameter_list( + parameters, + parameters.span, + if self.config.prefer_compact.errors() { + ListFormat::compact() + } else { + ListFormat::consistent() + }, + ); self.word(";"); } @@ -733,7 +741,15 @@ impl<'ast> State<'_, 'ast> { let ast::ItemEvent { name, parameters, anonymous } = event; self.word("event "); self.print_ident(name); - self.print_parameter_list(parameters, parameters.span, ListFormat::compact().break_cmnts()); + self.print_parameter_list( + parameters, + parameters.span, + if self.config.prefer_compact.events() { + ListFormat::compact().break_cmnts() + } else { + ListFormat::consistent().break_cmnts() + }, + ); if *anonymous { self.word(" anonymous"); } @@ -1701,7 +1717,7 @@ impl<'ast> State<'_, 'ast> { } fn print_named_args(&mut self, args: &'ast [ast::NamedArg<'ast>], pos_hi: BytePos) { - let list_format = match (self.config.bracket_spacing, self.config.call_compact_args) { + let list_format = match (self.config.bracket_spacing, self.config.prefer_compact.calls()) { (false, true) => ListFormat::compact(), (false, false) => ListFormat::consistent(), (true, true) => ListFormat::compact().with_space(), @@ -1738,7 +1754,7 @@ impl<'ast> State<'_, 'ast> { .break_cmnts() .break_single(true) .without_ind(self.call_stack.is_chain()) - .with_delimiters(!(self.emit_or_revert || self.call_with_opts_and_args)), + .with_delimiters(!self.call_with_opts_and_args), ); } else if self.config.bracket_spacing { self.nbsp(); @@ -2264,17 +2280,14 @@ impl<'ast> State<'_, 'ast> { self.nbsp(); }; self.s.cbox(0); - self.print_path(path, false); self.emit_or_revert = path.segments().len() > 1; - self.print_call_args( - args, - if self.config.call_compact_args { - ListFormat::compact().break_cmnts().with_delimiters(args.len() == 1) - } else { - ListFormat::consistent().break_cmnts().with_delimiters(args.len() == 1) - }, - path.to_string().len(), - ); + self.print_path(path, false); + let format = if self.config.prefer_compact.calls() { + ListFormat::compact() + } else { + ListFormat::consistent() + }; + self.print_call_args(args, format.break_cmnts(), path.to_string().len()); self.emit_or_revert = false; self.end(); } @@ -2797,6 +2810,7 @@ fn has_complex_successor(expr_kind: &ast::ExprKind<'_>, left: bool) -> bool { } ast::ExprKind::Unary(_, expr) => has_complex_successor(&expr.kind, left), ast::ExprKind::Lit(..) | ast::ExprKind::Ident(_) => false, + ast::ExprKind::Tuple(..) => false, _ => true, } } diff --git a/crates/fmt/testdata/DocComments/wrap-comments.fmt.sol b/crates/fmt/testdata/DocComments/wrap-comments.fmt.sol index f8a45cbbe4115..d1ff4e9b1410c 100644 --- a/crates/fmt/testdata/DocComments/wrap-comments.fmt.sol +++ b/crates/fmt/testdata/DocComments/wrap-comments.fmt.sol @@ -1,6 +1,5 @@ // config: line_length = 40 // config: wrap_comments = true -// config: call_compact_args = false pragma solidity ^0.8.13; /// @title A Hello world example @@ -24,8 +23,7 @@ contract HelloWorld { /// @param age The dude's age constructor(uint256 age) { theDude = Person({ - age: age, - wallet: msg.sender + age: age, wallet: msg.sender }); } diff --git a/crates/fmt/testdata/EmitStatement/120.compact.fmt.sol b/crates/fmt/testdata/EmitStatement/120.compact.fmt.sol new file mode 100644 index 0000000000000..99b1446b894d8 --- /dev/null +++ b/crates/fmt/testdata/EmitStatement/120.compact.fmt.sol @@ -0,0 +1,48 @@ +// config: line_length = 120 +event NewEvent(address beneficiary, uint256 index, uint64 timestamp, uint64 endTimestamp); + +function emitEvent() { + emit NewEvent(beneficiary, _vestingBeneficiaries.length - 1, uint64(block.timestamp), endTimestamp); + + emit NewEvent( /* beneficiary */ + beneficiary, + /* index */ + _vestingBeneficiaries.length - 1, + /* timestamp */ + uint64(block.timestamp), + /* end timestamp */ + endTimestamp + ); + + emit NewEvent( + beneficiary, // beneficiary + _vestingBeneficiaries.length - 1, // index + uint64(block.timestamp), // timestamp + endTimestamp // end timestamp + ); + + // https://github.com/foundry-rs/foundry/issues/12029 + emit OperatorSharesDecreased( + defaultOperator, + address(0), + strategyMock, + depositAmount / 6 // 1 withdrawal not queued so decreased + ); + + // https://github.com/foundry-rs/foundry/issues/12146 + emit ISablierComptroller.DisableCustomFeeUSD( + protocol_protocol, caller_caller, user_users.sender, previousMinFeeUSD_0, newMinFeeUSD_feeUSD + ); + emit ISablierComptroller.DisableCustomFeeUSD({ + protocol: protocol, caller: caller, user: users.sender, previousMinFeeUSD: 0, newMinFeeUSD: feeUSD + }); + + emit ISablierLockupLinear.CreateLockupLinearStream({ + streamId: streamId, + commonParams: Lockup.CreateEventCommon({ + funder: msg.sender, sender: sender, recipient: recipient, depositAmount: depositAmount + }), + cliffTime: cliffTime, + unlockAmounts: unlockAmounts + }); +} diff --git a/crates/fmt/testdata/EmitStatement/120.fmt.sol b/crates/fmt/testdata/EmitStatement/120.fmt.sol index 927184651b0ba..8e12d544bacf9 100644 --- a/crates/fmt/testdata/EmitStatement/120.fmt.sol +++ b/crates/fmt/testdata/EmitStatement/120.fmt.sol @@ -1,4 +1,5 @@ // config: line_length = 120 +// config: prefer_compact = "none" event NewEvent(address beneficiary, uint256 index, uint64 timestamp, uint64 endTimestamp); function emitEvent() { @@ -30,9 +31,19 @@ function emitEvent() { ); // https://github.com/foundry-rs/foundry/issues/12146 - emit ISablierComptroller.DisableCustomFeeUSD(protocol, caller, users.sender, 0, feeUSD); + emit ISablierComptroller.DisableCustomFeeUSD( + protocol_protocol, + caller_caller, + user_users.sender, + previousMinFeeUSD_0, + newMinFeeUSD_feeUSD + ); emit ISablierComptroller.DisableCustomFeeUSD({ - protocol: protocol, caller: caller, user: users.sender, previousMinFeeUSD: 0, newMinFeeUSD: feeUSD + protocol: protocol, + caller: caller, + user: users.sender, + previousMinFeeUSD: 0, + newMinFeeUSD: feeUSD }); emit ISablierLockupLinear.CreateLockupLinearStream({ @@ -41,12 +52,7 @@ function emitEvent() { funder: msg.sender, sender: sender, recipient: recipient, - depositAmount: depositAmount, - token: token, - cancelable: cancelable, - transferable: transferable, - timestamps: timestamps, - shape: shape + depositAmount: depositAmount }), cliffTime: cliffTime, unlockAmounts: unlockAmounts diff --git a/crates/fmt/testdata/EmitStatement/fmt.sol b/crates/fmt/testdata/EmitStatement/fmt.sol index af351d6c06a3e..b31df04a7818e 100644 --- a/crates/fmt/testdata/EmitStatement/fmt.sol +++ b/crates/fmt/testdata/EmitStatement/fmt.sol @@ -38,7 +38,11 @@ function emitEvent() { // https://github.com/foundry-rs/foundry/issues/12146 emit ISablierComptroller.DisableCustomFeeUSD( - protocol, caller, users.sender, 0, feeUSD + protocol_protocol, + caller_caller, + user_users.sender, + previousMinFeeUSD_0, + newMinFeeUSD_feeUSD ); emit ISablierComptroller.DisableCustomFeeUSD({ protocol: protocol, @@ -54,12 +58,7 @@ function emitEvent() { funder: msg.sender, sender: sender, recipient: recipient, - depositAmount: depositAmount, - token: token, - cancelable: cancelable, - transferable: transferable, - timestamps: timestamps, - shape: shape + depositAmount: depositAmount }), cliffTime: cliffTime, unlockAmounts: unlockAmounts diff --git a/crates/fmt/testdata/EmitStatement/original.sol b/crates/fmt/testdata/EmitStatement/original.sol index 657126374d718..bb554fa974ebd 100644 --- a/crates/fmt/testdata/EmitStatement/original.sol +++ b/crates/fmt/testdata/EmitStatement/original.sol @@ -31,7 +31,7 @@ function emitEvent() { ); // https://github.com/foundry-rs/foundry/issues/12146 - emit ISablierComptroller.DisableCustomFeeUSD(protocol, caller, users.sender, 0, feeUSD); + emit ISablierComptroller.DisableCustomFeeUSD(protocol_protocol, caller_caller, user_users.sender, previousMinFeeUSD_0, newMinFeeUSD_feeUSD); emit ISablierComptroller.DisableCustomFeeUSD({ protocol: protocol, caller: caller, user: users.sender, previousMinFeeUSD: 0, newMinFeeUSD: feeUSD }); emit ISablierLockupLinear.CreateLockupLinearStream({ @@ -40,12 +40,7 @@ function emitEvent() { funder: msg.sender, sender: sender, recipient: recipient, - depositAmount: depositAmount, - token: token, - cancelable: cancelable, - transferable: transferable, - timestamps: timestamps, - shape: shape + depositAmount: depositAmount }), cliffTime: cliffTime, unlockAmounts: unlockAmounts diff --git a/crates/fmt/testdata/NamedFunctionCallExpression/fmt.sol b/crates/fmt/testdata/NamedFunctionCallExpression/fmt.sol index 8b0690544c8b0..e8f832e3e299f 100644 --- a/crates/fmt/testdata/NamedFunctionCallExpression/fmt.sol +++ b/crates/fmt/testdata/NamedFunctionCallExpression/fmt.sol @@ -1,4 +1,4 @@ -// config: call_compact_args = false +// config: prefer_compact = "events_errors" contract NamedFunctionCallExpression { struct SimpleStruct { uint256 val; diff --git a/crates/fmt/testdata/OperatorExpressions/120.fmt.sol b/crates/fmt/testdata/OperatorExpressions/120.fmt.sol index d50d5f2ba9021..0a6d58cef8698 100644 --- a/crates/fmt/testdata/OperatorExpressions/120.fmt.sol +++ b/crates/fmt/testdata/OperatorExpressions/120.fmt.sol @@ -82,5 +82,8 @@ contract Repro { || chainId == LINEA || chainId == MODE || chainId == MORPH || chainId == OPTIMISM || chainId == POLYGON || chainId == SCROLL || chainId == SEI || chainId == SOPHON || chainId == SUPERSEED || chainId == SONIC || chainId == UNICHAIN || chainId == XDC || chainId == ZKSYNC; + + callsGas += (3 * FixedPointMathLib.divUp(paramsLength, 32)) + + FixedPointMathLib.mulDivUp(paramsLength, paramsLength, 524_288); } } diff --git a/crates/fmt/testdata/OperatorExpressions/fmt.sol b/crates/fmt/testdata/OperatorExpressions/fmt.sol index e7ebdf77d1747..c913b9859f066 100644 --- a/crates/fmt/testdata/OperatorExpressions/fmt.sol +++ b/crates/fmt/testdata/OperatorExpressions/fmt.sol @@ -98,5 +98,8 @@ contract Repro { || chainId == POLYGON || chainId == SCROLL || chainId == SEI || chainId == SOPHON || chainId == SUPERSEED || chainId == SONIC || chainId == UNICHAIN || chainId == XDC || chainId == ZKSYNC; + + callsGas += (3 * FixedPointMathLib.divUp(paramsLength, 32)) + + FixedPointMathLib.mulDivUp(paramsLength, paramsLength, 524_288); } } diff --git a/crates/fmt/testdata/OperatorExpressions/original.sol b/crates/fmt/testdata/OperatorExpressions/original.sol index 0b897c881c7ca..0de13f2149dd0 100644 --- a/crates/fmt/testdata/OperatorExpressions/original.sol +++ b/crates/fmt/testdata/OperatorExpressions/original.sol @@ -71,5 +71,7 @@ contract Repro { || chainId == MODE || chainId == MORPH || chainId == OPTIMISM || chainId == POLYGON || chainId == SCROLL || chainId == SEI || chainId == SOPHON || chainId == SUPERSEED || chainId == SONIC || chainId == UNICHAIN || chainId == XDC || chainId == ZKSYNC; + + callsGas += (3 * FixedPointMathLib.divUp(paramsLength, 32)) + FixedPointMathLib.mulDivUp(paramsLength, paramsLength, 524_288); } } diff --git a/crates/fmt/testdata/OperatorExpressions/pow-no-space.fmt.sol b/crates/fmt/testdata/OperatorExpressions/pow-no-space.fmt.sol index 81655935a1271..f03f3a382f3de 100644 --- a/crates/fmt/testdata/OperatorExpressions/pow-no-space.fmt.sol +++ b/crates/fmt/testdata/OperatorExpressions/pow-no-space.fmt.sol @@ -99,5 +99,8 @@ contract Repro { || chainId == POLYGON || chainId == SCROLL || chainId == SEI || chainId == SOPHON || chainId == SUPERSEED || chainId == SONIC || chainId == UNICHAIN || chainId == XDC || chainId == ZKSYNC; + + callsGas += (3 * FixedPointMathLib.divUp(paramsLength, 32)) + + FixedPointMathLib.mulDivUp(paramsLength, paramsLength, 524_288); } } diff --git a/crates/fmt/testdata/RevertNamedArgsStatement/bracket-spacing.fmt.sol b/crates/fmt/testdata/RevertNamedArgsStatement/bracket-spacing.fmt.sol index e5161a1ee7d08..0ba90b1060973 100644 --- a/crates/fmt/testdata/RevertNamedArgsStatement/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/RevertNamedArgsStatement/bracket-spacing.fmt.sol @@ -1,4 +1,4 @@ -// config: call_compact_args = false +// config: prefer_compact = "events_errors" // config: bracket_spacing = true contract RevertNamedArgsStatement { error EmptyError(); diff --git a/crates/fmt/testdata/RevertNamedArgsStatement/fmt.sol b/crates/fmt/testdata/RevertNamedArgsStatement/fmt.sol index ddb9228dfdc30..11c8069fe521c 100644 --- a/crates/fmt/testdata/RevertNamedArgsStatement/fmt.sol +++ b/crates/fmt/testdata/RevertNamedArgsStatement/fmt.sol @@ -1,4 +1,4 @@ -// config: call_compact_args = false +// config: prefer_compact = "events_errors" contract RevertNamedArgsStatement { error EmptyError(); error SimpleError(uint256 val); diff --git a/crates/fmt/testdata/RevertStatement/fmt.sol b/crates/fmt/testdata/RevertStatement/fmt.sol index 9f52fbceba927..4ed9eb348d39d 100644 --- a/crates/fmt/testdata/RevertStatement/fmt.sol +++ b/crates/fmt/testdata/RevertStatement/fmt.sol @@ -1,3 +1,4 @@ +// config: prefer_compact = "none" contract RevertStatement { error TestError(uint256, bool, string); @@ -42,11 +43,15 @@ contract RevertStatement { revert TestError(0, false, message); revert TestError( - 0, false, someVeryLongFunctionNameToGetDynamicErrorMessageString() + 0, + false, + someVeryLongFunctionNameToGetDynamicErrorMessageString() ); revert /* comment13 */ /* comment14 */ TestError( /* comment15 */ - 1234567890, false, message + 1234567890, + false, + message ); revert TestError( /* comment16 */ diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 53733d18d301a..4c8b09def26ef 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -138,7 +138,7 @@ ignore = [] contract_new_lines = false sort_imports = false pow_no_space = false -call_compact_args = true +prefer_compact = "all" [lint] severity = [] @@ -1317,7 +1317,7 @@ forgetest_init!(test_default_config, |prj, cmd| { "contract_new_lines": false, "sort_imports": false, "pow_no_space": false, - "call_compact_args": true + "prefer_compact": "all" }, "lint": { "severity": [],