From 98fc4a2785e5982a76dc7e516e1992be1da16181 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 9 Apr 2025 19:42:57 +0200 Subject: [PATCH 01/54] init --- Cargo.lock | 16 + Cargo.toml | 1 + crates/fmt-2/Cargo.toml | 30 + crates/fmt-2/README.md | 222 + crates/fmt-2/src/buffer.rs | 442 ++ crates/fmt-2/src/chunk.rs | 60 + crates/fmt-2/src/comments.rs | 456 ++ crates/fmt-2/src/formatter.rs | 3885 +++++++++++++++++ crates/fmt-2/src/helpers.rs | 135 + crates/fmt-2/src/inline_config.rs | 155 + crates/fmt-2/src/lib.rs | 27 + crates/fmt-2/src/macros.rs | 125 + crates/fmt-2/src/solang_ext/ast_eq.rs | 683 +++ crates/fmt-2/src/solang_ext/loc.rs | 156 + crates/fmt-2/src/solang_ext/mod.rs | 28 + crates/fmt-2/src/solang_ext/safe_unwrap.rs | 52 + crates/fmt-2/src/string.rs | 181 + crates/fmt-2/src/visit.rs | 642 +++ crates/fmt-2/testdata/Annotation/fmt.sol | 15 + crates/fmt-2/testdata/Annotation/original.sol | 15 + .../fmt-2/testdata/ArrayExpressions/fmt.sol | 69 + .../testdata/ArrayExpressions/original.sol | 47 + crates/fmt-2/testdata/BlockComments/fmt.sol | 25 + .../fmt-2/testdata/BlockComments/original.sol | 26 + .../testdata/BlockCommentsFunction/fmt.sol | 20 + .../BlockCommentsFunction/original.sol | 20 + .../ConditionalOperatorExpression/fmt.sol | 37 + .../original.sol | 33 + .../testdata/ConstructorDefinition/fmt.sol | 42 + .../ConstructorDefinition/original.sol | 17 + .../testdata/ConstructorModifierStyle/fmt.sol | 13 + .../ConstructorModifierStyle/original.sol | 13 + .../bracket-spacing.fmt.sol | 37 + .../contract-new-lines.fmt.sol | 52 + .../fmt-2/testdata/ContractDefinition/fmt.sol | 47 + .../testdata/ContractDefinition/original.sol | 40 + .../fmt-2/testdata/DoWhileStatement/fmt.sol | 30 + .../testdata/DoWhileStatement/original.sol | 24 + crates/fmt-2/testdata/DocComments/fmt.sol | 100 + .../fmt-2/testdata/DocComments/original.sol | 95 + .../DocComments/wrap-comments.fmt.sol | 128 + crates/fmt-2/testdata/EmitStatement/fmt.sol | 31 + .../fmt-2/testdata/EmitStatement/original.sol | 24 + .../EnumDefinition/bracket-spacing.fmt.sol | 21 + crates/fmt-2/testdata/EnumDefinition/fmt.sol | 20 + .../testdata/EnumDefinition/original.sol | 7 + crates/fmt-2/testdata/EnumVariants/fmt.sol | 19 + .../fmt-2/testdata/EnumVariants/original.sol | 23 + crates/fmt-2/testdata/ErrorDefinition/fmt.sol | 14 + .../testdata/ErrorDefinition/original.sol | 14 + crates/fmt-2/testdata/EventDefinition/fmt.sol | 144 + .../testdata/EventDefinition/original.sol | 36 + crates/fmt-2/testdata/ForStatement/fmt.sol | 37 + .../fmt-2/testdata/ForStatement/original.sol | 33 + .../FunctionCall/bracket-spacing.fmt.sol | 42 + crates/fmt-2/testdata/FunctionCall/fmt.sol | 41 + .../fmt-2/testdata/FunctionCall/original.sol | 34 + .../bracket-spacing.fmt.sol | 55 + .../FunctionCallArgsStatement/fmt.sol | 54 + .../FunctionCallArgsStatement/original.sol | 50 + .../FunctionDefinition/all-params.fmt.sol | 732 ++++ .../testdata/FunctionDefinition/all.fmt.sol | 730 ++++ .../fmt-2/testdata/FunctionDefinition/fmt.sol | 709 +++ .../testdata/FunctionDefinition/original.sol | 218 + .../override-spacing.fmt.sol | 710 +++ .../FunctionDefinition/params-first.fmt.sol | 716 +++ .../FunctionDefinition/params-multi.fmt.sol | 710 +++ .../fmt.sol | 21 + .../original.sol | 21 + crates/fmt-2/testdata/FunctionType/fmt.sol | 31 + .../fmt-2/testdata/FunctionType/original.sol | 31 + .../testdata/HexUnderscore/bytes.fmt.sol | 10 + crates/fmt-2/testdata/HexUnderscore/fmt.sol | 9 + .../fmt-2/testdata/HexUnderscore/original.sol | 9 + .../testdata/HexUnderscore/preserve.fmt.sol | 10 + .../testdata/HexUnderscore/remove.fmt.sol | 10 + .../testdata/IfStatement/block-multi.fmt.sol | 171 + .../testdata/IfStatement/block-single.fmt.sol | 123 + crates/fmt-2/testdata/IfStatement/fmt.sol | 145 + .../fmt-2/testdata/IfStatement/original.sol | 119 + crates/fmt-2/testdata/IfStatement2/fmt.sol | 7 + .../fmt-2/testdata/IfStatement2/original.sol | 10 + .../ImportDirective/bracket-spacing.fmt.sol | 21 + crates/fmt-2/testdata/ImportDirective/fmt.sol | 20 + .../testdata/ImportDirective/original.sol | 10 + .../ImportDirective/preserve-quote.fmt.sol | 21 + .../ImportDirective/single-quote.fmt.sol | 21 + crates/fmt-2/testdata/InlineDisable/fmt.sol | 507 +++ .../fmt-2/testdata/InlineDisable/original.sol | 486 +++ crates/fmt-2/testdata/IntTypes/fmt.sol | 24 + crates/fmt-2/testdata/IntTypes/original.sol | 24 + .../fmt-2/testdata/IntTypes/preserve.fmt.sol | 25 + crates/fmt-2/testdata/IntTypes/short.fmt.sol | 25 + .../fmt-2/testdata/LiteralExpression/fmt.sol | 59 + .../testdata/LiteralExpression/original.sol | 58 + .../LiteralExpression/preserve-quote.fmt.sol | 60 + .../LiteralExpression/single-quote.fmt.sol | 60 + crates/fmt-2/testdata/MappingType/fmt.sol | 35 + .../fmt-2/testdata/MappingType/original.sol | 23 + .../fmt-2/testdata/ModifierDefinition/fmt.sol | 14 + .../testdata/ModifierDefinition/original.sol | 9 + .../override-spacing.fmt.sol | 15 + .../NamedFunctionCallExpression/fmt.sol | 47 + .../NamedFunctionCallExpression/original.sol | 28 + .../testdata/NumberLiteralUnderscore/fmt.sol | 25 + .../NumberLiteralUnderscore/original.sol | 25 + .../NumberLiteralUnderscore/preserve.fmt.sol | 26 + .../NumberLiteralUnderscore/remove.fmt.sol | 26 + .../NumberLiteralUnderscore/thousands.fmt.sol | 26 + .../testdata/OperatorExpressions/fmt.sol | 43 + .../testdata/OperatorExpressions/original.sol | 30 + crates/fmt-2/testdata/PragmaDirective/fmt.sol | 9 + .../testdata/PragmaDirective/original.sol | 9 + crates/fmt-2/testdata/Repros/fmt.sol | 161 + crates/fmt-2/testdata/Repros/original.sol | 160 + crates/fmt-2/testdata/ReturnStatement/fmt.sol | 66 + .../testdata/ReturnStatement/original.sol | 60 + .../testdata/RevertNamedArgsStatement/fmt.sol | 35 + .../RevertNamedArgsStatement/original.sol | 32 + crates/fmt-2/testdata/RevertStatement/fmt.sol | 56 + .../testdata/RevertStatement/original.sol | 44 + crates/fmt-2/testdata/SimpleComments/fmt.sol | 80 + .../testdata/SimpleComments/original.sol | 83 + .../SimpleComments/wrap-comments.fmt.sol | 92 + crates/fmt-2/testdata/SortedImports/fmt.sol | 34 + .../fmt-2/testdata/SortedImports/original.sol | 23 + .../StatementBlock/bracket-spacing.fmt.sol | 20 + crates/fmt-2/testdata/StatementBlock/fmt.sol | 19 + .../testdata/StatementBlock/original.sol | 17 + .../StructDefinition/bracket-spacing.fmt.sol | 15 + .../fmt-2/testdata/StructDefinition/fmt.sol | 14 + .../testdata/StructDefinition/original.sol | 10 + crates/fmt-2/testdata/ThisExpression/fmt.sol | 20 + .../testdata/ThisExpression/original.sol | 17 + crates/fmt-2/testdata/TrailingComma/fmt.sol | 12 + .../fmt-2/testdata/TrailingComma/original.sol | 12 + crates/fmt-2/testdata/TryStatement/fmt.sol | 74 + .../fmt-2/testdata/TryStatement/original.sol | 66 + crates/fmt-2/testdata/TypeDefinition/fmt.sol | 12 + .../testdata/TypeDefinition/original.sol | 12 + crates/fmt-2/testdata/UnitExpression/fmt.sol | 24 + .../testdata/UnitExpression/original.sol | 23 + crates/fmt-2/testdata/UsingDirective/fmt.sol | 36 + .../testdata/UsingDirective/original.sol | 11 + .../bracket-spacing.fmt.sol | 27 + .../fmt-2/testdata/VariableAssignment/fmt.sol | 26 + .../testdata/VariableAssignment/original.sol | 26 + .../fmt-2/testdata/VariableDefinition/fmt.sol | 65 + .../testdata/VariableDefinition/original.sol | 27 + .../override-spacing.fmt.sol | 66 + .../WhileStatement/block-multi.fmt.sol | 80 + .../WhileStatement/block-single.fmt.sol | 52 + crates/fmt-2/testdata/WhileStatement/fmt.sol | 59 + .../testdata/WhileStatement/original.sol | 51 + crates/fmt-2/testdata/Yul/fmt.sol | 188 + crates/fmt-2/testdata/Yul/original.sol | 141 + crates/fmt-2/testdata/YulStrings/fmt.sol | 16 + crates/fmt-2/testdata/YulStrings/original.sol | 16 + .../YulStrings/preserve-quote.fmt.sol | 17 + .../testdata/YulStrings/single-quote.fmt.sol | 17 + crates/fmt-2/tests/formatter.rs | 203 + 161 files changed, 18420 insertions(+) create mode 100644 crates/fmt-2/Cargo.toml create mode 100644 crates/fmt-2/README.md create mode 100644 crates/fmt-2/src/buffer.rs create mode 100644 crates/fmt-2/src/chunk.rs create mode 100644 crates/fmt-2/src/comments.rs create mode 100644 crates/fmt-2/src/formatter.rs create mode 100644 crates/fmt-2/src/helpers.rs create mode 100644 crates/fmt-2/src/inline_config.rs create mode 100644 crates/fmt-2/src/lib.rs create mode 100644 crates/fmt-2/src/macros.rs create mode 100644 crates/fmt-2/src/solang_ext/ast_eq.rs create mode 100644 crates/fmt-2/src/solang_ext/loc.rs create mode 100644 crates/fmt-2/src/solang_ext/mod.rs create mode 100644 crates/fmt-2/src/solang_ext/safe_unwrap.rs create mode 100644 crates/fmt-2/src/string.rs create mode 100644 crates/fmt-2/src/visit.rs create mode 100644 crates/fmt-2/testdata/Annotation/fmt.sol create mode 100644 crates/fmt-2/testdata/Annotation/original.sol create mode 100644 crates/fmt-2/testdata/ArrayExpressions/fmt.sol create mode 100644 crates/fmt-2/testdata/ArrayExpressions/original.sol create mode 100644 crates/fmt-2/testdata/BlockComments/fmt.sol create mode 100644 crates/fmt-2/testdata/BlockComments/original.sol create mode 100644 crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol create mode 100644 crates/fmt-2/testdata/BlockCommentsFunction/original.sol create mode 100644 crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol create mode 100644 crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol create mode 100644 crates/fmt-2/testdata/ConstructorDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/ConstructorDefinition/original.sol create mode 100644 crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol create mode 100644 crates/fmt-2/testdata/ConstructorModifierStyle/original.sol create mode 100644 crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol create mode 100644 crates/fmt-2/testdata/ContractDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/ContractDefinition/original.sol create mode 100644 crates/fmt-2/testdata/DoWhileStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/DoWhileStatement/original.sol create mode 100644 crates/fmt-2/testdata/DocComments/fmt.sol create mode 100644 crates/fmt-2/testdata/DocComments/original.sol create mode 100644 crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol create mode 100644 crates/fmt-2/testdata/EmitStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/EmitStatement/original.sol create mode 100644 crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/EnumDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/EnumDefinition/original.sol create mode 100644 crates/fmt-2/testdata/EnumVariants/fmt.sol create mode 100644 crates/fmt-2/testdata/EnumVariants/original.sol create mode 100644 crates/fmt-2/testdata/ErrorDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/ErrorDefinition/original.sol create mode 100644 crates/fmt-2/testdata/EventDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/EventDefinition/original.sol create mode 100644 crates/fmt-2/testdata/ForStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/ForStatement/original.sol create mode 100644 crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionCall/fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionCall/original.sol create mode 100644 crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinition/original.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol create mode 100644 crates/fmt-2/testdata/FunctionType/fmt.sol create mode 100644 crates/fmt-2/testdata/FunctionType/original.sol create mode 100644 crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol create mode 100644 crates/fmt-2/testdata/HexUnderscore/fmt.sol create mode 100644 crates/fmt-2/testdata/HexUnderscore/original.sol create mode 100644 crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol create mode 100644 crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol create mode 100644 crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol create mode 100644 crates/fmt-2/testdata/IfStatement/block-single.fmt.sol create mode 100644 crates/fmt-2/testdata/IfStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/IfStatement/original.sol create mode 100644 crates/fmt-2/testdata/IfStatement2/fmt.sol create mode 100644 crates/fmt-2/testdata/IfStatement2/original.sol create mode 100644 crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/ImportDirective/fmt.sol create mode 100644 crates/fmt-2/testdata/ImportDirective/original.sol create mode 100644 crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol create mode 100644 crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol create mode 100644 crates/fmt-2/testdata/InlineDisable/fmt.sol create mode 100644 crates/fmt-2/testdata/InlineDisable/original.sol create mode 100644 crates/fmt-2/testdata/IntTypes/fmt.sol create mode 100644 crates/fmt-2/testdata/IntTypes/original.sol create mode 100644 crates/fmt-2/testdata/IntTypes/preserve.fmt.sol create mode 100644 crates/fmt-2/testdata/IntTypes/short.fmt.sol create mode 100644 crates/fmt-2/testdata/LiteralExpression/fmt.sol create mode 100644 crates/fmt-2/testdata/LiteralExpression/original.sol create mode 100644 crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol create mode 100644 crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol create mode 100644 crates/fmt-2/testdata/MappingType/fmt.sol create mode 100644 crates/fmt-2/testdata/MappingType/original.sol create mode 100644 crates/fmt-2/testdata/ModifierDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/ModifierDefinition/original.sol create mode 100644 crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol create mode 100644 crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol create mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol create mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol create mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol create mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol create mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol create mode 100644 crates/fmt-2/testdata/OperatorExpressions/fmt.sol create mode 100644 crates/fmt-2/testdata/OperatorExpressions/original.sol create mode 100644 crates/fmt-2/testdata/PragmaDirective/fmt.sol create mode 100644 crates/fmt-2/testdata/PragmaDirective/original.sol create mode 100644 crates/fmt-2/testdata/Repros/fmt.sol create mode 100644 crates/fmt-2/testdata/Repros/original.sol create mode 100644 crates/fmt-2/testdata/ReturnStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/ReturnStatement/original.sol create mode 100644 crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol create mode 100644 crates/fmt-2/testdata/RevertStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/RevertStatement/original.sol create mode 100644 crates/fmt-2/testdata/SimpleComments/fmt.sol create mode 100644 crates/fmt-2/testdata/SimpleComments/original.sol create mode 100644 crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol create mode 100644 crates/fmt-2/testdata/SortedImports/fmt.sol create mode 100644 crates/fmt-2/testdata/SortedImports/original.sol create mode 100644 crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/StatementBlock/fmt.sol create mode 100644 crates/fmt-2/testdata/StatementBlock/original.sol create mode 100644 crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/StructDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/StructDefinition/original.sol create mode 100644 crates/fmt-2/testdata/ThisExpression/fmt.sol create mode 100644 crates/fmt-2/testdata/ThisExpression/original.sol create mode 100644 crates/fmt-2/testdata/TrailingComma/fmt.sol create mode 100644 crates/fmt-2/testdata/TrailingComma/original.sol create mode 100644 crates/fmt-2/testdata/TryStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/TryStatement/original.sol create mode 100644 crates/fmt-2/testdata/TypeDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/TypeDefinition/original.sol create mode 100644 crates/fmt-2/testdata/UnitExpression/fmt.sol create mode 100644 crates/fmt-2/testdata/UnitExpression/original.sol create mode 100644 crates/fmt-2/testdata/UsingDirective/fmt.sol create mode 100644 crates/fmt-2/testdata/UsingDirective/original.sol create mode 100644 crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/VariableAssignment/fmt.sol create mode 100644 crates/fmt-2/testdata/VariableAssignment/original.sol create mode 100644 crates/fmt-2/testdata/VariableDefinition/fmt.sol create mode 100644 crates/fmt-2/testdata/VariableDefinition/original.sol create mode 100644 crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol create mode 100644 crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol create mode 100644 crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol create mode 100644 crates/fmt-2/testdata/WhileStatement/fmt.sol create mode 100644 crates/fmt-2/testdata/WhileStatement/original.sol create mode 100644 crates/fmt-2/testdata/Yul/fmt.sol create mode 100644 crates/fmt-2/testdata/Yul/original.sol create mode 100644 crates/fmt-2/testdata/YulStrings/fmt.sol create mode 100644 crates/fmt-2/testdata/YulStrings/original.sol create mode 100644 crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol create mode 100644 crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol create mode 100644 crates/fmt-2/tests/formatter.rs diff --git a/Cargo.lock b/Cargo.lock index 88e94e4ca5bf5..08eef7bb17cf7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3567,6 +3567,22 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "forge-fmt-2" +version = "1.1.0" +dependencies = [ + "alloy-primitives", + "ariadne", + "foundry-config", + "itertools 0.14.0", + "similar-asserts", + "solang-parser", + "thiserror 2.0.12", + "toml 0.8.20", + "tracing", + "tracing-subscriber", +] + [[package]] name = "forge-script" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 33cb640551a08..c2fa3570a611e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "crates/evm/fuzz/", "crates/evm/traces/", "crates/fmt/", + "crates/fmt-2/", "crates/forge/", "crates/script-sequence/", "crates/macros/", diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml new file mode 100644 index 0000000000000..761f05b1064c7 --- /dev/null +++ b/crates/fmt-2/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "forge-fmt-2" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +foundry-config.workspace = true + +alloy-primitives.workspace = true + +ariadne = "0.5" +itertools.workspace = true +solang-parser.workspace = true +thiserror.workspace = true +tracing.workspace = true + +[dev-dependencies] +itertools.workspace = true +similar-asserts.workspace = true +toml.workspace = true +tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/crates/fmt-2/README.md b/crates/fmt-2/README.md new file mode 100644 index 0000000000000..1fc2712ad6e0e --- /dev/null +++ b/crates/fmt-2/README.md @@ -0,0 +1,222 @@ +# Formatter (`fmt`) + +Solidity formatter that respects (some parts of) +the [Style Guide](https://docs.soliditylang.org/en/latest/style-guide.html) and +is tested on the [Prettier Solidity Plugin](https://github.com/prettier-solidity/prettier-plugin-solidity) cases. + +## Architecture + +The formatter works in two steps: + +1. Parse Solidity source code with [solang](https://github.com/hyperledger-labs/solang) into the PT (Parse Tree) + (not the same as Abstract Syntax Tree, [see difference](https://stackoverflow.com/a/9864571)). +2. Walk the PT and output new source code that's compliant with provided config and rule set. + +The technique for walking the tree is based on [Visitor Pattern](https://en.wikipedia.org/wiki/Visitor_pattern) +and works as following: + +1. Implement `Formatter` callback functions for each PT node type. + Every callback function should write formatted output for the current node + and call `Visitable::visit` function for child nodes delegating the output writing. +1. Implement `Visitable` trait and its `visit` function for each PT node type. Every `visit` function should call + corresponding `Formatter`'s callback function. + +### Output + +The formatted output is written into the output buffer in _chunks_. The `Chunk` struct holds the content to be written & +metadata for it. This includes the comments surrounding the content as well as the `needs_space` flag specifying whether +this _chunk_ needs a space. The flag overrides the default behavior of `Formatter::next_char_needs_space` method. + +The content gets written into the `FormatBuffer` which contains the information about the current indentation level, +indentation length, current state as well as the other data determining the rules for writing the content. +`FormatBuffer` implements the `std::fmt::Write` trait where it evaluates the current information and decides how the +content should be written to the destination. + +### Comments + +The solang parser does not output comments as a type of parse tree node, but rather +in a list alongside the parse tree with location information. It is therefore necessary +to infer where to insert the comments and how to format them while traversing the parse tree. + +To handle this, the formatter pre-parses the comments and puts them into two categories: +Prefix and Postfix comments. Prefix comments refer to the node directly after them, and +postfix comments refer to the node before them. As an illustration: + +```solidity +// This is a prefix comment +/* This is also a prefix comment */ +uint variable = 1 + 2; /* this is postfix */ // this is postfix too + // and this is a postfix comment on the next line +``` + +To insert the comments into the appropriate areas, strings get converted to chunks +before being written to the buffer. A chunk is any string that cannot be split by +whitespace. A chunk also carries with it the surrounding comment information. Thereby +when writing the chunk the comments can be added before and after the chunk as well +as any any whitespace surrounding. + +To construct a chunk, the string and the location of the string is given to the +Formatter and the pre-parsed comments before the start and end of the string are +associated with that string. The source code can then further be chunked before the +chunks are written to the buffer. + +To write the chunk, first the comments associated with the start of the chunk get +written to the buffer. Then the Formatter checks if any whitespace is needed between +what's been written to the buffer and what's in the chunk and inserts it where appropriate. +If the chunk content fits on the same line, it will be written directly to the buffer, +otherwise it will be written on the next line. Finally, any associated postfix +comments also get written. + +### Example + +Source code + +```solidity +pragma solidity ^0.8.10 ; +contract HelloWorld { + string public message; + constructor( string memory initMessage) { message = initMessage;} +} + + +event Greet( string indexed name) ; +``` + +Parse Tree (simplified) + +```text +SourceUnit + | PragmaDirective("solidity", "^0.8.10") + | ContractDefinition("HelloWorld") + | VariableDefinition("string", "message", null, ["public"]) + | FunctionDefinition("constructor") + | Parameter("string", "initMessage", ["memory"]) + | EventDefinition("string", "Greet", ["indexed"], ["name"]) +``` + +Formatted source code that was reconstructed from the Parse Tree + +```solidity +pragma solidity ^0.8.10; + +contract HelloWorld { + string public message; + + constructor(string memory initMessage) { + message = initMessage; + } +} + +event Greet(string indexed name); +``` + +### Configuration + +The formatter supports multiple configuration options defined in `FormatterConfig`. + +| Option | Default | Description | +|------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| line_length | 120 | Maximum line length where formatter will try to wrap the line | +| tab_width | 4 | Number of spaces per indentation level | +| bracket_spacing | false | Print spaces between brackets | +| int_types | long | Style of uint/int256 types. Available options: `long`, `short`, `preserve` | +| multiline_func_header | attributes_first | Style of multiline function header in case it doesn't fit. Available options: `params_first`, `params_first_multi`, `attributes_first`, `all`, `all_params` | +| quote_style | double | Style of quotation marks. Available options: `double`, `single`, `preserve` | +| number_underscore | preserve | Style of underscores in number literals. Available options: `preserve`, `remove`, `thousands` | +| hex_underscore | remove | Style of underscores in hex literals. Available options: `preserve`, `remove`, `bytes` | +| single_line_statement_blocks | preserve | Style of single line blocks in statements. Available options: `single`, `multi`, `preserve` | +| override_spacing | false | Print space in state variable, function and modifier `override` attribute | +| wrap_comments | false | Wrap comments on `line_length` reached | +| ignore | [] | Globs to ignore | +| contract_new_lines | false | Add new line at start and end of contract declarations | +| sort_imports | false | Sort import statements alphabetically in groups | + +### Disable Line + +The formatter can be disabled on specific lines by adding a comment `// forgefmt: disable-next-line`, like this: + +```solidity +// forgefmt: disable-next-line +uint x = 100; +``` + +Alternatively, the comment can also be placed at the end of the line. In this case, you'd have to use `disable-line` +instead: + +```solidity +uint x = 100; // forgefmt: disable-line +``` + +### Disable Block + +The formatter can be disabled for a section of code by adding a comment `// forgefmt: disable-start` before and a +comment `// forgefmt: disable-end` after, like this: + +```solidity +// forgefmt: disable-start +uint x = 100; +uint y = 101; +// forgefmt: disable-end +``` + +### Testing + +Tests reside under the `fmt/testdata` folder and specify the malformatted & expected Solidity code. The source code file +is named `original.sol` and expected file(s) are named in a format `({prefix}.)?fmt.sol`. Multiple expected files are +needed for tests covering available configuration options. + +The default configuration values can be overridden from within the expected file by adding a comment in the format +`// config: {config_entry} = {config_value}`. For example: + +```solidity +// config: line_length = 160 +``` + +The `test_directory` macro is used to specify a new folder with source files for the test suite. Each test suite has the +following process: + +1. Preparse comments with config values +2. Parse and compare the AST for source & expected files. + - The `AstEq` trait defines the comparison rules for the AST nodes +3. Format the source file and assert the equality of the output with the expected file. +4. Format the expected files and assert the idempotency of the formatting operation. + +## Contributing + +Check out the [foundry contribution guide](https://github.com/foundry-rs/foundry/blob/master/CONTRIBUTING.md). + +Guidelines for contributing to `forge fmt`: + +### Opening an issue + +1. Create a short concise title describing an issue. + - Bad Title Examples + ```text + Forge fmt does not work + Forge fmt breaks + Forge fmt unexpected behavior + ``` + - Good Title Examples + ```text + Forge fmt postfix comment misplaced + Forge fmt does not inline short yul blocks + ``` +2. Fill in the issue template fields that include foundry version, platform & component info. +3. Provide the code snippets showing the current & expected behaviors. +4. If it's a feature request, specify why this feature is needed. +5. Besides the default label (`T-Bug` for bugs or `T-feature` for features), add `C-forge` and `Cmd-forge-fmt` labels. + +### Fixing A Bug + +1. Specify an issue that is being addressed in the PR description. +2. Add a note on the solution in the PR description. +3. Make sure the PR includes the acceptance test(s). + +### Developing A Feature + +1. Specify an issue that is being addressed in the PR description. +2. Add a note on the solution in the PR description. +3. Provide the test coverage for the new feature. These should include: + - Adding malformatted & expected solidity code under `fmt/testdata/$dir/` + - Testing the behavior of pre and postfix comments + - If it's a new config value, tests covering **all** available options \ No newline at end of file diff --git a/crates/fmt-2/src/buffer.rs b/crates/fmt-2/src/buffer.rs new file mode 100644 index 0000000000000..031cccd6169f8 --- /dev/null +++ b/crates/fmt-2/src/buffer.rs @@ -0,0 +1,442 @@ +//! Format buffer. + +use crate::{ + comments::{CommentState, CommentStringExt}, + string::{QuoteState, QuotedStringExt}, +}; +use std::fmt::Write; + +/// An indent group. The group may optionally skip the first line +#[derive(Clone, Debug, Default)] +struct IndentGroup { + skip_line: bool, +} + +#[derive(Clone, Copy, Debug)] +enum WriteState { + LineStart(CommentState), + WriteTokens(CommentState), + WriteString(char), +} + +impl WriteState { + fn comment_state(&self) -> CommentState { + match self { + Self::LineStart(state) => *state, + Self::WriteTokens(state) => *state, + Self::WriteString(_) => CommentState::None, + } + } +} + +impl Default for WriteState { + fn default() -> Self { + Self::LineStart(CommentState::default()) + } +} + +/// A wrapper around a `std::fmt::Write` interface. The wrapper keeps track of indentation as well +/// as information about the last `write_str` command if available. The formatter may also be +/// restricted to a single line, in which case it will throw an error on a newline +#[derive(Clone, Debug)] +pub struct FormatBuffer { + pub w: W, + indents: Vec, + base_indent_len: usize, + tab_width: usize, + last_char: Option, + current_line_len: usize, + restrict_to_single_line: bool, + state: WriteState, +} + +impl FormatBuffer { + pub fn new(w: W, tab_width: usize) -> Self { + Self { + w, + tab_width, + base_indent_len: 0, + indents: vec![], + current_line_len: 0, + last_char: None, + restrict_to_single_line: false, + state: WriteState::default(), + } + } + + /// Create a new temporary buffer based on an existing buffer which retains information about + /// the buffer state, but has a blank String as its underlying `Write` interface + pub fn create_temp_buf(&self) -> FormatBuffer { + let mut new = FormatBuffer::new(String::new(), self.tab_width); + new.base_indent_len = self.total_indent_len(); + new.current_line_len = self.current_line_len(); + new.last_char = self.last_char; + new.restrict_to_single_line = self.restrict_to_single_line; + new.state = match self.state { + WriteState::WriteTokens(state) | WriteState::LineStart(state) => { + WriteState::LineStart(state) + } + WriteState::WriteString(ch) => WriteState::WriteString(ch), + }; + new + } + + /// Restrict the buffer to a single line + pub fn restrict_to_single_line(&mut self, restricted: bool) { + self.restrict_to_single_line = restricted; + } + + /// Indent the buffer by delta + pub fn indent(&mut self, delta: usize) { + self.indents.extend(std::iter::repeat_n(IndentGroup::default(), delta)); + } + + /// Dedent the buffer by delta + pub fn dedent(&mut self, delta: usize) { + self.indents.truncate(self.indents.len() - delta); + } + + /// Get the current level of the indent. This is multiplied by the tab width to get the + /// resulting indent + fn level(&self) -> usize { + self.indents.iter().filter(|i| !i.skip_line).count() + } + + /// Check if the last indent group is being skipped + pub fn last_indent_group_skipped(&self) -> bool { + self.indents.last().map(|i| i.skip_line).unwrap_or(false) + } + + /// Set whether the last indent group should be skipped + pub fn set_last_indent_group_skipped(&mut self, skip_line: bool) { + if let Some(i) = self.indents.last_mut() { + i.skip_line = skip_line + } + } + + /// Get the current indent size (level * tab_width) + pub fn current_indent_len(&self) -> usize { + self.level() * self.tab_width + } + + /// Get the total indent size + pub fn total_indent_len(&self) -> usize { + self.current_indent_len() + self.base_indent_len + } + + /// Get the current written position (this does not include the indent size) + pub fn current_line_len(&self) -> usize { + self.current_line_len + } + + /// Check if the buffer is at the beginning of a new line + pub fn is_beginning_of_line(&self) -> bool { + matches!(self.state, WriteState::LineStart(_)) + } + + /// Start a new indent group (skips first indent) + pub fn start_group(&mut self) { + self.indents.push(IndentGroup { skip_line: true }); + } + + /// End the last indent group + pub fn end_group(&mut self) { + self.indents.pop(); + } + + /// Get the last char written to the buffer + pub fn last_char(&self) -> Option { + self.last_char + } + + /// When writing a newline apply state changes + fn handle_newline(&mut self, mut comment_state: CommentState) { + if comment_state == CommentState::Line { + comment_state = CommentState::None; + } + self.current_line_len = 0; + self.set_last_indent_group_skipped(false); + self.last_char = Some('\n'); + self.state = WriteState::LineStart(comment_state); + } +} + +impl FormatBuffer { + /// Write a raw string to the buffer. This will ignore indents and remove the indents of the + /// written string to match the current base indent of this buffer if it is a temp buffer + pub fn write_raw(&mut self, s: impl AsRef) -> std::fmt::Result { + self._write_raw(s.as_ref()) + } + + fn _write_raw(&mut self, s: &str) -> std::fmt::Result { + let mut lines = s.lines().peekable(); + let mut comment_state = self.state.comment_state(); + while let Some(line) = lines.next() { + // remove the whitespace that covered by the base indent length (this is normally the + // case with temporary buffers as this will be re-added by the underlying IndentWriter + // later on + let (new_comment_state, line_start) = line + .comment_state_char_indices() + .with_state(comment_state) + .take(self.base_indent_len) + .take_while(|(_, _, ch)| ch.is_whitespace()) + .last() + .map(|(state, idx, _)| (state, idx + 1)) + .unwrap_or((comment_state, 0)); + comment_state = new_comment_state; + let trimmed_line = &line[line_start..]; + if !trimmed_line.is_empty() { + self.w.write_str(trimmed_line)?; + self.current_line_len += trimmed_line.len(); + self.last_char = trimmed_line.chars().next_back(); + self.state = WriteState::WriteTokens(comment_state); + } + if lines.peek().is_some() || s.ends_with('\n') { + if self.restrict_to_single_line { + return Err(std::fmt::Error) + } + self.w.write_char('\n')?; + self.handle_newline(comment_state); + } + } + Ok(()) + } +} + +impl Write for FormatBuffer { + fn write_str(&mut self, mut s: &str) -> std::fmt::Result { + if s.is_empty() { + return Ok(()) + } + + let mut indent = " ".repeat(self.current_indent_len()); + + loop { + match self.state { + WriteState::LineStart(mut comment_state) => { + match s.find(|b| b != '\n') { + // No non-empty lines in input, write the entire string (only newlines) + None => { + if !s.is_empty() { + self.w.write_str(s)?; + self.handle_newline(comment_state); + } + break + } + + // We can see the next non-empty line. Write up to the + // beginning of that line, then insert an indent, then + // continue. + Some(len) => { + let (head, tail) = s.split_at(len); + self.w.write_str(head)?; + self.w.write_str(&indent)?; + self.current_line_len = 0; + self.last_char = Some(' '); + // a newline has been inserted + if len > 0 { + if self.last_indent_group_skipped() { + indent = " ".repeat(self.current_indent_len() + self.tab_width); + self.set_last_indent_group_skipped(false); + } + if comment_state == CommentState::Line { + comment_state = CommentState::None; + } + } + s = tail; + self.state = WriteState::WriteTokens(comment_state); + } + } + } + WriteState::WriteTokens(comment_state) => { + if s.is_empty() { + break + } + + // find the next newline or non-comment string separator (e.g. ' or ") + let mut len = 0; + let mut new_state = WriteState::WriteTokens(comment_state); + for (state, idx, ch) in s.comment_state_char_indices().with_state(comment_state) + { + len = idx; + if ch == '\n' { + if self.restrict_to_single_line { + return Err(std::fmt::Error) + } + new_state = WriteState::LineStart(state); + break + } else if state == CommentState::None && (ch == '\'' || ch == '"') { + new_state = WriteState::WriteString(ch); + break + } else { + new_state = WriteState::WriteTokens(state); + } + } + + if matches!(new_state, WriteState::WriteTokens(_)) { + // No newlines or strings found, write the entire string + self.w.write_str(s)?; + self.current_line_len += s.len(); + self.last_char = s.chars().next_back(); + self.state = new_state; + break + } else { + // A newline or string has been found. Write up to that character and + // continue on the tail + let (head, tail) = s.split_at(len + 1); + self.w.write_str(head)?; + s = tail; + match new_state { + WriteState::LineStart(comment_state) => { + self.handle_newline(comment_state) + } + new_state => { + self.current_line_len += head.len(); + self.last_char = head.chars().next_back(); + self.state = new_state; + } + } + } + } + WriteState::WriteString(quote) => { + match s.quoted_ranges().with_state(QuoteState::String(quote)).next() { + // No end found, write the rest of the string + None => { + self.w.write_str(s)?; + self.current_line_len += s.len(); + self.last_char = s.chars().next_back(); + break + } + // String end found, write the string and continue to add tokens after + Some((_, _, len)) => { + let (head, tail) = s.split_at(len + 1); + self.w.write_str(head)?; + if let Some((_, last)) = head.rsplit_once('\n') { + self.set_last_indent_group_skipped(false); + self.current_line_len = last.len(); + } else { + self.current_line_len += head.len(); + } + self.last_char = Some(quote); + s = tail; + self.state = WriteState::WriteTokens(CommentState::None); + } + } + } + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const TAB_WIDTH: usize = 4; + + #[test] + fn test_buffer_indents() -> std::fmt::Result { + let delta = 1; + + let mut buf = FormatBuffer::new(String::new(), TAB_WIDTH); + assert_eq!(buf.indents.len(), 0); + assert_eq!(buf.level(), 0); + assert_eq!(buf.current_indent_len(), 0); + + buf.indent(delta); + assert_eq!(buf.indents.len(), delta); + assert_eq!(buf.level(), delta); + assert_eq!(buf.current_indent_len(), delta * TAB_WIDTH); + + buf.indent(delta); + buf.set_last_indent_group_skipped(true); + assert!(buf.last_indent_group_skipped()); + assert_eq!(buf.indents.len(), delta * 2); + assert_eq!(buf.level(), delta); + assert_eq!(buf.current_indent_len(), delta * TAB_WIDTH); + buf.dedent(delta); + + buf.dedent(delta); + assert_eq!(buf.indents.len(), 0); + assert_eq!(buf.level(), 0); + assert_eq!(buf.current_indent_len(), 0); + + // panics on extra dedent + let res = std::panic::catch_unwind(|| buf.clone().dedent(delta)); + assert!(res.is_err()); + + Ok(()) + } + + #[test] + fn test_identical_temp_buf() -> std::fmt::Result { + let content = "test string"; + let multiline_content = "test\nmultiline\nmultiple"; + let mut buf = FormatBuffer::new(String::new(), TAB_WIDTH); + + // create identical temp buf + let mut temp = buf.create_temp_buf(); + writeln!(buf, "{content}")?; + writeln!(temp, "{content}")?; + assert_eq!(buf.w, format!("{content}\n")); + assert_eq!(temp.w, buf.w); + assert_eq!(temp.current_line_len, buf.current_line_len); + assert_eq!(temp.base_indent_len, buf.total_indent_len()); + + let delta = 1; + buf.indent(delta); + + let mut temp_indented = buf.create_temp_buf(); + assert!(temp_indented.w.is_empty()); + assert_eq!(temp_indented.base_indent_len, buf.total_indent_len()); + assert_eq!(temp_indented.level() + delta, buf.level()); + + let indent = " ".repeat(delta * TAB_WIDTH); + + let mut original_buf = buf.clone(); + write!(buf, "{multiline_content}")?; + let expected_content = format!( + "{}\n{}{}", + content, + indent, + multiline_content.lines().collect::>().join(&format!("\n{indent}")) + ); + assert_eq!(buf.w, expected_content); + + write!(temp_indented, "{multiline_content}")?; + + // write temp buf to original and assert the result + write!(original_buf, "{}", temp_indented.w)?; + assert_eq!(buf.w, original_buf.w); + + Ok(()) + } + + #[test] + fn test_preserves_original_content_with_default_settings() -> std::fmt::Result { + let contents = [ + "simple line", + r" + some + multiline + content", + "// comment", + "/* comment */", + r"mutliline + content + // comment1 + with comments + /* comment2 */ ", + ]; + + for content in &contents { + let mut buf = FormatBuffer::new(String::new(), TAB_WIDTH); + write!(buf, "{content}")?; + assert_eq!(&buf.w, content); + } + + Ok(()) + } +} diff --git a/crates/fmt-2/src/chunk.rs b/crates/fmt-2/src/chunk.rs new file mode 100644 index 0000000000000..7d9ce25c7fbd0 --- /dev/null +++ b/crates/fmt-2/src/chunk.rs @@ -0,0 +1,60 @@ +use crate::comments::CommentWithMetadata; + +/// Holds information about a non-whitespace-splittable string, and the surrounding comments +#[derive(Clone, Debug, Default)] +pub struct Chunk { + pub postfixes_before: Vec, + pub prefixes: Vec, + pub content: String, + pub postfixes: Vec, + pub needs_space: Option, +} + +impl From for Chunk { + fn from(string: String) -> Self { + Self { content: string, ..Default::default() } + } +} + +impl From<&str> for Chunk { + fn from(string: &str) -> Self { + Self { content: string.to_owned(), ..Default::default() } + } +} + +// The struct with information about chunks used in the [Formatter::surrounded] method +#[derive(Debug)] +pub struct SurroundingChunk { + pub before: Option, + pub next: Option, + pub spaced: Option, + pub content: String, +} + +impl SurroundingChunk { + pub fn new( + content: impl std::fmt::Display, + before: Option, + next: Option, + ) -> Self { + Self { before, next, content: format!("{content}"), spaced: None } + } + + pub fn spaced(mut self) -> Self { + self.spaced = Some(true); + self + } + + pub fn non_spaced(mut self) -> Self { + self.spaced = Some(false); + self + } + + pub fn loc_before(&self) -> usize { + self.before.unwrap_or_default() + } + + pub fn loc_next(&self) -> Option { + self.next + } +} diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs new file mode 100644 index 0000000000000..eafdb998910d9 --- /dev/null +++ b/crates/fmt-2/src/comments.rs @@ -0,0 +1,456 @@ +use crate::inline_config::{InlineConfigItem, InvalidInlineConfigItem}; +use itertools::Itertools; +use solang_parser::pt::*; +use std::collections::VecDeque; + +/// The type of a Comment +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CommentType { + /// A Line comment (e.g. `// ...`) + Line, + /// A Block comment (e.g. `/* ... */`) + Block, + /// A Doc Line comment (e.g. `/// ...`) + DocLine, + /// A Doc Block comment (e.g. `/** ... */`) + DocBlock, +} + +/// The comment position +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CommentPosition { + /// Comes before the code it describes + Prefix, + /// Comes after the code it describes + Postfix, +} + +/// Comment with additional metadata +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CommentWithMetadata { + pub ty: CommentType, + pub loc: Loc, + pub has_newline_before: bool, + pub indent_len: usize, + pub comment: String, + pub position: CommentPosition, +} + +impl PartialOrd for CommentWithMetadata { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for CommentWithMetadata { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.loc.cmp(&other.loc) + } +} + +impl CommentWithMetadata { + fn new( + comment: Comment, + position: CommentPosition, + has_newline_before: bool, + indent_len: usize, + ) -> Self { + let (ty, loc, comment) = match comment { + Comment::Line(loc, comment) => (CommentType::Line, loc, comment), + Comment::Block(loc, comment) => (CommentType::Block, loc, comment), + Comment::DocLine(loc, comment) => (CommentType::DocLine, loc, comment), + Comment::DocBlock(loc, comment) => (CommentType::DocBlock, loc, comment), + }; + Self { + comment: comment.trim_end().to_string(), + ty, + loc, + position, + has_newline_before, + indent_len, + } + } + + /// Construct a comment with metadata by analyzing its surrounding source code + fn from_comment_and_src(comment: Comment, src: &str, last_comment: Option<&Self>) -> Self { + let src_before = &src[..comment.loc().start()]; + if src_before.is_empty() { + return Self::new(comment, CommentPosition::Prefix, false, 0) + } + + let mut lines_before = src_before.lines().rev(); + let this_line = + if src_before.ends_with('\n') { "" } else { lines_before.next().unwrap_or_default() }; + let indent_len = this_line.chars().take_while(|c| c.is_whitespace()).count(); + let last_line = lines_before.next().map(str::trim_start); + + if matches!(comment, Comment::DocLine(..) | Comment::DocBlock(..)) { + return Self::new( + comment, + CommentPosition::Prefix, + last_line.is_none_or(str::is_empty), + indent_len, + ) + } + + // TODO: this loop takes almost the entirety of the time spent in parsing, which is up to + // 80% of `crate::fmt` + let mut code_end = 0; + for (state, idx, ch) in src_before.comment_state_char_indices() { + if matches!(state, CommentState::None) && !ch.is_whitespace() { + code_end = idx; + } + } + + let (position, has_newline_before) = if src_before[code_end..].contains('\n') { + // comment sits on a line without code + if let Some(last_line) = last_line { + if last_line.is_empty() { + // line before is empty + (CommentPosition::Prefix, true) + } else { + // line has something + // check if the last comment after code was a postfix comment + if last_comment + .is_some_and(|last| last.loc.end() > code_end && !last.is_prefix()) + { + // get the indent size of the next item of code + let next_indent_len = src[comment.loc().end()..] + .non_comment_chars() + .take_while(|ch| ch.is_whitespace()) + .fold(indent_len, |indent, ch| if ch == '\n' { 0 } else { indent + 1 }); + if indent_len > next_indent_len { + // the comment indent is bigger than the next code indent + (CommentPosition::Postfix, false) + } else { + // the comment indent is equal to or less than the next code + // indent + (CommentPosition::Prefix, false) + } + } else { + // if there is no postfix comment after the piece of code + (CommentPosition::Prefix, false) + } + } + } else { + // beginning of file + (CommentPosition::Prefix, false) + } + } else { + // comment is after some code + (CommentPosition::Postfix, false) + }; + + Self::new(comment, position, has_newline_before, indent_len) + } + + pub fn is_line(&self) -> bool { + matches!(self.ty, CommentType::Line | CommentType::DocLine) + } + + pub fn is_doc_block(&self) -> bool { + matches!(self.ty, CommentType::DocBlock) + } + + pub fn is_prefix(&self) -> bool { + matches!(self.position, CommentPosition::Prefix) + } + + pub fn is_before(&self, byte: usize) -> bool { + self.loc.start() < byte + } + + /// Returns the contents of the comment without the start and end tokens + pub fn contents(&self) -> &str { + let mut s = self.comment.as_str(); + if let Some(stripped) = s.strip_prefix(self.start_token()) { + s = stripped; + } + if let Some(end_token) = self.end_token() { + if let Some(stripped) = s.strip_suffix(end_token) { + s = stripped; + } + } + s + } + + /// The start token of the comment + #[inline] + pub const fn start_token(&self) -> &'static str { + match self.ty { + CommentType::Line => "//", + CommentType::Block => "/*", + CommentType::DocLine => "///", + CommentType::DocBlock => "/**", + } + } + + /// The token that gets written on the newline when the + /// comment is wrapped + #[inline] + pub const fn wrap_token(&self) -> &'static str { + match self.ty { + CommentType::Line => "// ", + CommentType::DocLine => "/// ", + CommentType::Block => "", + CommentType::DocBlock => " * ", + } + } + + /// The end token of the comment + #[inline] + pub const fn end_token(&self) -> Option<&'static str> { + match self.ty { + CommentType::Line | CommentType::DocLine => None, + CommentType::Block | CommentType::DocBlock => Some("*/"), + } + } +} + +/// A list of comments +#[derive(Clone, Debug, Default)] +pub struct Comments { + prefixes: VecDeque, + postfixes: VecDeque, +} + +impl Comments { + pub fn new(mut comments: Vec, src: &str) -> Self { + let mut prefixes = VecDeque::with_capacity(comments.len()); + let mut postfixes = VecDeque::with_capacity(comments.len()); + let mut last_comment = None; + + comments.sort_by_key(|comment| comment.loc()); + for comment in comments { + let comment = CommentWithMetadata::from_comment_and_src(comment, src, last_comment); + let vec = if comment.is_prefix() { &mut prefixes } else { &mut postfixes }; + vec.push_back(comment); + last_comment = Some(vec.back().unwrap()); + } + Self { prefixes, postfixes } + } + + /// Helper for removing comments before a byte offset + fn remove_comments_before( + comments: &mut VecDeque, + byte: usize, + ) -> Vec { + let pos = comments + .iter() + .find_position(|comment| !comment.is_before(byte)) + .map(|(idx, _)| idx) + .unwrap_or_else(|| comments.len()); + if pos == 0 { + return Vec::new() + } + comments.rotate_left(pos); + comments.split_off(comments.len() - pos).into() + } + + /// Remove any prefix comments that occur before the byte offset in the src + pub(crate) fn remove_prefixes_before(&mut self, byte: usize) -> Vec { + Self::remove_comments_before(&mut self.prefixes, byte) + } + + /// Remove any postfix comments that occur before the byte offset in the src + pub(crate) fn remove_postfixes_before(&mut self, byte: usize) -> Vec { + Self::remove_comments_before(&mut self.postfixes, byte) + } + + /// Remove any comments that occur before the byte offset in the src + pub(crate) fn remove_all_comments_before(&mut self, byte: usize) -> Vec { + self.remove_prefixes_before(byte) + .into_iter() + .merge(self.remove_postfixes_before(byte)) + .collect() + } + + pub(crate) fn pop(&mut self) -> Option { + if self.iter().next()?.is_prefix() { + self.prefixes.pop_front() + } else { + self.postfixes.pop_front() + } + } + + pub(crate) fn iter(&self) -> impl Iterator { + self.prefixes.iter().merge(self.postfixes.iter()) + } + + /// Parse all comments to return a list of inline config items. This will return an iterator of + /// results of parsing comments which start with `forgefmt:` + pub fn parse_inline_config_items( + &self, + ) -> impl Iterator> + '_ + { + self.iter() + .filter_map(|comment| { + Some((comment, comment.contents().trim_start().strip_prefix("forgefmt:")?.trim())) + }) + .map(|(comment, item)| { + let loc = comment.loc; + item.parse().map(|out| (loc, out)).map_err(|out| (loc, out)) + }) + } +} + +/// The state of a character in a string with possible comments +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum CommentState { + /// character not in a comment + #[default] + None, + /// First `/` in line comment start `"//"` + LineStart1, + /// Second `/` in line comment start `"//"` + LineStart2, + /// Character in a line comment + Line, + /// `/` in block comment start `"/*"` + BlockStart1, + /// `*` in block comment start `"/*"` + BlockStart2, + /// Character in a block comment + Block, + /// `*` in block comment end `"*/"` + BlockEnd1, + /// `/` in block comment end `"*/"` + BlockEnd2, +} + +/// An Iterator over characters and indices in a string slice with information about the state of +/// comments +pub struct CommentStateCharIndices<'a> { + iter: std::str::CharIndices<'a>, + state: CommentState, +} + +impl<'a> CommentStateCharIndices<'a> { + #[inline] + fn new(string: &'a str) -> Self { + Self { iter: string.char_indices(), state: CommentState::None } + } + + #[inline] + pub fn with_state(mut self, state: CommentState) -> Self { + self.state = state; + self + } + + #[inline] + pub fn peek(&mut self) -> Option<(usize, char)> { + self.iter.clone().next() + } +} + +impl Iterator for CommentStateCharIndices<'_> { + type Item = (CommentState, usize, char); + + #[inline] + fn next(&mut self) -> Option { + let (idx, ch) = self.iter.next()?; + match self.state { + CommentState::None => { + if ch == '/' { + self.state = match self.peek() { + Some((_, '/')) => CommentState::LineStart1, + Some((_, '*')) => CommentState::BlockStart1, + _ => CommentState::None, + }; + } + } + CommentState::LineStart1 => { + self.state = CommentState::LineStart2; + } + CommentState::LineStart2 => { + self.state = CommentState::Line; + } + CommentState::Line => { + if ch == '\n' { + self.state = CommentState::None; + } + } + CommentState::BlockStart1 => { + self.state = CommentState::BlockStart2; + } + CommentState::BlockStart2 => { + self.state = CommentState::Block; + } + CommentState::Block => { + if ch == '*' { + if let Some((_, '/')) = self.peek() { + self.state = CommentState::BlockEnd1; + } + } + } + CommentState::BlockEnd1 => { + self.state = CommentState::BlockEnd2; + } + CommentState::BlockEnd2 => { + self.state = CommentState::None; + } + } + Some((self.state, idx, ch)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.iter.count() + } +} + +impl std::iter::FusedIterator for CommentStateCharIndices<'_> {} + +/// An Iterator over characters in a string slice which are not a apart of comments +pub struct NonCommentChars<'a>(CommentStateCharIndices<'a>); + +impl Iterator for NonCommentChars<'_> { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { + for (state, _, ch) in self.0.by_ref() { + if state == CommentState::None { + return Some(ch) + } + } + None + } +} + +/// Helpers for iterating over comment containing strings +pub trait CommentStringExt { + fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_>; + + #[inline] + fn non_comment_chars(&self) -> NonCommentChars<'_> { + NonCommentChars(self.comment_state_char_indices()) + } + + #[inline] + fn trim_comments(&self) -> String { + self.non_comment_chars().collect() + } +} + +impl CommentStringExt for T +where + T: AsRef, +{ + #[inline] + fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_> { + CommentStateCharIndices::new(self.as_ref()) + } +} + +impl CommentStringExt for str { + #[inline] + fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_> { + CommentStateCharIndices::new(self) + } +} diff --git a/crates/fmt-2/src/formatter.rs b/crates/fmt-2/src/formatter.rs new file mode 100644 index 0000000000000..aee9eee91db12 --- /dev/null +++ b/crates/fmt-2/src/formatter.rs @@ -0,0 +1,3885 @@ +//! A Solidity formatter + +use crate::{ + buffer::*, + chunk::*, + comments::{ + CommentPosition, CommentState, CommentStringExt, CommentType, CommentWithMetadata, Comments, + }, + format_diagnostics_report, + helpers::import_path_string, + macros::*, + solang_ext::{pt::*, *}, + string::{QuoteState, QuotedStringExt}, + visit::{Visitable, Visitor}, + FormatterConfig, InlineConfig, IntTypes, +}; +use alloy_primitives::Address; +use foundry_config::fmt::{HexUnderscore, MultilineFuncHeaderStyle, SingleLineBlockStyle}; +use itertools::{Either, Itertools}; +use solang_parser::diagnostics::Diagnostic; +use std::{fmt::Write, path::PathBuf, str::FromStr}; +use thiserror::Error; + +type Result = std::result::Result; + +/// A custom Error thrown by the Formatter +#[derive(Debug, Error)] +pub enum FormatterError { + /// Error thrown by `std::fmt::Write` interfaces + #[error(transparent)] + Fmt(#[from] std::fmt::Error), + /// Encountered invalid parse tree item. + #[error("encountered invalid parse tree item at {0:?}")] + InvalidParsedItem(Loc), + /// Failed to parse the source code + #[error("failed to parse file:\n{}", format_diagnostics_report(_0, _1.as_deref(), _2))] + Parse(String, Option, Vec), + /// All other errors + #[error(transparent)] + Custom(Box), +} + +impl FormatterError { + fn fmt() -> Self { + Self::Fmt(std::fmt::Error) + } + + fn custom(err: impl std::error::Error + Send + Sync + 'static) -> Self { + Self::Custom(Box::new(err)) + } +} + +#[expect(unused_macros)] +macro_rules! format_err { + ($msg:literal $(,)?) => { + $crate::formatter::FormatterError::custom($msg.to_string()) + }; + ($err:expr $(,)?) => { + $crate::formatter::FormatterError::custom($err) + }; + ($fmt:expr, $($arg:tt)*) => { + $crate::formatter::FormatterError::custom(format!($fmt, $($arg)*)) + }; +} + +macro_rules! bail { + ($msg:literal $(,)?) => { + return Err($crate::formatter::format_err!($msg)) + }; + ($err:expr $(,)?) => { + return Err($err) + }; + ($fmt:expr, $($arg:tt)*) => { + return Err($crate::formatter::format_err!($fmt, $(arg)*)) + }; +} + +// TODO: store context entities as references without copying +/// Current context of the Formatter (e.g. inside Contract or Function definition) +#[derive(Debug, Default)] +struct Context { + contract: Option, + function: Option, + if_stmt_single_line: Option, +} + +impl Context { + /// Returns true if the current function context is the constructor + pub(crate) fn is_constructor_function(&self) -> bool { + self.function.as_ref().is_some_and(|f| matches!(f.ty, FunctionTy::Constructor)) + } +} + +/// A Solidity formatter +#[derive(Debug)] +pub struct Formatter<'a, W> { + buf: FormatBuffer, + source: &'a str, + config: FormatterConfig, + temp_bufs: Vec>, + context: Context, + comments: Comments, + inline_config: InlineConfig, +} + +impl<'a, W: Write> Formatter<'a, W> { + pub fn new( + w: W, + source: &'a str, + comments: Comments, + inline_config: InlineConfig, + config: FormatterConfig, + ) -> Self { + Self { + buf: FormatBuffer::new(w, config.tab_width), + source, + config, + temp_bufs: Vec::new(), + context: Context::default(), + comments, + inline_config, + } + } + + /// Get the Write interface of the current temp buffer or the underlying Write + fn buf(&mut self) -> &mut dyn Write { + match &mut self.temp_bufs[..] { + [] => &mut self.buf as &mut dyn Write, + [.., buf] => buf as &mut dyn Write, + } + } + + /// Casts the current writer `w` as a `String` reference. Should only be used for debugging. + unsafe fn buf_contents(&self) -> &String { + *(&self.buf.w as *const W as *const &mut String) + } + + /// Casts the current `W` writer or the current temp buffer as a `String` reference. + /// Should only be used for debugging. + #[expect(dead_code)] + unsafe fn temp_buf_contents(&self) -> &String { + match &self.temp_bufs[..] { + [] => self.buf_contents(), + [.., buf] => &buf.w, + } + } + + buf_fn! { fn indent(&mut self, delta: usize) } + buf_fn! { fn dedent(&mut self, delta: usize) } + buf_fn! { fn start_group(&mut self) } + buf_fn! { fn end_group(&mut self) } + buf_fn! { fn create_temp_buf(&self) -> FormatBuffer } + buf_fn! { fn restrict_to_single_line(&mut self, restricted: bool) } + buf_fn! { fn current_line_len(&self) -> usize } + buf_fn! { fn total_indent_len(&self) -> usize } + buf_fn! { fn is_beginning_of_line(&self) -> bool } + buf_fn! { fn last_char(&self) -> Option } + buf_fn! { fn last_indent_group_skipped(&self) -> bool } + buf_fn! { fn set_last_indent_group_skipped(&mut self, skip: bool) } + buf_fn! { fn write_raw(&mut self, s: impl AsRef) -> std::fmt::Result } + + /// Do the callback within the context of a temp buffer + fn with_temp_buf( + &mut self, + mut fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result> { + self.temp_bufs.push(self.create_temp_buf()); + let res = fun(self); + let out = self.temp_bufs.pop().unwrap(); + res?; + Ok(out) + } + + /// Does the next written character require whitespace before + fn next_char_needs_space(&self, next_char: char) -> bool { + if self.is_beginning_of_line() { + return false + } + let last_char = + if let Some(last_char) = self.last_char() { last_char } else { return false }; + if last_char.is_whitespace() || next_char.is_whitespace() { + return false + } + match last_char { + '{' => match next_char { + '{' | '[' | '(' => false, + '/' => true, + _ => self.config.bracket_spacing, + }, + '(' | '.' | '[' => matches!(next_char, '/'), + '/' => true, + _ => match next_char { + '}' => self.config.bracket_spacing, + ')' | ',' | '.' | ';' | ']' => false, + _ => true, + }, + } + } + + /// Is length of the `text` with respect to already written line <= `config.line_length` + fn will_it_fit(&self, text: impl AsRef) -> bool { + let text = text.as_ref(); + if text.is_empty() { + return true + } + if text.contains('\n') { + return false + } + let space: usize = self.next_char_needs_space(text.chars().next().unwrap()).into(); + self.config.line_length >= + self.total_indent_len() + .saturating_add(self.current_line_len()) + .saturating_add(text.chars().count() + space) + } + + /// Write empty brackets with respect to `config.bracket_spacing` setting: + /// `"{ }"` if `true`, `"{}"` if `false` + fn write_empty_brackets(&mut self) -> Result<()> { + let brackets = if self.config.bracket_spacing { "{ }" } else { "{}" }; + write_chunk!(self, "{brackets}")?; + Ok(()) + } + + /// Write semicolon to the buffer + fn write_semicolon(&mut self) -> Result<()> { + write!(self.buf(), ";")?; + Ok(()) + } + + /// Write whitespace separator to the buffer + /// `"\n"` if `multiline` is `true`, `" "` if `false` + fn write_whitespace_separator(&mut self, multiline: bool) -> Result<()> { + if !self.is_beginning_of_line() { + write!(self.buf(), "{}", if multiline { "\n" } else { " " })?; + } + Ok(()) + } + + /// Write new line with preserved `last_indent_group_skipped` flag + fn write_preserved_line(&mut self) -> Result<()> { + let last_indent_group_skipped = self.last_indent_group_skipped(); + writeln!(self.buf())?; + self.set_last_indent_group_skipped(last_indent_group_skipped); + Ok(()) + } + + /// Write unformatted src and comments for given location. + fn write_raw_src(&mut self, loc: Loc) -> Result<()> { + let disabled_stmts_src = String::from_utf8(self.source.as_bytes()[loc.range()].to_vec()) + .map_err(FormatterError::custom)?; + self.write_raw(disabled_stmts_src.trim_end())?; + self.write_whitespace_separator(true)?; + // Remove comments as they're already included in disabled src. + let _ = self.comments.remove_all_comments_before(loc.end()); + Ok(()) + } + + /// Returns number of blank lines in source between two byte indexes + fn blank_lines(&self, start: usize, end: usize) -> usize { + // because of sorting import statements, start can be greater than end + if start > end { + return 0 + } + self.source[start..end].trim_comments().matches('\n').count() + } + + /// Get the byte offset of the next line + fn find_next_line(&self, byte_offset: usize) -> Option { + let mut iter = self.source[byte_offset..].char_indices(); + while let Some((_, ch)) = iter.next() { + match ch { + '\n' => return iter.next().map(|(idx, _)| byte_offset + idx), + '\r' => { + return iter.next().and_then(|(idx, ch)| match ch { + '\n' => iter.next().map(|(idx, _)| byte_offset + idx), + _ => Some(byte_offset + idx), + }) + } + _ => {} + } + } + None + } + + /// Find the next instance of the character in source excluding comments + fn find_next_in_src(&self, byte_offset: usize, needle: char) -> Option { + self.source[byte_offset..] + .comment_state_char_indices() + .position(|(state, _, ch)| needle == ch && state == CommentState::None) + .map(|p| byte_offset + p) + } + + /// Find the start of the next instance of a slice in source + fn find_next_str_in_src(&self, byte_offset: usize, needle: &str) -> Option { + let subset = &self.source[byte_offset..]; + needle.chars().next().and_then(|first_char| { + subset + .comment_state_char_indices() + .position(|(state, idx, ch)| { + first_char == ch && + state == CommentState::None && + idx + needle.len() <= subset.len() && + subset[idx..idx + needle.len()] == *needle + }) + .map(|p| byte_offset + p) + }) + } + + /// Extends the location to the next instance of a character. Returns true if the loc was + /// extended + fn extend_loc_until(&self, loc: &mut Loc, needle: char) -> bool { + if let Some(end) = self.find_next_in_src(loc.end(), needle).map(|offset| offset + 1) { + *loc = loc.with_end(end); + true + } else { + false + } + } + + /// Return the flag whether the attempt should be made + /// to write the block on a single line. + /// If the block style is configured to [SingleLineBlockStyle::Preserve], + /// lookup whether there was a newline introduced in `[start_from, end_at]` range + /// where `end_at` is the start of the block. + fn should_attempt_block_single_line( + &mut self, + stmt: &mut Statement, + start_from: usize, + ) -> bool { + match self.config.single_line_statement_blocks { + SingleLineBlockStyle::Single => true, + SingleLineBlockStyle::Multi => false, + SingleLineBlockStyle::Preserve => { + let end_at = match stmt { + Statement::Block { statements, .. } if !statements.is_empty() => { + statements.first().as_ref().unwrap().loc().start() + } + Statement::Expression(loc, _) => loc.start(), + _ => stmt.loc().start(), + }; + + self.find_next_line(start_from).is_some_and(|loc| loc >= end_at) + } + } + } + + /// Create a chunk given a string and the location information + fn chunk_at( + &mut self, + byte_offset: usize, + next_byte_offset: Option, + needs_space: Option, + content: impl std::fmt::Display, + ) -> Chunk { + Chunk { + postfixes_before: self.comments.remove_postfixes_before(byte_offset), + prefixes: self.comments.remove_prefixes_before(byte_offset), + content: content.to_string(), + postfixes: next_byte_offset + .map(|byte_offset| self.comments.remove_postfixes_before(byte_offset)) + .unwrap_or_default(), + needs_space, + } + } + + /// Create a chunk given a callback + fn chunked( + &mut self, + byte_offset: usize, + next_byte_offset: Option, + mut fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result { + self.chunked_mono(byte_offset, next_byte_offset, &mut fun) + } + + fn chunked_mono( + &mut self, + byte_offset: usize, + next_byte_offset: Option, + fun: &mut dyn FnMut(&mut Self) -> Result<()>, + ) -> Result { + let postfixes_before = self.comments.remove_postfixes_before(byte_offset); + let prefixes = self.comments.remove_prefixes_before(byte_offset); + let content = self.with_temp_buf(fun)?.w; + let postfixes = next_byte_offset + .map(|byte_offset| self.comments.remove_postfixes_before(byte_offset)) + .unwrap_or_default(); + Ok(Chunk { postfixes_before, prefixes, content, postfixes, needs_space: None }) + } + + /// Create a chunk given a [Visitable] item + fn visit_to_chunk( + &mut self, + byte_offset: usize, + next_byte_offset: Option, + visitable: &mut impl Visitable, + ) -> Result { + self.chunked(byte_offset, next_byte_offset, |fmt| { + visitable.visit(fmt)?; + Ok(()) + }) + } + + /// Transform [Visitable] items to the list of chunks + fn items_to_chunks<'b>( + &mut self, + next_byte_offset: Option, + items: impl Iterator + 'b, + ) -> Result> { + let mut items = items.peekable(); + let mut out = Vec::with_capacity(items.size_hint().1.unwrap_or(0)); + while let Some((loc, item)) = items.next() { + let chunk_next_byte_offset = + items.peek().map(|(loc, _)| loc.start()).or(next_byte_offset); + + let chunk = if self.inline_config.is_disabled(loc) { + // If item format is disabled, we determine last disabled line from item and create + // chunk with raw src. + let mut disabled_loc = loc; + self.chunked(disabled_loc.start(), chunk_next_byte_offset, |fmt| { + while fmt.inline_config.is_disabled(disabled_loc) { + if let Some(next_line) = fmt.find_next_line(disabled_loc.end()) { + disabled_loc = disabled_loc.with_end(next_line); + } else { + break; + } + } + fmt.write_raw_src(disabled_loc)?; + Ok(()) + })? + } else { + self.visit_to_chunk(loc.start(), chunk_next_byte_offset, item)? + }; + out.push(chunk); + } + Ok(out) + } + + /// Transform [Visitable] items to a list of chunks and then sort those chunks. + fn items_to_chunks_sorted<'b>( + &mut self, + next_byte_offset: Option, + items: impl Iterator + 'b, + ) -> Result> { + let mut items = items.peekable(); + let mut out = Vec::with_capacity(items.size_hint().1.unwrap_or(0)); + while let Some(item) = items.next() { + let chunk_next_byte_offset = + items.peek().map(|next| next.loc().start()).or(next_byte_offset); + let chunk = self.visit_to_chunk(item.loc().start(), chunk_next_byte_offset, item)?; + out.push((item, chunk)); + } + out.sort_by(|(a, _), (b, _)| a.cmp(b)); + Ok(out.into_iter().map(|(_, c)| c).collect()) + } + + /// Write a comment to the buffer formatted. + /// WARNING: This may introduce a newline if the comment is a Line comment + /// or if the comment are wrapped + fn write_comment(&mut self, comment: &CommentWithMetadata, is_first: bool) -> Result<()> { + if self.inline_config.is_disabled(comment.loc) { + return self.write_raw_comment(comment) + } + + match comment.position { + CommentPosition::Prefix => self.write_prefix_comment(comment, is_first), + CommentPosition::Postfix => self.write_postfix_comment(comment), + } + } + + /// Write a comment with position [CommentPosition::Prefix] + fn write_prefix_comment( + &mut self, + comment: &CommentWithMetadata, + is_first: bool, + ) -> Result<()> { + if !self.is_beginning_of_line() { + self.write_preserved_line()?; + } + if !is_first && comment.has_newline_before { + self.write_preserved_line()?; + } + + if matches!(comment.ty, CommentType::DocBlock) { + let mut lines = comment.contents().trim().lines(); + writeln!(self.buf(), "{}", comment.start_token())?; + lines.try_for_each(|l| self.write_doc_block_line(comment, l))?; + write!(self.buf(), " {}", comment.end_token().unwrap())?; + self.write_preserved_line()?; + return Ok(()) + } + + write!(self.buf(), "{}", comment.start_token())?; + + let mut wrapped = false; + let contents = comment.contents(); + let mut lines = contents.lines().peekable(); + while let Some(line) = lines.next() { + wrapped |= self.write_comment_line(comment, line)?; + if lines.peek().is_some() { + self.write_preserved_line()?; + } + } + + if let Some(end) = comment.end_token() { + // Check if the end token in the original comment was on the separate line + if !wrapped && comment.comment.lines().count() > contents.lines().count() { + self.write_preserved_line()?; + } + write!(self.buf(), "{end}")?; + } + if self.find_next_line(comment.loc.end()).is_some() { + self.write_preserved_line()?; + } + + Ok(()) + } + + /// Write a comment with position [CommentPosition::Postfix] + fn write_postfix_comment(&mut self, comment: &CommentWithMetadata) -> Result<()> { + let indented = self.is_beginning_of_line(); + self.indented_if(indented, 1, |fmt| { + if !indented && fmt.next_char_needs_space('/') { + fmt.write_whitespace_separator(false)?; + } + + write!(fmt.buf(), "{}", comment.start_token())?; + let start_token_pos = fmt.current_line_len(); + + let mut lines = comment.contents().lines().peekable(); + fmt.grouped(|fmt| { + while let Some(line) = lines.next() { + fmt.write_comment_line(comment, line)?; + if lines.peek().is_some() { + fmt.write_whitespace_separator(true)?; + } + } + Ok(()) + })?; + + if let Some(end) = comment.end_token() { + // If comment is not multiline, end token has to be aligned with the start + if fmt.is_beginning_of_line() { + write!(fmt.buf(), "{}{end}", " ".repeat(start_token_pos))?; + } else { + write!(fmt.buf(), "{end}")?; + } + } + + if comment.is_line() { + fmt.write_whitespace_separator(true)?; + } + Ok(()) + }) + } + + /// Write the line of a doc block comment line + fn write_doc_block_line(&mut self, comment: &CommentWithMetadata, line: &str) -> Result<()> { + if line.trim().starts_with('*') { + let line = line.trim().trim_start_matches('*'); + let needs_space = line.chars().next().is_some_and(|ch| !ch.is_whitespace()); + write!(self.buf(), " *{}", if needs_space { " " } else { "" })?; + self.write_comment_line(comment, line)?; + self.write_whitespace_separator(true)?; + return Ok(()) + } + + let indent_whitespace_count = line + .char_indices() + .take_while(|(idx, ch)| ch.is_whitespace() && *idx <= self.buf.current_indent_len()) + .count(); + let to_skip = indent_whitespace_count - indent_whitespace_count % self.config.tab_width; + write!(self.buf(), " *")?; + let content = &line[to_skip..]; + if !content.trim().is_empty() { + write!(self.buf(), " ")?; + self.write_comment_line(comment, &line[to_skip..])?; + } + self.write_whitespace_separator(true)?; + Ok(()) + } + + /// Write a comment line that might potentially overflow the maximum line length + /// and, if configured, will be wrapped to the next line. + fn write_comment_line(&mut self, comment: &CommentWithMetadata, line: &str) -> Result { + if self.will_it_fit(line) || !self.config.wrap_comments { + let start_with_ws = + line.chars().next().map(|ch| ch.is_whitespace()).unwrap_or_default(); + if !self.is_beginning_of_line() || !start_with_ws { + write!(self.buf(), "{line}")?; + return Ok(false) + } + + // if this is the beginning of the line, + // the comment should start with at least an indent + let indent = self.buf.current_indent_len(); + let mut chars = line + .char_indices() + .skip_while(|(idx, ch)| ch.is_whitespace() && *idx < indent) + .map(|(_, ch)| ch); + let padded = format!("{}{}", " ".repeat(indent), chars.join("")); + self.write_raw(padded)?; + return Ok(false) + } + + let mut words = line.split(' ').peekable(); + while let Some(word) = words.next() { + if self.is_beginning_of_line() { + write!(self.buf(), "{}", word.trim_start())?; + } else { + self.write_raw(word)?; + } + + if let Some(next) = words.peek() { + if !word.is_empty() && !self.will_it_fit(next) { + // the next word doesn't fit on this line, + // write remaining words on the next + self.write_whitespace_separator(true)?; + // write newline wrap token + write!(self.buf(), "{}", comment.wrap_token())?; + self.write_comment_line(comment, &words.join(" "))?; + return Ok(true) + } + + self.write_whitespace_separator(false)?; + } + } + Ok(false) + } + + /// Write a raw comment. This is like [`write_comment`](Self::write_comment) but won't do any + /// formatting or worry about whitespace behind the comment. + fn write_raw_comment(&mut self, comment: &CommentWithMetadata) -> Result<()> { + self.write_raw(&comment.comment)?; + if comment.is_line() { + self.write_preserved_line()?; + } + Ok(()) + } + + // TODO handle whitespace between comments for disabled sections + /// Write multiple comments + fn write_comments<'b>( + &mut self, + comments: impl IntoIterator, + ) -> Result<()> { + let mut comments = comments.into_iter().peekable(); + let mut last_byte_written = match comments.peek() { + Some(comment) => comment.loc.start(), + None => return Ok(()), + }; + let mut is_first = true; + for comment in comments { + let unwritten_whitespace_loc = + Loc::File(comment.loc.file_no(), last_byte_written, comment.loc.start()); + if self.inline_config.is_disabled(unwritten_whitespace_loc) { + self.write_raw(&self.source[unwritten_whitespace_loc.range()])?; + self.write_raw_comment(comment)?; + last_byte_written = if comment.is_line() { + self.find_next_line(comment.loc.end()).unwrap_or_else(|| comment.loc.end()) + } else { + comment.loc.end() + }; + } else { + self.write_comment(comment, is_first)?; + } + is_first = false; + } + Ok(()) + } + + /// Write a postfix comments before a given location + fn write_postfix_comments_before(&mut self, byte_end: usize) -> Result<()> { + let comments = self.comments.remove_postfixes_before(byte_end); + self.write_comments(&comments) + } + + /// Write all prefix comments before a given location + fn write_prefix_comments_before(&mut self, byte_end: usize) -> Result<()> { + let comments = self.comments.remove_prefixes_before(byte_end); + self.write_comments(&comments) + } + + /// Check if a chunk will fit on the current line + fn will_chunk_fit(&mut self, format_string: &str, chunk: &Chunk) -> Result { + if let Some(chunk_str) = self.simulate_to_single_line(|fmt| fmt.write_chunk(chunk))? { + Ok(self.will_it_fit(format_string.replacen("{}", &chunk_str, 1))) + } else { + Ok(false) + } + } + + /// Check if a separated list of chunks will fit on the current line + fn are_chunks_separated_multiline<'b>( + &mut self, + format_string: &str, + items: impl IntoIterator, + separator: &str, + ) -> Result { + let items = items.into_iter().collect_vec(); + if let Some(chunks) = self.simulate_to_single_line(|fmt| { + fmt.write_chunks_separated(items.iter().copied(), separator, false) + })? { + Ok(!self.will_it_fit(format_string.replacen("{}", &chunks, 1))) + } else { + Ok(true) + } + } + + /// Write the chunk and any surrounding comments into the buffer + /// This will automatically add whitespace before the chunk given the rule set in + /// `next_char_needs_space`. If the chunk does not fit on the current line it will be put on + /// to the next line + fn write_chunk(&mut self, chunk: &Chunk) -> Result<()> { + // handle comments before chunk + self.write_comments(&chunk.postfixes_before)?; + self.write_comments(&chunk.prefixes)?; + + // trim chunk start + let content = if chunk.content.starts_with('\n') { + let mut chunk = chunk.content.trim_start().to_string(); + chunk.insert(0, '\n'); + chunk + } else if chunk.content.starts_with(' ') { + let mut chunk = chunk.content.trim_start().to_string(); + chunk.insert(0, ' '); + chunk + } else { + chunk.content.clone() + }; + + if !content.is_empty() { + // add whitespace if necessary + let needs_space = chunk + .needs_space + .unwrap_or_else(|| self.next_char_needs_space(content.chars().next().unwrap())); + if needs_space { + if self.will_it_fit(&content) { + write!(self.buf(), " ")?; + } else { + writeln!(self.buf())?; + } + } + + // write chunk + write!(self.buf(), "{content}")?; + } + + // write any postfix comments + self.write_comments(&chunk.postfixes)?; + + Ok(()) + } + + /// Write chunks separated by a separator. If `multiline`, each chunk will be written to a + /// separate line + fn write_chunks_separated<'b>( + &mut self, + chunks: impl IntoIterator, + separator: &str, + multiline: bool, + ) -> Result<()> { + let mut chunks = chunks.into_iter().peekable(); + while let Some(chunk) = chunks.next() { + let mut chunk = chunk.clone(); + + // handle postfixes before and add newline if necessary + self.write_comments(&std::mem::take(&mut chunk.postfixes_before))?; + if multiline && !self.is_beginning_of_line() { + writeln!(self.buf())?; + } + + // remove postfixes so we can add separator between + let postfixes = std::mem::take(&mut chunk.postfixes); + + self.write_chunk(&chunk)?; + + // add separator + if chunks.peek().is_some() { + write!(self.buf(), "{separator}")?; + self.write_comments(&postfixes)?; + if multiline && !self.is_beginning_of_line() { + writeln!(self.buf())?; + } + } else { + self.write_comments(&postfixes)?; + } + } + Ok(()) + } + + /// Apply the callback indented by the indent size + fn indented(&mut self, delta: usize, fun: impl FnMut(&mut Self) -> Result<()>) -> Result<()> { + self.indented_if(true, delta, fun) + } + + /// Apply the callback indented by the indent size if the condition is true + fn indented_if( + &mut self, + condition: bool, + delta: usize, + mut fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result<()> { + if condition { + self.indent(delta); + } + let res = fun(self); + if condition { + self.dedent(delta); + } + res?; + Ok(()) + } + + /// Apply the callback into an indent group. The first line of the indent group is not + /// indented but lines thereafter are + fn grouped(&mut self, mut fun: impl FnMut(&mut Self) -> Result<()>) -> Result { + self.start_group(); + let res = fun(self); + let indented = !self.last_indent_group_skipped(); + self.end_group(); + res?; + Ok(indented) + } + + /// Add a function context around a procedure and revert the context at the end of the procedure + /// regardless of the response + fn with_function_context( + &mut self, + context: FunctionDefinition, + mut fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result<()> { + self.context.function = Some(context); + let res = fun(self); + self.context.function = None; + res + } + + /// Add a contract context around a procedure and revert the context at the end of the procedure + /// regardless of the response + fn with_contract_context( + &mut self, + context: ContractDefinition, + mut fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result<()> { + self.context.contract = Some(context); + let res = fun(self); + self.context.contract = None; + res + } + + /// Create a transaction. The result of the transaction is not applied to the buffer unless + /// `Transacton::commit` is called + fn transact<'b>( + &'b mut self, + fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result> { + Transaction::new(self, fun) + } + + /// Do the callback and return the result on the buffer as a string + fn simulate_to_string(&mut self, fun: impl FnMut(&mut Self) -> Result<()>) -> Result { + Ok(self.transact(fun)?.buffer) + } + + /// Turn a chunk and its surrounding comments into a string + fn chunk_to_string(&mut self, chunk: &Chunk) -> Result { + self.simulate_to_string(|fmt| fmt.write_chunk(chunk)) + } + + /// Try to create a string based on a callback. If the string does not fit on a single line + /// this will return `None` + fn simulate_to_single_line( + &mut self, + mut fun: impl FnMut(&mut Self) -> Result<()>, + ) -> Result> { + let mut single_line = false; + let tx = self.transact(|fmt| { + fmt.restrict_to_single_line(true); + single_line = match fun(fmt) { + Ok(()) => true, + Err(FormatterError::Fmt(_)) => false, + Err(err) => bail!(err), + }; + Ok(()) + })?; + Ok(if single_line && tx.will_it_fit(&tx.buffer) { Some(tx.buffer) } else { None }) + } + + /// Try to apply a callback to a single line. If the callback cannot be applied to a single + /// line the callback will not be applied to the buffer and `false` will be returned. Otherwise + /// `true` will be returned + fn try_on_single_line(&mut self, mut fun: impl FnMut(&mut Self) -> Result<()>) -> Result { + let mut single_line = false; + let tx = self.transact(|fmt| { + fmt.restrict_to_single_line(true); + single_line = match fun(fmt) { + Ok(()) => true, + Err(FormatterError::Fmt(_)) => false, + Err(err) => bail!(err), + }; + Ok(()) + })?; + Ok(if single_line && tx.will_it_fit(&tx.buffer) { + tx.commit()?; + true + } else { + false + }) + } + + /// Surrounds a callback with parentheses. The callback will try to be applied to a single + /// line. If the callback cannot be applied to a single line the callback will applied to the + /// nextline indented. The callback receives a `multiline` hint as the second argument which + /// receives `true` in the latter case + fn surrounded( + &mut self, + first: SurroundingChunk, + last: SurroundingChunk, + mut fun: impl FnMut(&mut Self, bool) -> Result<()>, + ) -> Result<()> { + let first_chunk = + self.chunk_at(first.loc_before(), first.loc_next(), first.spaced, first.content); + self.write_chunk(&first_chunk)?; + + let multiline = !self.try_on_single_line(|fmt| { + fun(fmt, false)?; + let last_chunk = + fmt.chunk_at(last.loc_before(), last.loc_next(), last.spaced, &last.content); + fmt.write_chunk(&last_chunk)?; + Ok(()) + })?; + + if multiline { + self.indented(1, |fmt| { + fmt.write_whitespace_separator(true)?; + let stringified = fmt.with_temp_buf(|fmt| fun(fmt, true))?.w; + write_chunk!(fmt, "{}", stringified.trim_start()) + })?; + if !last.content.trim_start().is_empty() { + self.indented(1, |fmt| fmt.write_whitespace_separator(true))?; + } + let last_chunk = + self.chunk_at(last.loc_before(), last.loc_next(), last.spaced, &last.content); + self.write_chunk(&last_chunk)?; + } + + Ok(()) + } + + /// Write each [Visitable] item on a separate line. The function will check if there are any + /// blank lines between each visitable statement and will apply a single blank line if there + /// exists any. The `needs_space` callback can force a newline and is given the last_item if + /// any and the next item as arguments + fn write_lined_visitable<'b, I, V, F>( + &mut self, + loc: Loc, + items: I, + needs_space_fn: F, + ) -> Result<()> + where + I: Iterator + 'b, + V: Visitable + CodeLocation + 'b, + F: Fn(&V, &V) -> bool, + { + let mut items = items.collect::>(); + items.reverse(); + // get next item + let pop_next = |fmt: &mut Self, items: &mut Vec<&'b mut V>| { + let comment = + fmt.comments.iter().next().filter(|comment| comment.loc.end() < loc.end()); + let item = items.last(); + if let (Some(comment), Some(item)) = (comment, item) { + if comment.loc < item.loc() { + Some(Either::Left(fmt.comments.pop().unwrap())) + } else { + Some(Either::Right(items.pop().unwrap())) + } + } else if comment.is_some() { + Some(Either::Left(fmt.comments.pop().unwrap())) + } else if item.is_some() { + Some(Either::Right(items.pop().unwrap())) + } else { + None + } + }; + // get whitespace between to offsets. this needs to account for possible left over + // semicolons which are not included in the `Loc` + let unwritten_whitespace = |from: usize, to: usize| { + let to = to.max(from); + let mut loc = Loc::File(loc.file_no(), from, to); + let src = &self.source[from..to]; + if let Some(semi) = src.find(';') { + loc = loc.with_start(from + semi + 1); + } + (loc, &self.source[loc.range()]) + }; + + let mut last_byte_written = match ( + self.comments.iter().next().filter(|comment| comment.loc.end() < loc.end()), + items.last(), + ) { + (Some(comment), Some(item)) => comment.loc.min(item.loc()), + (None, Some(item)) => item.loc(), + (Some(comment), None) => comment.loc, + (None, None) => return Ok(()), + } + .start(); + + let mut last_loc: Option = None; + let mut visited_locs: Vec = Vec::new(); + + // marker for whether the next item needs additional space + let mut needs_space = false; + let mut last_comment = None; + + while let Some(mut line_item) = pop_next(self, &mut items) { + let loc = line_item.as_ref().either(|c| c.loc, |i| i.loc()); + let (unwritten_whitespace_loc, unwritten_whitespace) = + unwritten_whitespace(last_byte_written, loc.start()); + let ignore_whitespace = if self.inline_config.is_disabled(unwritten_whitespace_loc) { + trace!("Unwritten whitespace: {unwritten_whitespace:?}"); + self.write_raw(unwritten_whitespace)?; + true + } else { + false + }; + match line_item.as_mut() { + Either::Left(comment) => { + if ignore_whitespace { + self.write_raw_comment(comment)?; + if unwritten_whitespace.contains('\n') { + needs_space = false; + } + } else { + self.write_comment(comment, last_loc.is_none())?; + if last_loc.is_some() && comment.has_newline_before { + needs_space = false; + } + } + } + Either::Right(item) => { + if !ignore_whitespace { + self.write_whitespace_separator(true)?; + if let Some(mut last_loc) = last_loc { + // here's an edge case when we reordered items so the last_loc isn't + // necessarily the item that directly precedes the current item because + // the order might have changed, so we need to find the last item that + // is before the current item by checking the recorded locations + if let Some(last_item) = visited_locs + .iter() + .rev() + .find(|prev_item| prev_item.start() > last_loc.end()) + { + last_loc = *last_item; + } + + // The blank lines check is susceptible additional trailing new lines + // because the block docs can contain + // multiple lines, but the function def should follow directly after the + // block comment + let is_last_doc_comment = matches!( + last_comment, + Some(CommentWithMetadata { ty: CommentType::DocBlock, .. }) + ); + + if needs_space || + (!is_last_doc_comment && + self.blank_lines(last_loc.end(), loc.start()) > 1) + { + writeln!(self.buf())?; + } + } + } + if let Some(next_item) = items.last() { + needs_space = needs_space_fn(item, next_item); + } + trace!("Visiting {}", { + let n = std::any::type_name::(); + n.strip_prefix("solang_parser::pt::").unwrap_or(n) + }); + item.visit(self)?; + } + } + + last_loc = Some(loc); + visited_locs.push(loc); + + last_comment = None; + + last_byte_written = loc.end(); + if let Some(comment) = line_item.left() { + if comment.is_line() { + last_byte_written = + self.find_next_line(last_byte_written).unwrap_or(last_byte_written); + } + last_comment = Some(comment); + } + } + + // write manually to avoid eof comment being detected as first + let comments = self.comments.remove_prefixes_before(loc.end()); + for comment in comments { + self.write_comment(&comment, false)?; + } + + let (unwritten_src_loc, mut unwritten_whitespace) = + unwritten_whitespace(last_byte_written, loc.end()); + if self.inline_config.is_disabled(unwritten_src_loc) { + if unwritten_src_loc.end() == self.source.len() { + // remove EOF line ending + unwritten_whitespace = unwritten_whitespace + .strip_suffix('\n') + .map(|w| w.strip_suffix('\r').unwrap_or(w)) + .unwrap_or(unwritten_whitespace); + } + trace!("Unwritten whitespace: {unwritten_whitespace:?}"); + self.write_raw(unwritten_whitespace)?; + } + + Ok(()) + } + + /// Visit the right side of an assignment. The function will try to write the assignment on a + /// single line or indented on the next line. If it can't do this it resorts to letting the + /// expression decide how to split itself on multiple lines + fn visit_assignment(&mut self, expr: &mut Expression) -> Result<()> { + if self.try_on_single_line(|fmt| expr.visit(fmt))? { + return Ok(()) + } + + self.write_postfix_comments_before(expr.loc().start())?; + self.write_prefix_comments_before(expr.loc().start())?; + + if self.try_on_single_line(|fmt| fmt.indented(1, |fmt| expr.visit(fmt)))? { + return Ok(()) + } + + let mut fit_on_next_line = false; + self.indented(1, |fmt| { + let tx = fmt.transact(|fmt| { + writeln!(fmt.buf())?; + fit_on_next_line = fmt.try_on_single_line(|fmt| expr.visit(fmt))?; + Ok(()) + })?; + if fit_on_next_line { + tx.commit()?; + } + Ok(()) + })?; + + if !fit_on_next_line { + self.indented_if(expr.is_unsplittable(), 1, |fmt| expr.visit(fmt))?; + } + + Ok(()) + } + + /// Visit the list of comma separated items. + /// If the prefix is not empty, then the function will write + /// the whitespace before the parentheses (if they are required). + fn visit_list( + &mut self, + prefix: &str, + items: &mut [T], + start_offset: Option, + end_offset: Option, + paren_required: bool, + ) -> Result<()> + where + T: Visitable + CodeLocation, + { + write_chunk!(self, "{}", prefix)?; + let whitespace = if !prefix.is_empty() { " " } else { "" }; + let next_after_start_offset = items.first().map(|item| item.loc().start()); + let first_surrounding = SurroundingChunk::new("", start_offset, next_after_start_offset); + let last_surronding = SurroundingChunk::new(")", None, end_offset); + if items.is_empty() { + if paren_required { + write!(self.buf(), "{whitespace}(")?; + self.surrounded(first_surrounding, last_surronding, |fmt, _| { + // write comments before the list end + write_chunk!(fmt, end_offset.unwrap_or_default(), "")?; + Ok(()) + })?; + } + } else { + write!(self.buf(), "{whitespace}(")?; + self.surrounded(first_surrounding, last_surronding, |fmt, multiline| { + let args = + fmt.items_to_chunks(end_offset, items.iter_mut().map(|arg| (arg.loc(), arg)))?; + let multiline = + multiline && fmt.are_chunks_separated_multiline("{}", &args, ",")?; + fmt.write_chunks_separated(&args, ",", multiline)?; + Ok(()) + })?; + } + Ok(()) + } + + /// Visit the block item. Attempt to write it on the single + /// line if requested. Surround by curly braces and indent + /// each line otherwise. Returns `true` if the block fit + /// on a single line + fn visit_block( + &mut self, + loc: Loc, + statements: &mut [T], + attempt_single_line: bool, + attempt_omit_braces: bool, + ) -> Result + where + T: Visitable + CodeLocation, + { + if attempt_single_line && statements.len() == 1 { + let fits_on_single = self.try_on_single_line(|fmt| { + if !attempt_omit_braces { + write!(fmt.buf(), "{{ ")?; + } + statements.first_mut().unwrap().visit(fmt)?; + if !attempt_omit_braces { + write!(fmt.buf(), " }}")?; + } + Ok(()) + })?; + + if fits_on_single { + return Ok(true) + } + } + + // Determine if any of start / end of the block is disabled and block lines boundaries. + let is_start_disabled = self.inline_config.is_disabled(loc.with_end(loc.start())); + let is_end_disabled = self.inline_config.is_disabled(loc.with_start(loc.end())); + let end_of_first_line = self.find_next_line(loc.start()).unwrap_or_default(); + let end_of_last_line = self.find_next_line(loc.end()).unwrap_or_default(); + + // Write first line of the block: + // - as it is until the end of line, if format disabled + // - start block if line formatted + if is_start_disabled { + self.write_raw_src(loc.with_end(end_of_first_line))?; + } else { + write_chunk!(self, "{{")?; + } + + // Write comments and close block if no statement. + if statements.is_empty() { + self.indented(1, |fmt| { + fmt.write_prefix_comments_before(loc.end())?; + fmt.write_postfix_comments_before(loc.end())?; + Ok(()) + })?; + + write_chunk!(self, "}}")?; + return Ok(false) + } + + // Determine writable statements by excluding statements from disabled start / end lines. + // We check the position of last statement from first line (if disabled) and position of + // first statement from last line (if disabled) and slice accordingly. + let writable_statments = match ( + statements.iter().rposition(|stmt| { + is_start_disabled && + self.find_next_line(stmt.loc().end()).unwrap_or_default() == + end_of_first_line + }), + statements.iter().position(|stmt| { + is_end_disabled && + self.find_next_line(stmt.loc().end()).unwrap_or_default() == end_of_last_line + }), + ) { + // We have statements on both disabled start / end lines. + (Some(start), Some(end)) => { + if start == end || start + 1 == end { + None + } else { + Some(&mut statements[start + 1..end]) + } + } + // We have statements only on disabled start line. + (Some(start), None) => { + if start + 1 == statements.len() { + None + } else { + Some(&mut statements[start + 1..]) + } + } + // We have statements only on disabled end line. + (None, Some(end)) => { + if end == 0 { + None + } else { + Some(&mut statements[..end]) + } + } + // No statements on disabled start / end line. + (None, None) => Some(statements), + }; + + // Write statements that are not on any disabled first / last block line. + let mut statements_loc = loc; + if let Some(writable_statements) = writable_statments { + if let Some(first_statement) = writable_statements.first() { + statements_loc = statements_loc.with_start(first_statement.loc().start()); + self.write_whitespace_separator(true)?; + self.write_postfix_comments_before(statements_loc.start())?; + } + // If last line is disabled then statements location ends where last block line starts. + if is_end_disabled { + if let Some(last_statement) = writable_statements.last() { + statements_loc = statements_loc.with_end( + self.find_next_line(last_statement.loc().end()).unwrap_or_default(), + ); + } + } + self.indented(1, |fmt| { + fmt.write_lined_visitable( + statements_loc, + writable_statements.iter_mut(), + |_, _| false, + )?; + Ok(()) + })?; + self.write_whitespace_separator(true)?; + } + + // Write last line of the block: + // - as it is from where statements location ends until the end of last line, if format + // disabled + // - close block if line formatted + if is_end_disabled { + self.write_raw_src(loc.with_start(statements_loc.end()).with_end(end_of_last_line))?; + } else { + if end_of_first_line != end_of_last_line { + self.write_whitespace_separator(true)?; + } + write_chunk!(self, loc.end(), "}}")?; + } + + Ok(false) + } + + /// Visit statement as `Statement::Block`. + fn visit_stmt_as_block( + &mut self, + stmt: &mut Statement, + attempt_single_line: bool, + ) -> Result { + match stmt { + Statement::Block { loc, statements, .. } => { + self.visit_block(*loc, statements, attempt_single_line, true) + } + _ => self.visit_block(stmt.loc(), &mut [stmt], attempt_single_line, true), + } + } + + /// Visit the generic member access expression and + /// attempt flatten it by checking if the inner expression + /// matches a given member access variant. + fn visit_member_access<'b, T, M>( + &mut self, + expr: &'b mut Box, + ident: &mut Identifier, + mut matcher: M, + ) -> Result<()> + where + T: CodeLocation + Visitable, + M: FnMut(&mut Self, &'b mut Box) -> Result, &'b mut Identifier)>>, + { + let chunk_member_access = |fmt: &mut Self, ident: &mut Identifier, expr: &mut Box| { + fmt.chunked(ident.loc.start(), Some(expr.loc().start()), |fmt| ident.visit(fmt)) + }; + + let mut chunks: Vec = vec![chunk_member_access(self, ident, expr)?]; + let mut remaining = expr; + while let Some((inner_expr, inner_ident)) = matcher(self, remaining)? { + chunks.push(chunk_member_access(self, inner_ident, inner_expr)?); + remaining = inner_expr; + } + + chunks.reverse(); + chunks.iter_mut().for_each(|chunk| chunk.content.insert(0, '.')); + + if !self.try_on_single_line(|fmt| fmt.write_chunks_separated(&chunks, "", false))? { + self.grouped(|fmt| fmt.write_chunks_separated(&chunks, "", true))?; + } + Ok(()) + } + + /// Visit the yul string with an optional identifier. + /// If the identifier is present, write the value in the format `:`. + /// + /// Ref: + fn visit_yul_string_with_ident( + &mut self, + loc: Loc, + val: &str, + ident: &mut Option, + ) -> Result<()> { + let ident = + if let Some(ident) = ident { format!(":{}", ident.name) } else { String::new() }; + write_chunk!(self, loc.start(), loc.end(), "{val}{ident}")?; + Ok(()) + } + + /// Format a quoted string as `prefix"string"` where the quote character is handled + /// by the configuration `quote_style` + fn quote_str(&self, loc: Loc, prefix: Option<&str>, string: &str) -> String { + let get_og_quote = || { + self.source[loc.range()] + .quote_state_char_indices() + .find_map( + |(state, _, ch)| { + if matches!(state, QuoteState::Opening(_)) { + Some(ch) + } else { + None + } + }, + ) + .expect("Could not find quote character for quoted string") + }; + let mut quote = self.config.quote_style.quote().unwrap_or_else(get_og_quote); + let mut quoted = format!("{quote}{string}{quote}"); + if !quoted.is_quoted() { + quote = get_og_quote(); + quoted = format!("{quote}{string}{quote}"); + } + let prefix = prefix.unwrap_or(""); + format!("{prefix}{quoted}") + } + + /// Write a quoted string. See `Formatter::quote_str` for more information + fn write_quoted_str(&mut self, loc: Loc, prefix: Option<&str>, string: &str) -> Result<()> { + write_chunk!(self, loc.start(), loc.end(), "{}", self.quote_str(loc, prefix, string)) + } + + /// Write and format numbers. This will fix underscores as well as remove unnecessary 0's and + /// exponents + fn write_num_literal( + &mut self, + loc: Loc, + value: &str, + fractional: Option<&str>, + exponent: &str, + unit: &mut Option, + ) -> Result<()> { + let config = self.config.number_underscore; + + // get source if we preserve underscores + let (value, fractional, exponent) = if config.is_preserve() { + let source = &self.source[loc.start()..loc.end()]; + // Strip unit + let (source, _) = source.split_once(' ').unwrap_or((source, "")); + let (val, exp) = source.split_once(['e', 'E']).unwrap_or((source, "")); + let (val, fract) = + val.split_once('.').map(|(val, fract)| (val, Some(fract))).unwrap_or((val, None)); + ( + val.trim().to_string(), + fract.map(|fract| fract.trim().to_string()), + exp.trim().to_string(), + ) + } else { + // otherwise strip underscores + ( + value.trim().replace('_', ""), + fractional.map(|fract| fract.trim().replace('_', "")), + exponent.trim().replace('_', ""), + ) + }; + + // strip any padded 0's + let val = value.trim_start_matches('0'); + let fract = fractional.as_ref().map(|fract| fract.trim_end_matches('0')); + let (exp_sign, mut exp) = if let Some(exp) = exponent.strip_prefix('-') { + ("-", exp) + } else { + ("", exponent.as_str()) + }; + exp = exp.trim().trim_start_matches('0'); + + let add_underscores = |string: &str, reversed: bool| -> String { + if !config.is_thousands() || string.len() < 5 { + return string.to_string() + } + if reversed { + Box::new(string.as_bytes().chunks(3)) as Box> + } else { + Box::new(string.as_bytes().rchunks(3).rev()) as Box> + } + .map(|chunk| std::str::from_utf8(chunk).expect("valid utf8 content.")) + .collect::>() + .join("_") + }; + + let mut out = String::new(); + if val.is_empty() { + out.push('0'); + } else { + out.push_str(&add_underscores(val, false)); + } + if let Some(fract) = fract { + out.push('.'); + if fract.is_empty() { + out.push('0'); + } else { + // TODO re-enable me on the next solang-parser v0.1.18 + // currently disabled because of the following bug + // https://github.com/hyperledger-labs/solang/pull/954 + // out.push_str(&add_underscores(fract, true)); + out.push_str(fract) + } + } + if !exp.is_empty() { + out.push('e'); + out.push_str(exp_sign); + out.push_str(&add_underscores(exp, false)); + } + + write_chunk!(self, loc.start(), loc.end(), "{out}")?; + self.write_unit(unit) + } + + /// Write and hex literals according to the configuration. + fn write_hex_literal(&mut self, lit: &HexLiteral) -> Result<()> { + let HexLiteral { loc, hex } = lit; + match self.config.hex_underscore { + HexUnderscore::Remove => self.write_quoted_str(*loc, Some("hex"), hex), + HexUnderscore::Preserve => { + let quote = &self.source[loc.start()..loc.end()].trim_start_matches("hex"); + // source is always quoted so we remove the quotes first so we can adhere to the + // configured quoting style + let hex = "e[1..quote.len() - 1]; + self.write_quoted_str(*loc, Some("hex"), hex) + } + HexUnderscore::Bytes => { + // split all bytes + let hex = hex + .chars() + .chunks(2) + .into_iter() + .map(|chunk| chunk.collect::()) + .collect::>() + .join("_"); + self.write_quoted_str(*loc, Some("hex"), &hex) + } + } + } + + /// Write built-in unit. + fn write_unit(&mut self, unit: &mut Option) -> Result<()> { + if let Some(unit) = unit { + write_chunk!(self, unit.loc.start(), unit.loc.end(), "{}", unit.name)?; + } + Ok(()) + } + + /// Write the function header + fn write_function_header( + &mut self, + func: &mut FunctionDefinition, + body_loc: Option, + header_multiline: bool, + ) -> Result { + let func_name = if let Some(ident) = &func.name { + format!("{} {}", func.ty, ident.name) + } else { + func.ty.to_string() + }; + + // calculate locations of chunk groups + let attrs_loc = func.attributes.first().map(|attr| attr.loc()); + let returns_loc = func.returns.first().map(|param| param.0); + + let params_next_offset = attrs_loc + .as_ref() + .or(returns_loc.as_ref()) + .or(body_loc.as_ref()) + .map(|loc| loc.start()); + let attrs_end = returns_loc.as_ref().or(body_loc.as_ref()).map(|loc| loc.start()); + let returns_end = body_loc.as_ref().map(|loc| loc.start()); + + let mut params_multiline = false; + + let params_loc = { + let mut loc = func.loc.with_end(func.loc.start()); + self.extend_loc_until(&mut loc, ')'); + loc + }; + let params_disabled = self.inline_config.is_disabled(params_loc); + if params_disabled { + let chunk = self.chunked(func.loc.start(), None, |fmt| fmt.visit_source(params_loc))?; + params_multiline = chunk.content.contains('\n'); + self.write_chunk(&chunk)?; + } else { + let first_surrounding = SurroundingChunk::new( + format!("{func_name}("), + Some(func.loc.start()), + Some( + func.params + .first() + .map(|param| param.0.start()) + .unwrap_or_else(|| params_loc.end()), + ), + ); + self.surrounded( + first_surrounding, + SurroundingChunk::new(")", None, params_next_offset), + |fmt, multiline| { + let params = fmt.items_to_chunks( + params_next_offset, + func.params + .iter_mut() + .filter_map(|(loc, param)| param.as_mut().map(|param| (*loc, param))), + )?; + let after_params = if !func.attributes.is_empty() || !func.returns.is_empty() { + "" + } else if func.body.is_some() { + " {" + } else { + ";" + }; + let should_multiline = header_multiline && + matches!( + fmt.config.multiline_func_header, + MultilineFuncHeaderStyle::ParamsFirst | + MultilineFuncHeaderStyle::ParamsFirstMulti | + MultilineFuncHeaderStyle::All | + MultilineFuncHeaderStyle::AllParams + ); + params_multiline = should_multiline || + multiline || + fmt.are_chunks_separated_multiline( + &format!("{{}}){after_params}"), + ¶ms, + ",", + )?; + // Write new line if we have only one parameter and params first set, + // or if the function definition is multiline and all params set. + let single_param_multiline = matches!( + fmt.config.multiline_func_header, + MultilineFuncHeaderStyle::ParamsFirst + ) || params_multiline && + matches!( + fmt.config.multiline_func_header, + MultilineFuncHeaderStyle::AllParams + ); + if params.len() == 1 && single_param_multiline { + writeln!(fmt.buf())?; + } + fmt.write_chunks_separated(¶ms, ",", params_multiline)?; + Ok(()) + }, + )?; + } + + let mut write_attributes = |fmt: &mut Self, multiline: bool| -> Result<()> { + // write attributes + if !func.attributes.is_empty() { + let attrs_loc = func + .attributes + .first() + .unwrap() + .loc() + .with_end_from(&func.attributes.last().unwrap().loc()); + if fmt.inline_config.is_disabled(attrs_loc) { + // If params are also disabled then write functions attributes on the same line. + if params_disabled { + fmt.write_whitespace_separator(false)?; + let attrs_src = + String::from_utf8(self.source.as_bytes()[attrs_loc.range()].to_vec()) + .map_err(FormatterError::custom)?; + fmt.write_raw(attrs_src)?; + } else { + fmt.indented(1, |fmt| fmt.visit_source(attrs_loc))?; + } + } else { + fmt.write_postfix_comments_before(attrs_loc.start())?; + fmt.write_whitespace_separator(multiline)?; + let attributes = + fmt.items_to_chunks_sorted(attrs_end, func.attributes.iter_mut())?; + fmt.indented(1, |fmt| { + fmt.write_chunks_separated(&attributes, "", multiline)?; + Ok(()) + })?; + } + } + + // write returns + if !func.returns.is_empty() { + let returns_start_loc = func.returns.first().unwrap().0; + let returns_loc = returns_start_loc.with_end_from(&func.returns.last().unwrap().0); + if fmt.inline_config.is_disabled(returns_loc) { + fmt.write_whitespace_separator(false)?; + let returns_src = + String::from_utf8(self.source.as_bytes()[returns_loc.range()].to_vec()) + .map_err(FormatterError::custom)?; + fmt.write_raw(format!("returns ({returns_src})"))?; + } else { + let mut returns = fmt.items_to_chunks( + returns_end, + func.returns + .iter_mut() + .filter_map(|(loc, param)| param.as_mut().map(|param| (*loc, param))), + )?; + + // there's an issue with function return value that would lead to indent issues because those can be formatted with line breaks + for function_chunk in + returns.iter_mut().filter(|chunk| chunk.content.starts_with("function(")) + { + // this will bypass the recursive indent that was applied when the function + // content was formatted in the chunk + function_chunk.content = function_chunk + .content + .split('\n') + .map(|s| s.trim_start()) + .collect::>() + .join("\n"); + } + + fmt.write_postfix_comments_before(returns_loc.start())?; + fmt.write_whitespace_separator(multiline)?; + fmt.indented(1, |fmt| { + fmt.surrounded( + SurroundingChunk::new("returns (", Some(returns_loc.start()), None), + SurroundingChunk::new(")", None, returns_end), + |fmt, multiline_hint| { + fmt.write_chunks_separated(&returns, ",", multiline_hint)?; + Ok(()) + }, + )?; + Ok(()) + })?; + } + } + Ok(()) + }; + + let should_multiline = header_multiline && + if params_multiline { + matches!( + self.config.multiline_func_header, + MultilineFuncHeaderStyle::All | MultilineFuncHeaderStyle::AllParams + ) + } else { + matches!( + self.config.multiline_func_header, + MultilineFuncHeaderStyle::AttributesFirst + ) + }; + let attrs_multiline = should_multiline || + !self.try_on_single_line(|fmt| { + write_attributes(fmt, false)?; + if !fmt.will_it_fit(if func.body.is_some() { " {" } else { ";" }) { + bail!(FormatterError::fmt()) + } + Ok(()) + })?; + if attrs_multiline { + write_attributes(self, true)?; + } + Ok(attrs_multiline) + } + + /// Write potentially nested `if statements` + fn write_if_stmt( + &mut self, + loc: Loc, + cond: &mut Expression, + if_branch: &mut Box, + else_branch: &mut Option>, + ) -> Result<(), FormatterError> { + let single_line_stmt_wide = self.context.if_stmt_single_line.unwrap_or_default(); + + visit_source_if_disabled_else!(self, loc.with_end(if_branch.loc().start()), { + self.surrounded( + SurroundingChunk::new("if (", Some(loc.start()), Some(cond.loc().start())), + SurroundingChunk::new(")", None, Some(if_branch.loc().start())), + |fmt, _| { + fmt.write_prefix_comments_before(cond.loc().end())?; + cond.visit(fmt)?; + fmt.write_postfix_comments_before(if_branch.loc().start()) + }, + )?; + }); + + let cond_close_paren_loc = + self.find_next_in_src(cond.loc().end(), ')').unwrap_or_else(|| cond.loc().end()); + let attempt_single_line = single_line_stmt_wide && + self.should_attempt_block_single_line(if_branch.as_mut(), cond_close_paren_loc); + let if_branch_is_single_line = self.visit_stmt_as_block(if_branch, attempt_single_line)?; + if single_line_stmt_wide && !if_branch_is_single_line { + bail!(FormatterError::fmt()) + } + + if let Some(else_branch) = else_branch { + self.write_postfix_comments_before(else_branch.loc().start())?; + if if_branch_is_single_line { + writeln!(self.buf())?; + } + write_chunk!(self, else_branch.loc().start(), "else")?; + if let Statement::If(loc, cond, if_branch, else_branch) = else_branch.as_mut() { + self.visit_if(*loc, cond, if_branch, else_branch, false)?; + } else { + let else_branch_is_single_line = + self.visit_stmt_as_block(else_branch, attempt_single_line)?; + if single_line_stmt_wide && !else_branch_is_single_line { + bail!(FormatterError::fmt()) + } + } + } + Ok(()) + } + + /// Sorts grouped import statement alphabetically. + fn sort_imports(&self, source_unit: &mut SourceUnit) { + // first we need to find the grouped import statements + // A group is defined as a set of import statements that are separated by a blank line + let mut import_groups = Vec::new(); + let mut current_group = Vec::new(); + let mut source_unit_parts = source_unit.0.iter().enumerate().peekable(); + while let Some((i, part)) = source_unit_parts.next() { + if let SourceUnitPart::ImportDirective(_) = part { + current_group.push(i); + let current_loc = part.loc(); + if let Some((_, next_part)) = source_unit_parts.peek() { + let next_loc = next_part.loc(); + // import statements are followed by a new line, so if there are more than one + // we have a group + if self.blank_lines(current_loc.end(), next_loc.start()) > 1 { + import_groups.push(std::mem::take(&mut current_group)); + } + } + } else if !current_group.is_empty() { + import_groups.push(std::mem::take(&mut current_group)); + } + } + + if !current_group.is_empty() { + import_groups.push(current_group); + } + + if import_groups.is_empty() { + // nothing to sort + return + } + + // order all groups alphabetically + for group in &import_groups { + // SAFETY: group is not empty + let first = group[0]; + let last = group.last().copied().expect("group is not empty"); + let import_directives = &mut source_unit.0[first..=last]; + + // sort rename style imports alphabetically based on the actual import and not the + // rename + for source_unit_part in import_directives.iter_mut() { + if let SourceUnitPart::ImportDirective(Import::Rename(_, renames, _)) = + source_unit_part + { + renames.sort_by_cached_key(|(og_ident, _)| og_ident.name.clone()); + } + } + + import_directives.sort_by_cached_key(|item| match item { + SourceUnitPart::ImportDirective(import) => match import { + Import::Plain(path, _) => path.to_string(), + Import::GlobalSymbol(path, _, _) => path.to_string(), + Import::Rename(path, _, _) => path.to_string(), + }, + _ => { + unreachable!("import group contains non-import statement") + } + }); + } + } +} + +// Traverse the Solidity Parse Tree and write to the code formatter +impl Visitor for Formatter<'_, W> { + type Error = FormatterError; + + #[instrument(name = "source", skip(self))] + fn visit_source(&mut self, loc: Loc) -> Result<()> { + let source = String::from_utf8(self.source.as_bytes()[loc.range()].to_vec()) + .map_err(FormatterError::custom)?; + let mut lines = source.splitn(2, '\n'); + + write_chunk!(self, loc.start(), "{}", lines.next().unwrap())?; + if let Some(remainder) = lines.next() { + // Call with `self.write_str` and not `write!`, so we can have `\n` at the beginning + // without triggering an indentation + self.write_raw(format!("\n{remainder}"))?; + } + + let _ = self.comments.remove_all_comments_before(loc.end()); + + Ok(()) + } + + #[instrument(name = "SU", skip_all)] + fn visit_source_unit(&mut self, source_unit: &mut SourceUnit) -> Result<()> { + if self.config.sort_imports { + self.sort_imports(source_unit); + } + // TODO: do we need to put pragma and import directives at the top of the file? + // source_unit.0.sort_by_key(|item| match item { + // SourceUnitPart::PragmaDirective(_, _, _) => 0, + // SourceUnitPart::ImportDirective(_, _) => 1, + // _ => usize::MAX, + // }); + let loc = Loc::File( + source_unit + .loc_opt() + .or_else(|| self.comments.iter().next().map(|comment| comment.loc)) + .map(|loc| loc.file_no()) + .unwrap_or_default(), + 0, + self.source.len(), + ); + + self.write_lined_visitable( + loc, + source_unit.0.iter_mut(), + |last_unit, unit| match last_unit { + SourceUnitPart::PragmaDirective(..) => { + !matches!(unit, SourceUnitPart::PragmaDirective(..)) + } + SourceUnitPart::ImportDirective(_) => { + !matches!(unit, SourceUnitPart::ImportDirective(_)) + } + SourceUnitPart::ErrorDefinition(_) => { + !matches!(unit, SourceUnitPart::ErrorDefinition(_)) + } + SourceUnitPart::Using(_) => !matches!(unit, SourceUnitPart::Using(_)), + SourceUnitPart::VariableDefinition(_) => { + !matches!(unit, SourceUnitPart::VariableDefinition(_)) + } + SourceUnitPart::Annotation(_) => false, + _ => true, + }, + )?; + + // EOF newline + if self.last_char() != Some('\n') { + writeln!(self.buf())?; + } + + Ok(()) + } + + #[instrument(name = "contract", skip_all)] + fn visit_contract(&mut self, contract: &mut ContractDefinition) -> Result<()> { + return_source_if_disabled!(self, contract.loc); + + self.with_contract_context(contract.clone(), |fmt| { + let contract_name = contract.name.safe_unwrap(); + + visit_source_if_disabled_else!( + fmt, + contract.loc.with_end_from( + &contract.base.first().map(|b| b.loc).unwrap_or(contract_name.loc) + ), + { + fmt.grouped(|fmt| { + write_chunk!(fmt, contract.loc.start(), "{}", contract.ty)?; + write_chunk!(fmt, contract_name.loc.end(), "{}", contract_name.name)?; + if !contract.base.is_empty() { + write_chunk!( + fmt, + contract_name.loc.end(), + contract.base.first().unwrap().loc.start(), + "is" + )?; + } + Ok(()) + })?; + } + ); + + if !contract.base.is_empty() { + visit_source_if_disabled_else!( + fmt, + contract + .base + .first() + .unwrap() + .loc + .with_end_from(&contract.base.last().unwrap().loc), + { + fmt.indented(1, |fmt| { + let base_end = contract.parts.first().map(|part| part.loc().start()); + let bases = fmt.items_to_chunks( + base_end, + contract.base.iter_mut().map(|base| (base.loc, base)), + )?; + let multiline = + fmt.are_chunks_separated_multiline("{}", &bases, ",")?; + fmt.write_chunks_separated(&bases, ",", multiline)?; + fmt.write_whitespace_separator(multiline)?; + Ok(()) + })?; + } + ); + } + + write_chunk!(fmt, "{{")?; + + fmt.indented(1, |fmt| { + if let Some(first) = contract.parts.first() { + fmt.write_postfix_comments_before(first.loc().start())?; + fmt.write_whitespace_separator(true)?; + } else { + return Ok(()) + } + + if fmt.config.contract_new_lines { + write_chunk!(fmt, "\n")?; + } + + fmt.write_lined_visitable( + contract.loc, + contract.parts.iter_mut(), + |last_part, part| match last_part { + ContractPart::ErrorDefinition(_) => { + !matches!(part, ContractPart::ErrorDefinition(_)) + } + ContractPart::EventDefinition(_) => { + !matches!(part, ContractPart::EventDefinition(_)) + } + ContractPart::VariableDefinition(_) => { + !matches!(part, ContractPart::VariableDefinition(_)) + } + ContractPart::TypeDefinition(_) => { + !matches!(part, ContractPart::TypeDefinition(_)) + } + ContractPart::EnumDefinition(_) => { + !matches!(part, ContractPart::EnumDefinition(_)) + } + ContractPart::Using(_) => !matches!(part, ContractPart::Using(_)), + ContractPart::FunctionDefinition(last_def) => { + if last_def.is_empty() { + match part { + ContractPart::FunctionDefinition(def) => !def.is_empty(), + _ => true, + } + } else { + true + } + } + ContractPart::Annotation(_) => false, + _ => true, + }, + ) + })?; + + if !contract.parts.is_empty() { + fmt.write_whitespace_separator(true)?; + + if fmt.config.contract_new_lines { + write_chunk!(fmt, "\n")?; + } + } + + write_chunk!(fmt, contract.loc.end(), "}}")?; + + Ok(()) + })?; + + Ok(()) + } + + // Support extension for Solana/Substrate + #[instrument(name = "annotation", skip_all)] + fn visit_annotation(&mut self, annotation: &mut Annotation) -> Result<()> { + return_source_if_disabled!(self, annotation.loc); + let id = self.simulate_to_string(|fmt| annotation.id.visit(fmt))?; + write!(self.buf(), "@{id}")?; + write!(self.buf(), "(")?; + annotation.value.visit(self)?; + write!(self.buf(), ")")?; + Ok(()) + } + + #[instrument(name = "pragma", skip_all)] + fn visit_pragma( + &mut self, + loc: Loc, + ident: &mut Option, + string: &mut Option, + ) -> Result<()> { + let (ident, string) = (ident.safe_unwrap(), string.safe_unwrap()); + return_source_if_disabled!(self, loc, ';'); + + let pragma_descriptor = if ident.name == "solidity" { + // There are some issues with parsing Solidity's versions with crates like `semver`: + // 1. Ranges like `>=0.4.21<0.6.0` or `>=0.4.21 <0.6.0` are not parseable at all. + // 2. Versions like `0.8.10` got transformed into `^0.8.10` which is not the same. + // TODO: semver-solidity crate :D + &string.string + } else { + &string.string + }; + + write_chunk!(self, string.loc.end(), "pragma {} {};", &ident.name, pragma_descriptor)?; + + Ok(()) + } + + #[instrument(name = "import_plain", skip_all)] + fn visit_import_plain(&mut self, loc: Loc, import: &mut ImportPath) -> Result<()> { + return_source_if_disabled!(self, loc, ';'); + + self.grouped(|fmt| { + write_chunk!(fmt, loc.start(), import.loc().start(), "import")?; + fmt.write_quoted_str(import.loc(), None, &import_path_string(import))?; + fmt.write_semicolon()?; + Ok(()) + })?; + Ok(()) + } + + #[instrument(name = "import_global", skip_all)] + fn visit_import_global( + &mut self, + loc: Loc, + global: &mut ImportPath, + alias: &mut Identifier, + ) -> Result<()> { + return_source_if_disabled!(self, loc, ';'); + + self.grouped(|fmt| { + write_chunk!(fmt, loc.start(), global.loc().start(), "import")?; + fmt.write_quoted_str(global.loc(), None, &import_path_string(global))?; + write_chunk!(fmt, loc.start(), alias.loc.start(), "as")?; + alias.visit(fmt)?; + fmt.write_semicolon()?; + Ok(()) + })?; + Ok(()) + } + + #[instrument(name = "import_renames", skip_all)] + fn visit_import_renames( + &mut self, + loc: Loc, + imports: &mut [(Identifier, Option)], + from: &mut ImportPath, + ) -> Result<()> { + return_source_if_disabled!(self, loc, ';'); + + if imports.is_empty() { + self.grouped(|fmt| { + write_chunk!(fmt, loc.start(), "import")?; + fmt.write_empty_brackets()?; + write_chunk!(fmt, loc.start(), from.loc().start(), "from")?; + fmt.write_quoted_str(from.loc(), None, &import_path_string(from))?; + fmt.write_semicolon()?; + Ok(()) + })?; + return Ok(()) + } + + let imports_start = imports.first().unwrap().0.loc.start(); + + write_chunk!(self, loc.start(), imports_start, "import")?; + + self.surrounded( + SurroundingChunk::new("{", Some(imports_start), None), + SurroundingChunk::new("}", None, Some(from.loc().start())), + |fmt, _multiline| { + let mut imports = imports.iter_mut().peekable(); + let mut import_chunks = Vec::new(); + while let Some((ident, alias)) = imports.next() { + import_chunks.push(fmt.chunked( + ident.loc.start(), + imports.peek().map(|(ident, _)| ident.loc.start()), + |fmt| { + fmt.grouped(|fmt| { + ident.visit(fmt)?; + if let Some(alias) = alias { + write_chunk!(fmt, ident.loc.end(), alias.loc.start(), "as")?; + alias.visit(fmt)?; + } + Ok(()) + })?; + Ok(()) + }, + )?); + } + + let multiline = fmt.are_chunks_separated_multiline( + &format!("{{}} }} from \"{}\";", import_path_string(from)), + &import_chunks, + ",", + )?; + fmt.write_chunks_separated(&import_chunks, ",", multiline)?; + Ok(()) + }, + )?; + + self.grouped(|fmt| { + write_chunk!(fmt, imports_start, from.loc().start(), "from")?; + fmt.write_quoted_str(from.loc(), None, &import_path_string(from))?; + fmt.write_semicolon()?; + Ok(()) + })?; + + Ok(()) + } + + #[instrument(name = "enum", skip_all)] + fn visit_enum(&mut self, enumeration: &mut EnumDefinition) -> Result<()> { + return_source_if_disabled!(self, enumeration.loc); + + let enum_name = enumeration.name.safe_unwrap_mut(); + let mut name = + self.visit_to_chunk(enum_name.loc.start(), Some(enum_name.loc.end()), enum_name)?; + name.content = format!("enum {} ", name.content); + if enumeration.values.is_empty() { + self.write_chunk(&name)?; + self.write_empty_brackets()?; + } else { + name.content.push('{'); + self.write_chunk(&name)?; + + self.indented(1, |fmt| { + let values = fmt.items_to_chunks( + Some(enumeration.loc.end()), + enumeration.values.iter_mut().map(|ident| { + let ident = ident.safe_unwrap_mut(); + (ident.loc, ident) + }), + )?; + fmt.write_chunks_separated(&values, ",", true)?; + writeln!(fmt.buf())?; + Ok(()) + })?; + write_chunk!(self, "}}")?; + } + + Ok(()) + } + + #[instrument(name = "assembly", skip_all)] + fn visit_assembly( + &mut self, + loc: Loc, + dialect: &mut Option, + block: &mut YulBlock, + flags: &mut Option>, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + + write_chunk!(self, loc.start(), "assembly")?; + if let Some(StringLiteral { loc, string, .. }) = dialect { + write_chunk!(self, loc.start(), loc.end(), "\"{string}\"")?; + } + if let Some(flags) = flags { + if !flags.is_empty() { + let loc_start = flags.first().unwrap().loc.start(); + self.surrounded( + SurroundingChunk::new("(", Some(loc_start), None), + SurroundingChunk::new(")", None, Some(block.loc.start())), + |fmt, _| { + let mut flags = flags.iter_mut().peekable(); + let mut chunks = vec![]; + while let Some(flag) = flags.next() { + let next_byte_offset = + flags.peek().map(|next_flag| next_flag.loc.start()); + chunks.push(fmt.chunked( + flag.loc.start(), + next_byte_offset, + |fmt| { + write!(fmt.buf(), "\"{}\"", flag.string)?; + Ok(()) + }, + )?); + } + fmt.write_chunks_separated(&chunks, ",", false)?; + Ok(()) + }, + )?; + } + } + + block.visit(self) + } + + #[instrument(name = "block", skip_all)] + fn visit_block( + &mut self, + loc: Loc, + unchecked: bool, + statements: &mut Vec, + ) -> Result<()> { + return_source_if_disabled!(self, loc); + if unchecked { + write_chunk!(self, loc.start(), "unchecked ")?; + } + + self.visit_block(loc, statements, false, false)?; + Ok(()) + } + + #[instrument(name = "args", skip_all)] + fn visit_args(&mut self, loc: Loc, args: &mut Vec) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + + write!(self.buf(), "{{")?; + + let mut args_iter = args.iter_mut().peekable(); + let mut chunks = Vec::new(); + while let Some(NamedArgument { loc: arg_loc, name, expr }) = args_iter.next() { + let next_byte_offset = args_iter + .peek() + .map(|NamedArgument { loc: arg_loc, .. }| arg_loc.start()) + .unwrap_or_else(|| loc.end()); + chunks.push(self.chunked(arg_loc.start(), Some(next_byte_offset), |fmt| { + fmt.grouped(|fmt| { + write_chunk!(fmt, name.loc.start(), "{}: ", name.name)?; + expr.visit(fmt) + })?; + Ok(()) + })?); + } + + if let Some(first) = chunks.first_mut() { + if first.prefixes.is_empty() && + first.postfixes_before.is_empty() && + !self.config.bracket_spacing + { + first.needs_space = Some(false); + } + } + let multiline = self.are_chunks_separated_multiline("{}}", &chunks, ",")?; + self.indented_if(multiline, 1, |fmt| fmt.write_chunks_separated(&chunks, ",", multiline))?; + + let prefix = if multiline && !self.is_beginning_of_line() { + "\n" + } else if self.config.bracket_spacing { + " " + } else { + "" + }; + let closing_bracket = format!("{prefix}{}", "}"); + if let Some(arg) = args.last() { + write_chunk!(self, arg.loc.end(), "{closing_bracket}")?; + } else { + write_chunk!(self, "{closing_bracket}")?; + } + + Ok(()) + } + + #[instrument(name = "expr", skip_all)] + fn visit_expr(&mut self, loc: Loc, expr: &mut Expression) -> Result<()> { + return_source_if_disabled!(self, loc); + + match expr { + Expression::Type(loc, typ) => match typ { + Type::Address => write_chunk!(self, loc.start(), "address")?, + Type::AddressPayable => write_chunk!(self, loc.start(), "address payable")?, + Type::Payable => write_chunk!(self, loc.start(), "payable")?, + Type::Bool => write_chunk!(self, loc.start(), "bool")?, + Type::String => write_chunk!(self, loc.start(), "string")?, + Type::Bytes(n) => write_chunk!(self, loc.start(), "bytes{}", n)?, + Type::Rational => write_chunk!(self, loc.start(), "rational")?, + Type::DynamicBytes => write_chunk!(self, loc.start(), "bytes")?, + Type::Int(ref n) | Type::Uint(ref n) => { + let int = if matches!(typ, Type::Int(_)) { "int" } else { "uint" }; + match n { + 256 => match self.config.int_types { + IntTypes::Long => write_chunk!(self, loc.start(), "{int}{n}")?, + IntTypes::Short => write_chunk!(self, loc.start(), "{int}")?, + IntTypes::Preserve => self.visit_source(*loc)?, + }, + _ => write_chunk!(self, loc.start(), "{int}{n}")?, + } + } + Type::Mapping { loc, key, key_name, value, value_name } => { + let arrow_loc = self.find_next_str_in_src(loc.start(), "=>"); + let close_paren_loc = + self.find_next_in_src(value.loc().end(), ')').unwrap_or(loc.end()); + let first = SurroundingChunk::new( + "mapping(", + Some(loc.start()), + Some(key.loc().start()), + ); + let last = SurroundingChunk::new(")", Some(close_paren_loc), Some(loc.end())) + .non_spaced(); + self.surrounded(first, last, |fmt, multiline| { + fmt.grouped(|fmt| { + key.visit(fmt)?; + + if let Some(name) = key_name { + let end_loc = arrow_loc.unwrap_or(value.loc().start()); + write_chunk!(fmt, name.loc.start(), end_loc, " {}", name)?; + } else if let Some(arrow_loc) = arrow_loc { + fmt.write_postfix_comments_before(arrow_loc)?; + } + + let mut write_arrow_and_value = |fmt: &mut Self| { + write!(fmt.buf(), "=> ")?; + value.visit(fmt)?; + if let Some(name) = value_name { + write_chunk!(fmt, name.loc.start(), " {}", name)?; + } + Ok(()) + }; + + let rest_str = fmt.simulate_to_string(&mut write_arrow_and_value)?; + let multiline = multiline && !fmt.will_it_fit(rest_str); + fmt.write_whitespace_separator(multiline)?; + + write_arrow_and_value(fmt)?; + + fmt.write_postfix_comments_before(close_paren_loc)?; + fmt.write_prefix_comments_before(close_paren_loc) + })?; + Ok(()) + })?; + } + Type::Function { .. } => self.visit_source(*loc)?, + }, + Expression::BoolLiteral(loc, val) => { + write_chunk!(self, loc.start(), loc.end(), "{val}")?; + } + Expression::NumberLiteral(loc, val, exp, unit) => { + self.write_num_literal(*loc, val, None, exp, unit)?; + } + Expression::HexNumberLiteral(loc, val, unit) => { + // ref: https://docs.soliditylang.org/en/latest/types.html?highlight=address%20literal#address-literals + let val = if val.len() == 42 { + Address::from_str(val).expect("").to_string() + } else { + val.to_owned() + }; + write_chunk!(self, loc.start(), loc.end(), "{val}")?; + self.write_unit(unit)?; + } + Expression::RationalNumberLiteral(loc, val, fraction, exp, unit) => { + self.write_num_literal(*loc, val, Some(fraction), exp, unit)?; + } + Expression::StringLiteral(vals) => { + for StringLiteral { loc, string, unicode } in vals { + let prefix = if *unicode { Some("unicode") } else { None }; + self.write_quoted_str(*loc, prefix, string)?; + } + } + Expression::HexLiteral(vals) => { + for val in vals { + self.write_hex_literal(val)?; + } + } + Expression::AddressLiteral(loc, val) => { + // support of solana/substrate address literals + self.write_quoted_str(*loc, Some("address"), val)?; + } + Expression::Parenthesis(loc, expr) => { + self.surrounded( + SurroundingChunk::new("(", Some(loc.start()), None), + SurroundingChunk::new(")", None, Some(loc.end())), + |fmt, _| expr.visit(fmt), + )?; + } + Expression::ArraySubscript(_, ty_exp, index_expr) => { + ty_exp.visit(self)?; + write!(self.buf(), "[")?; + index_expr.as_mut().map(|index| index.visit(self)).transpose()?; + write!(self.buf(), "]")?; + } + Expression::ArraySlice(loc, expr, start, end) => { + expr.visit(self)?; + write!(self.buf(), "[")?; + let mut write_slice = |fmt: &mut Self, multiline| -> Result<()> { + if multiline { + fmt.write_whitespace_separator(true)?; + } + fmt.grouped(|fmt| { + start.as_mut().map(|start| start.visit(fmt)).transpose()?; + write!(fmt.buf(), ":")?; + if let Some(end) = end { + let mut chunk = + fmt.chunked(end.loc().start(), Some(loc.end()), |fmt| { + end.visit(fmt) + })?; + if chunk.prefixes.is_empty() && + chunk.postfixes_before.is_empty() && + (start.is_none() || fmt.will_it_fit(&chunk.content)) + { + chunk.needs_space = Some(false); + } + fmt.write_chunk(&chunk)?; + } + Ok(()) + })?; + if multiline { + fmt.write_whitespace_separator(true)?; + } + Ok(()) + }; + + if !self.try_on_single_line(|fmt| write_slice(fmt, false))? { + self.indented(1, |fmt| write_slice(fmt, true))?; + } + + write!(self.buf(), "]")?; + } + Expression::ArrayLiteral(loc, exprs) => { + write_chunk!(self, loc.start(), "[")?; + let chunks = self.items_to_chunks( + Some(loc.end()), + exprs.iter_mut().map(|expr| (expr.loc(), expr)), + )?; + let multiline = self.are_chunks_separated_multiline("{}]", &chunks, ",")?; + self.indented_if(multiline, 1, |fmt| { + fmt.write_chunks_separated(&chunks, ",", multiline)?; + if multiline { + fmt.write_postfix_comments_before(loc.end())?; + fmt.write_prefix_comments_before(loc.end())?; + fmt.write_whitespace_separator(true)?; + } + Ok(()) + })?; + write_chunk!(self, loc.end(), "]")?; + } + Expression::PreIncrement(..) | + Expression::PostIncrement(..) | + Expression::PreDecrement(..) | + Expression::PostDecrement(..) | + Expression::Not(..) | + Expression::UnaryPlus(..) | + Expression::Add(..) | + Expression::Negate(..) | + Expression::Subtract(..) | + Expression::Power(..) | + Expression::Multiply(..) | + Expression::Divide(..) | + Expression::Modulo(..) | + Expression::ShiftLeft(..) | + Expression::ShiftRight(..) | + Expression::BitwiseNot(..) | + Expression::BitwiseAnd(..) | + Expression::BitwiseXor(..) | + Expression::BitwiseOr(..) | + Expression::Less(..) | + Expression::More(..) | + Expression::LessEqual(..) | + Expression::MoreEqual(..) | + Expression::And(..) | + Expression::Or(..) | + Expression::Equal(..) | + Expression::NotEqual(..) => { + let spaced = expr.has_space_around(); + let op = expr.operator().unwrap(); + + match expr.components_mut() { + (Some(left), Some(right)) => { + left.visit(self)?; + + let right_chunk = + self.chunked(right.loc().start(), Some(loc.end()), |fmt| { + write_chunk!(fmt, right.loc().start(), "{op}")?; + right.visit(fmt)?; + Ok(()) + })?; + + self.grouped(|fmt| fmt.write_chunk(&right_chunk))?; + } + (Some(left), None) => { + left.visit(self)?; + write_chunk_spaced!(self, loc.end(), Some(spaced), "{op}")?; + } + (None, Some(right)) => { + write_chunk!(self, right.loc().start(), "{op}")?; + let mut right_chunk = + self.visit_to_chunk(right.loc().end(), Some(loc.end()), right)?; + right_chunk.needs_space = Some(spaced); + self.write_chunk(&right_chunk)?; + } + (None, None) => {} + } + } + Expression::Assign(..) | + Expression::AssignOr(..) | + Expression::AssignAnd(..) | + Expression::AssignXor(..) | + Expression::AssignShiftLeft(..) | + Expression::AssignShiftRight(..) | + Expression::AssignAdd(..) | + Expression::AssignSubtract(..) | + Expression::AssignMultiply(..) | + Expression::AssignDivide(..) | + Expression::AssignModulo(..) => { + let op = expr.operator().unwrap(); + let (left, right) = expr.components_mut(); + let (left, right) = (left.unwrap(), right.unwrap()); + + left.visit(self)?; + write_chunk!(self, "{op}")?; + self.visit_assignment(right)?; + } + Expression::ConditionalOperator(loc, cond, first_expr, second_expr) => { + cond.visit(self)?; + + let first_expr = self.chunked( + first_expr.loc().start(), + Some(second_expr.loc().start()), + |fmt| { + write_chunk!(fmt, "?")?; + first_expr.visit(fmt) + }, + )?; + let second_expr = + self.chunked(second_expr.loc().start(), Some(loc.end()), |fmt| { + write_chunk!(fmt, ":")?; + second_expr.visit(fmt) + })?; + + let chunks = vec![first_expr, second_expr]; + if !self.try_on_single_line(|fmt| fmt.write_chunks_separated(&chunks, "", false))? { + self.grouped(|fmt| fmt.write_chunks_separated(&chunks, "", true))?; + } + } + Expression::Variable(ident) => { + write_chunk!(self, loc.end(), "{}", ident.name)?; + } + Expression::MemberAccess(_, expr, ident) => { + self.visit_member_access(expr, ident, |fmt, expr| match expr.as_mut() { + Expression::MemberAccess(_, inner_expr, inner_ident) => { + Ok(Some((inner_expr, inner_ident))) + } + expr => { + expr.visit(fmt)?; + Ok(None) + } + })?; + } + Expression::List(loc, items) => { + self.surrounded( + SurroundingChunk::new( + "(", + Some(loc.start()), + items.first().map(|item| item.0.start()), + ), + SurroundingChunk::new(")", None, Some(loc.end())), + |fmt, _| { + let items = fmt.items_to_chunks( + Some(loc.end()), + items.iter_mut().map(|(loc, item)| (*loc, item)), + )?; + let write_items = |fmt: &mut Self, multiline| { + fmt.write_chunks_separated(&items, ",", multiline) + }; + if !fmt.try_on_single_line(|fmt| write_items(fmt, false))? { + write_items(fmt, true)?; + } + Ok(()) + }, + )?; + } + Expression::FunctionCall(loc, expr, exprs) => { + self.visit_expr(expr.loc(), expr)?; + self.visit_list("", exprs, Some(expr.loc().end()), Some(loc.end()), true)?; + } + Expression::NamedFunctionCall(loc, expr, args) => { + self.visit_expr(expr.loc(), expr)?; + write!(self.buf(), "(")?; + self.visit_args(*loc, args)?; + write!(self.buf(), ")")?; + } + Expression::FunctionCallBlock(_, expr, stmt) => { + expr.visit(self)?; + stmt.visit(self)?; + } + Expression::New(_, expr) => { + write_chunk!(self, "new ")?; + self.visit_expr(expr.loc(), expr)?; + } + _ => self.visit_source(loc)?, + }; + + Ok(()) + } + + #[instrument(name = "ident", skip_all)] + fn visit_ident(&mut self, loc: Loc, ident: &mut Identifier) -> Result<()> { + return_source_if_disabled!(self, loc); + write_chunk!(self, loc.end(), "{}", ident.name)?; + Ok(()) + } + + #[instrument(name = "ident_path", skip_all)] + fn visit_ident_path(&mut self, idents: &mut IdentifierPath) -> Result<(), Self::Error> { + if idents.identifiers.is_empty() { + return Ok(()) + } + return_source_if_disabled!(self, idents.loc); + + idents.identifiers.iter_mut().skip(1).for_each(|chunk| { + if !chunk.name.starts_with('.') { + chunk.name.insert(0, '.') + } + }); + let chunks = self.items_to_chunks( + Some(idents.loc.end()), + idents.identifiers.iter_mut().map(|ident| (ident.loc, ident)), + )?; + self.grouped(|fmt| { + let multiline = fmt.are_chunks_separated_multiline("{}", &chunks, "")?; + fmt.write_chunks_separated(&chunks, "", multiline) + })?; + Ok(()) + } + + #[instrument(name = "emit", skip_all)] + fn visit_emit(&mut self, loc: Loc, event: &mut Expression) -> Result<()> { + return_source_if_disabled!(self, loc); + write_chunk!(self, loc.start(), "emit")?; + event.visit(self)?; + self.write_semicolon()?; + Ok(()) + } + + #[instrument(name = "var_definition", skip_all)] + fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<()> { + return_source_if_disabled!(self, var.loc, ';'); + + var.ty.visit(self)?; + + let multiline = self.grouped(|fmt| { + let var_name = var.name.safe_unwrap_mut(); + let name_start = var_name.loc.start(); + + let attrs = fmt.items_to_chunks_sorted(Some(name_start), var.attrs.iter_mut())?; + if !fmt.try_on_single_line(|fmt| fmt.write_chunks_separated(&attrs, "", false))? { + fmt.write_chunks_separated(&attrs, "", true)?; + } + + let mut name = fmt.visit_to_chunk(name_start, Some(var_name.loc.end()), var_name)?; + if var.initializer.is_some() { + name.content.push_str(" ="); + } + fmt.write_chunk(&name)?; + + Ok(()) + })?; + + var.initializer + .as_mut() + .map(|init| self.indented_if(multiline, 1, |fmt| fmt.visit_assignment(init))) + .transpose()?; + + self.write_semicolon()?; + + Ok(()) + } + + #[instrument(name = "var_definition_stmt", skip_all)] + fn visit_var_definition_stmt( + &mut self, + loc: Loc, + declaration: &mut VariableDeclaration, + expr: &mut Option, + ) -> Result<()> { + return_source_if_disabled!(self, loc, ';'); + + let declaration = self + .chunked(declaration.loc.start(), None, |fmt| fmt.visit_var_declaration(declaration))?; + let multiline = declaration.content.contains('\n'); + self.write_chunk(&declaration)?; + + if let Some(expr) = expr { + write!(self.buf(), " =")?; + self.indented_if(multiline, 1, |fmt| fmt.visit_assignment(expr))?; + } + + self.write_semicolon() + } + + #[instrument(name = "var_declaration", skip_all)] + fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<()> { + return_source_if_disabled!(self, var.loc); + self.grouped(|fmt| { + var.ty.visit(fmt)?; + if let Some(storage) = &var.storage { + write_chunk!(fmt, storage.loc().end(), "{storage}")?; + } + let var_name = var.name.safe_unwrap(); + write_chunk!(fmt, var_name.loc.end(), "{var_name}") + })?; + Ok(()) + } + + #[instrument(name = "return", skip_all)] + fn visit_return(&mut self, loc: Loc, expr: &mut Option) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc, ';'); + + self.write_postfix_comments_before(loc.start())?; + self.write_prefix_comments_before(loc.start())?; + + if expr.is_none() { + write_chunk!(self, loc.end(), "return;")?; + return Ok(()) + } + + let expr = expr.as_mut().unwrap(); + let expr_loc_start = expr.loc().start(); + let write_return = |fmt: &mut Self| -> Result<()> { + write_chunk!(fmt, loc.start(), "return")?; + fmt.write_postfix_comments_before(expr_loc_start)?; + Ok(()) + }; + + let mut write_return_with_expr = |fmt: &mut Self| -> Result<()> { + let fits_on_single = fmt.try_on_single_line(|fmt| { + write_return(fmt)?; + expr.visit(fmt) + })?; + if fits_on_single { + return Ok(()) + } + + let mut fit_on_next_line = false; + let tx = fmt.transact(|fmt| { + fmt.grouped(|fmt| { + write_return(fmt)?; + if !fmt.is_beginning_of_line() { + fmt.write_whitespace_separator(true)?; + } + fit_on_next_line = fmt.try_on_single_line(|fmt| expr.visit(fmt))?; + Ok(()) + })?; + Ok(()) + })?; + if fit_on_next_line { + tx.commit()?; + return Ok(()) + } + + write_return(fmt)?; + expr.visit(fmt)?; + Ok(()) + }; + + write_return_with_expr(self)?; + write_chunk!(self, loc.end(), ";")?; + Ok(()) + } + + #[instrument(name = "revert", skip_all)] + fn visit_revert( + &mut self, + loc: Loc, + error: &mut Option, + args: &mut Vec, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc, ';'); + write_chunk!(self, loc.start(), "revert")?; + if let Some(error) = error { + error.visit(self)?; + } + self.visit_list("", args, None, Some(loc.end()), true)?; + self.write_semicolon()?; + + Ok(()) + } + + #[instrument(name = "revert_named_args", skip_all)] + fn visit_revert_named_args( + &mut self, + loc: Loc, + error: &mut Option, + args: &mut Vec, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc, ';'); + + write_chunk!(self, loc.start(), "revert")?; + let mut error_indented = false; + if let Some(error) = error { + if !self.try_on_single_line(|fmt| error.visit(fmt))? { + error.visit(self)?; + error_indented = true; + } + } + + if args.is_empty() { + write!(self.buf(), "({{}});")?; + return Ok(()) + } + + write!(self.buf(), "(")?; + self.indented_if(error_indented, 1, |fmt| fmt.visit_args(loc, args))?; + write!(self.buf(), ")")?; + self.write_semicolon()?; + + Ok(()) + } + + #[instrument(name = "break", skip_all)] + fn visit_break(&mut self, loc: Loc, semicolon: bool) -> Result<()> { + if semicolon { + return_source_if_disabled!(self, loc, ';'); + } else { + return_source_if_disabled!(self, loc); + } + write_chunk!(self, loc.start(), loc.end(), "break{}", if semicolon { ";" } else { "" }) + } + + #[instrument(name = "continue", skip_all)] + fn visit_continue(&mut self, loc: Loc, semicolon: bool) -> Result<()> { + if semicolon { + return_source_if_disabled!(self, loc, ';'); + } else { + return_source_if_disabled!(self, loc); + } + write_chunk!(self, loc.start(), loc.end(), "continue{}", if semicolon { ";" } else { "" }) + } + + #[instrument(name = "try", skip_all)] + fn visit_try( + &mut self, + loc: Loc, + expr: &mut Expression, + returns: &mut Option<(Vec<(Loc, Option)>, Box)>, + clauses: &mut Vec, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + + let try_next_byte = clauses.first().map(|c| match c { + CatchClause::Simple(loc, ..) => loc.start(), + CatchClause::Named(loc, ..) => loc.start(), + }); + let try_chunk = self.chunked(loc.start(), try_next_byte, |fmt| { + write_chunk!(fmt, loc.start(), expr.loc().start(), "try")?; + expr.visit(fmt)?; + if let Some((params, stmt)) = returns { + let mut params = + params.iter_mut().filter(|(_, param)| param.is_some()).collect::>(); + let byte_offset = params.first().map_or(stmt.loc().start(), |p| p.0.start()); + fmt.surrounded( + SurroundingChunk::new("returns (", Some(byte_offset), None), + SurroundingChunk::new(")", None, params.last().map(|p| p.0.end())), + |fmt, _| { + let chunks = fmt.items_to_chunks( + Some(stmt.loc().start()), + params.iter_mut().map(|(loc, ref mut ident)| (*loc, ident)), + )?; + let multiline = fmt.are_chunks_separated_multiline("{})", &chunks, ",")?; + fmt.write_chunks_separated(&chunks, ",", multiline)?; + Ok(()) + }, + )?; + stmt.visit(fmt)?; + } + Ok(()) + })?; + + let mut chunks = vec![try_chunk]; + for clause in clauses { + let (loc, ident, mut param, stmt) = match clause { + CatchClause::Simple(loc, param, stmt) => (loc, None, param.as_mut(), stmt), + CatchClause::Named(loc, ident, param, stmt) => { + (loc, Some(ident), Some(param), stmt) + } + }; + + let chunk = self.chunked(loc.start(), Some(stmt.loc().start()), |fmt| { + write_chunk!(fmt, "catch")?; + if let Some(ident) = ident.as_ref() { + fmt.write_postfix_comments_before( + param.as_ref().map(|p| p.loc.start()).unwrap_or_else(|| ident.loc.end()), + )?; + write_chunk!(fmt, ident.loc.start(), "{}", ident.name)?; + } + if let Some(param) = param.as_mut() { + write_chunk_spaced!(fmt, param.loc.start(), Some(ident.is_none()), "(")?; + fmt.surrounded( + SurroundingChunk::new("", Some(param.loc.start()), None), + SurroundingChunk::new(")", None, Some(stmt.loc().start())), + |fmt, _| param.visit(fmt), + )?; + } + + stmt.visit(fmt)?; + Ok(()) + })?; + + chunks.push(chunk); + } + + let multiline = self.are_chunks_separated_multiline("{}", &chunks, "")?; + if !multiline { + self.write_chunks_separated(&chunks, "", false)?; + return Ok(()) + } + + let mut chunks = chunks.iter_mut().peekable(); + let mut prev_multiline = false; + + // write try chunk first + if let Some(chunk) = chunks.next() { + let chunk_str = self.simulate_to_string(|fmt| fmt.write_chunk(chunk))?; + write!(self.buf(), "{chunk_str}")?; + prev_multiline = chunk_str.contains('\n'); + } + + while let Some(chunk) = chunks.next() { + let chunk_str = self.simulate_to_string(|fmt| fmt.write_chunk(chunk))?; + let multiline = chunk_str.contains('\n'); + self.indented_if(!multiline, 1, |fmt| { + chunk.needs_space = Some(false); + let on_same_line = prev_multiline && (multiline || chunks.peek().is_none()); + let prefix = if fmt.is_beginning_of_line() { + "" + } else if on_same_line { + " " + } else { + "\n" + }; + let chunk_str = format!("{prefix}{chunk_str}"); + write!(fmt.buf(), "{chunk_str}")?; + Ok(()) + })?; + prev_multiline = multiline; + } + Ok(()) + } + + #[instrument(name = "if", skip_all)] + fn visit_if( + &mut self, + loc: Loc, + cond: &mut Expression, + if_branch: &mut Box, + else_branch: &mut Option>, + is_first_stmt: bool, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + + if !is_first_stmt { + self.write_if_stmt(loc, cond, if_branch, else_branch)?; + return Ok(()) + } + + self.context.if_stmt_single_line = Some(true); + let mut stmt_fits_on_single = false; + let tx = self.transact(|fmt| { + stmt_fits_on_single = match fmt.write_if_stmt(loc, cond, if_branch, else_branch) { + Ok(()) => true, + Err(FormatterError::Fmt(_)) => false, + Err(err) => bail!(err), + }; + Ok(()) + })?; + + if stmt_fits_on_single { + tx.commit()?; + } else { + self.context.if_stmt_single_line = Some(false); + self.write_if_stmt(loc, cond, if_branch, else_branch)?; + } + self.context.if_stmt_single_line = None; + + Ok(()) + } + + #[instrument(name = "do_while", skip_all)] + fn visit_do_while( + &mut self, + loc: Loc, + body: &mut Statement, + cond: &mut Expression, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc, ';'); + write_chunk!(self, loc.start(), "do ")?; + self.visit_stmt_as_block(body, false)?; + visit_source_if_disabled_else!(self, loc.with_start(body.loc().end()), { + self.surrounded( + SurroundingChunk::new("while (", Some(cond.loc().start()), None), + SurroundingChunk::new(");", None, Some(loc.end())), + |fmt, _| cond.visit(fmt), + )?; + }); + Ok(()) + } + + #[instrument(name = "while", skip_all)] + fn visit_while( + &mut self, + loc: Loc, + cond: &mut Expression, + body: &mut Statement, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + self.surrounded( + SurroundingChunk::new("while (", Some(loc.start()), None), + SurroundingChunk::new(")", None, Some(cond.loc().end())), + |fmt, _| { + cond.visit(fmt)?; + fmt.write_postfix_comments_before(body.loc().start()) + }, + )?; + + let cond_close_paren_loc = + self.find_next_in_src(cond.loc().end(), ')').unwrap_or_else(|| cond.loc().end()); + let attempt_single_line = self.should_attempt_block_single_line(body, cond_close_paren_loc); + self.visit_stmt_as_block(body, attempt_single_line)?; + Ok(()) + } + + #[instrument(name = "for", skip_all)] + fn visit_for( + &mut self, + loc: Loc, + init: &mut Option>, + cond: &mut Option>, + update: &mut Option>, + body: &mut Option>, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + + let next_byte_end = update.as_ref().map(|u| u.loc().end()); + self.surrounded( + SurroundingChunk::new("for (", Some(loc.start()), None), + SurroundingChunk::new(")", None, next_byte_end), + |fmt, _| { + let mut write_for_loop_header = |fmt: &mut Self, multiline: bool| -> Result<()> { + match init { + Some(stmt) => stmt.visit(fmt), + None => fmt.write_semicolon(), + }?; + if multiline { + fmt.write_whitespace_separator(true)?; + } + + cond.visit(fmt)?; + fmt.write_semicolon()?; + if multiline { + fmt.write_whitespace_separator(true)?; + } + + match update { + Some(expr) => expr.visit(fmt), + None => Ok(()), + } + }; + let multiline = !fmt.try_on_single_line(|fmt| write_for_loop_header(fmt, false))?; + if multiline { + write_for_loop_header(fmt, true)?; + } + Ok(()) + }, + )?; + match body { + Some(body) => { + self.visit_stmt_as_block(body, false)?; + } + None => { + self.write_empty_brackets()?; + } + }; + Ok(()) + } + + #[instrument(name = "function", skip_all)] + fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<()> { + if func.body.is_some() { + return_source_if_disabled!(self, func.loc()); + } else { + return_source_if_disabled!(self, func.loc(), ';'); + } + + self.with_function_context(func.clone(), |fmt| { + fmt.write_postfix_comments_before(func.loc.start())?; + fmt.write_prefix_comments_before(func.loc.start())?; + + let body_loc = func.body.as_ref().map(CodeLocation::loc); + let mut attrs_multiline = false; + let fits_on_single = fmt.try_on_single_line(|fmt| { + fmt.write_function_header(func, body_loc, false)?; + Ok(()) + })?; + if !fits_on_single { + attrs_multiline = fmt.write_function_header(func, body_loc, true)?; + } + + // write function body + match &mut func.body { + Some(body) => { + let body_loc = body.loc(); + // Handle case where block / statements starts on disabled line. + if fmt.inline_config.is_disabled(body_loc.with_end(body_loc.start())) { + match body { + Statement::Block { statements, .. } if !statements.is_empty() => { + fmt.write_whitespace_separator(false)?; + fmt.visit_block(body_loc, statements, false, false)?; + return Ok(()) + } + _ => { + // Attrs should be written on same line if first line is disabled + // and there's no statement. + attrs_multiline = false + } + } + } + + let byte_offset = body_loc.start(); + let body = fmt.visit_to_chunk(byte_offset, Some(body_loc.end()), body)?; + fmt.write_whitespace_separator( + attrs_multiline && !(func.attributes.is_empty() && func.returns.is_empty()), + )?; + fmt.write_chunk(&body)?; + } + None => fmt.write_semicolon()?, + } + Ok(()) + })?; + + Ok(()) + } + + #[instrument(name = "function_attribute", skip_all)] + fn visit_function_attribute(&mut self, attribute: &mut FunctionAttribute) -> Result<()> { + return_source_if_disabled!(self, attribute.loc()); + + match attribute { + FunctionAttribute::Mutability(mutability) => { + write_chunk!(self, mutability.loc().end(), "{mutability}")? + } + FunctionAttribute::Visibility(visibility) => { + // Visibility will always have a location in a Function attribute + write_chunk!(self, visibility.loc_opt().unwrap().end(), "{visibility}")? + } + FunctionAttribute::Virtual(loc) => write_chunk!(self, loc.end(), "virtual")?, + FunctionAttribute::Immutable(loc) => write_chunk!(self, loc.end(), "immutable")?, + FunctionAttribute::Override(loc, args) => { + write_chunk!(self, loc.start(), "override")?; + if !args.is_empty() && self.config.override_spacing { + self.write_whitespace_separator(false)?; + } + self.visit_list("", args, None, Some(loc.end()), false)? + } + FunctionAttribute::BaseOrModifier(loc, base) => { + // here we need to find out if this attribute belongs to the constructor because the + // modifier need to include the trailing parenthesis + // This is very ambiguous because the modifier can either by an inherited contract + // or a modifier here: e.g.: This is valid constructor: + // `constructor() public Ownable() OnlyOwner {}` + let is_constructor = self.context.is_constructor_function(); + // we can't make any decisions here regarding trailing `()` because we'd need to + // find out if the `base` is a solidity modifier or an + // interface/contract therefore we we its raw content. + + // we can however check if the contract `is` the `base`, this however also does + // not cover all cases + let is_contract_base = self.context.contract.as_ref().is_some_and(|contract| { + contract.base.iter().any(|contract_base| { + contract_base + .name + .identifiers + .iter() + .zip(&base.name.identifiers) + .all(|(l, r)| l.name == r.name) + }) + }); + + if is_contract_base { + base.visit(self)?; + } else if is_constructor { + // This is ambiguous because the modifier can either by an inherited + // contract modifiers with empty parenthesis are + // valid, but not required so we make the assumption + // here that modifiers are lowercase + let mut base_or_modifier = + self.visit_to_chunk(loc.start(), Some(loc.end()), base)?; + let is_lowercase = + base_or_modifier.content.chars().next().is_some_and(|c| c.is_lowercase()); + if is_lowercase && base_or_modifier.content.ends_with("()") { + base_or_modifier.content.truncate(base_or_modifier.content.len() - 2); + } + + self.write_chunk(&base_or_modifier)?; + } else { + let mut base_or_modifier = + self.visit_to_chunk(loc.start(), Some(loc.end()), base)?; + if base_or_modifier.content.ends_with("()") { + base_or_modifier.content.truncate(base_or_modifier.content.len() - 2); + } + self.write_chunk(&base_or_modifier)?; + } + } + FunctionAttribute::Error(loc) => self.visit_parser_error(*loc)?, + }; + + Ok(()) + } + + #[instrument(name = "var_attribute", skip_all)] + fn visit_var_attribute(&mut self, attribute: &mut VariableAttribute) -> Result<()> { + return_source_if_disabled!(self, attribute.loc()); + + let token = match attribute { + VariableAttribute::Visibility(visibility) => Some(visibility.to_string()), + VariableAttribute::Constant(_) => Some("constant".to_string()), + VariableAttribute::Immutable(_) => Some("immutable".to_string()), + VariableAttribute::Override(loc, idents) => { + write_chunk!(self, loc.start(), "override")?; + if !idents.is_empty() && self.config.override_spacing { + self.write_whitespace_separator(false)?; + } + self.visit_list("", idents, Some(loc.start()), Some(loc.end()), false)?; + None + } + }; + if let Some(token) = token { + let loc = attribute.loc(); + write_chunk!(self, loc.start(), loc.end(), "{}", token)?; + } + Ok(()) + } + + #[instrument(name = "base", skip_all)] + fn visit_base(&mut self, base: &mut Base) -> Result<()> { + return_source_if_disabled!(self, base.loc); + + let name_loc = &base.name.loc; + let mut name = self.chunked(name_loc.start(), Some(name_loc.end()), |fmt| { + fmt.visit_ident_path(&mut base.name)?; + Ok(()) + })?; + + if base.args.is_none() || base.args.as_ref().unwrap().is_empty() { + // This is ambiguous because the modifier can either by an inherited contract or a + // modifier + if self.context.function.is_some() { + name.content.push_str("()"); + } + self.write_chunk(&name)?; + return Ok(()) + } + + let args = base.args.as_mut().unwrap(); + let args_start = CodeLocation::loc(args.first().unwrap()).start(); + + name.content.push('('); + let formatted_name = self.chunk_to_string(&name)?; + + let multiline = !self.will_it_fit(&formatted_name); + + self.surrounded( + SurroundingChunk::new(&formatted_name, Some(args_start), None), + SurroundingChunk::new(")", None, Some(base.loc.end())), + |fmt, multiline_hint| { + let args = fmt.items_to_chunks( + Some(base.loc.end()), + args.iter_mut().map(|arg| (arg.loc(), arg)), + )?; + let multiline = multiline || + multiline_hint || + fmt.are_chunks_separated_multiline("{}", &args, ",")?; + fmt.write_chunks_separated(&args, ",", multiline)?; + Ok(()) + }, + )?; + + Ok(()) + } + + #[instrument(name = "parameter", skip_all)] + fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<()> { + return_source_if_disabled!(self, parameter.loc); + self.grouped(|fmt| { + parameter.ty.visit(fmt)?; + if let Some(storage) = ¶meter.storage { + write_chunk!(fmt, storage.loc().end(), "{storage}")?; + } + if let Some(name) = ¶meter.name { + write_chunk!(fmt, parameter.loc.end(), "{}", name.name)?; + } + Ok(()) + })?; + Ok(()) + } + + #[instrument(name = "struct", skip_all)] + fn visit_struct(&mut self, structure: &mut StructDefinition) -> Result<()> { + return_source_if_disabled!(self, structure.loc); + self.grouped(|fmt| { + let struct_name = structure.name.safe_unwrap_mut(); + write_chunk!(fmt, struct_name.loc.start(), "struct")?; + struct_name.visit(fmt)?; + if structure.fields.is_empty() { + return fmt.write_empty_brackets() + } + + write!(fmt.buf(), " {{")?; + fmt.surrounded( + SurroundingChunk::new("", Some(struct_name.loc.end()), None), + SurroundingChunk::new("}", None, Some(structure.loc.end())), + |fmt, _multiline| { + let chunks = fmt.items_to_chunks( + Some(structure.loc.end()), + structure.fields.iter_mut().map(|ident| (ident.loc, ident)), + )?; + for mut chunk in chunks { + chunk.content.push(';'); + fmt.write_chunk(&chunk)?; + fmt.write_whitespace_separator(true)?; + } + Ok(()) + }, + ) + })?; + + Ok(()) + } + + #[instrument(name = "event", skip_all)] + fn visit_event(&mut self, event: &mut EventDefinition) -> Result<()> { + return_source_if_disabled!(self, event.loc, ';'); + + let event_name = event.name.safe_unwrap_mut(); + let mut name = + self.visit_to_chunk(event_name.loc.start(), Some(event.loc.end()), event_name)?; + name.content = format!("event {}(", name.content); + + let last_chunk = if event.anonymous { ") anonymous;" } else { ");" }; + if event.fields.is_empty() { + name.content.push_str(last_chunk); + self.write_chunk(&name)?; + } else { + let byte_offset = event.fields.first().unwrap().loc.start(); + let first_chunk = self.chunk_to_string(&name)?; + self.surrounded( + SurroundingChunk::new(first_chunk, Some(byte_offset), None), + SurroundingChunk::new(last_chunk, None, Some(event.loc.end())), + |fmt, multiline| { + let params = fmt + .items_to_chunks(None, event.fields.iter_mut().map(|arg| (arg.loc, arg)))?; + + let multiline = + multiline && fmt.are_chunks_separated_multiline("{}", ¶ms, ",")?; + fmt.write_chunks_separated(¶ms, ",", multiline) + }, + )?; + } + + Ok(()) + } + + #[instrument(name = "event_parameter", skip_all)] + fn visit_event_parameter(&mut self, param: &mut EventParameter) -> Result<()> { + return_source_if_disabled!(self, param.loc); + + self.grouped(|fmt| { + param.ty.visit(fmt)?; + if param.indexed { + write_chunk!(fmt, param.loc.start(), "indexed")?; + } + if let Some(name) = ¶m.name { + write_chunk!(fmt, name.loc.end(), "{}", name.name)?; + } + Ok(()) + })?; + Ok(()) + } + + #[instrument(name = "error", skip_all)] + fn visit_error(&mut self, error: &mut ErrorDefinition) -> Result<()> { + return_source_if_disabled!(self, error.loc, ';'); + + let error_name = error.name.safe_unwrap_mut(); + let mut name = self.visit_to_chunk(error_name.loc.start(), None, error_name)?; + name.content = format!("error {}", name.content); + + let formatted_name = self.chunk_to_string(&name)?; + write!(self.buf(), "{formatted_name}")?; + let start_offset = error.fields.first().map(|f| f.loc.start()); + self.visit_list("", &mut error.fields, start_offset, Some(error.loc.end()), true)?; + self.write_semicolon()?; + + Ok(()) + } + + #[instrument(name = "error_parameter", skip_all)] + fn visit_error_parameter(&mut self, param: &mut ErrorParameter) -> Result<()> { + return_source_if_disabled!(self, param.loc); + self.grouped(|fmt| { + param.ty.visit(fmt)?; + if let Some(name) = ¶m.name { + write_chunk!(fmt, name.loc.end(), "{}", name.name)?; + } + Ok(()) + })?; + Ok(()) + } + + #[instrument(name = "type_definition", skip_all)] + fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<()> { + return_source_if_disabled!(self, def.loc, ';'); + self.grouped(|fmt| { + write_chunk!(fmt, def.loc.start(), def.name.loc.start(), "type")?; + def.name.visit(fmt)?; + write_chunk!(fmt, def.name.loc.end(), CodeLocation::loc(&def.ty).start(), "is")?; + def.ty.visit(fmt)?; + fmt.write_semicolon()?; + Ok(()) + })?; + Ok(()) + } + + #[instrument(name = "stray_semicolon", skip_all)] + fn visit_stray_semicolon(&mut self) -> Result<()> { + self.write_semicolon() + } + + #[instrument(name = "opening_paren", skip_all)] + fn visit_opening_paren(&mut self) -> Result<()> { + write_chunk!(self, "(")?; + Ok(()) + } + + #[instrument(name = "closing_paren", skip_all)] + fn visit_closing_paren(&mut self) -> Result<()> { + write_chunk!(self, ")")?; + Ok(()) + } + + #[instrument(name = "newline", skip_all)] + fn visit_newline(&mut self) -> Result<()> { + writeln_chunk!(self)?; + Ok(()) + } + + #[instrument(name = "using", skip_all)] + fn visit_using(&mut self, using: &mut Using) -> Result<()> { + return_source_if_disabled!(self, using.loc, ';'); + + write_chunk!(self, using.loc.start(), "using")?; + + let ty_start = using.ty.as_mut().map(|ty| CodeLocation::loc(&ty).start()); + let global_start = using.global.as_mut().map(|global| global.loc.start()); + let loc_end = using.loc.end(); + + let (is_library, mut list_chunks) = match &mut using.list { + UsingList::Library(library) => { + (true, vec![self.visit_to_chunk(library.loc.start(), None, library)?]) + } + UsingList::Functions(funcs) => { + let mut funcs = funcs.iter_mut().peekable(); + let mut chunks = Vec::new(); + while let Some(func) = funcs.next() { + let next_byte_end = funcs.peek().map(|func| func.loc.start()); + chunks.push(self.chunked(func.loc.start(), next_byte_end, |fmt| { + fmt.visit_ident_path(&mut func.path)?; + if let Some(op) = func.oper { + write!(fmt.buf(), " as {op}")?; + } + Ok(()) + })?); + } + (false, chunks) + } + UsingList::Error => return self.visit_parser_error(using.loc), + }; + + let for_chunk = self.chunk_at( + using.loc.start(), + Some(ty_start.or(global_start).unwrap_or(loc_end)), + None, + "for", + ); + let ty_chunk = if let Some(ty) = &mut using.ty { + self.visit_to_chunk(ty.loc().start(), Some(global_start.unwrap_or(loc_end)), ty)? + } else { + self.chunk_at(using.loc.start(), Some(global_start.unwrap_or(loc_end)), None, "*") + }; + let global_chunk = using + .global + .as_mut() + .map(|global| self.visit_to_chunk(global.loc.start(), Some(using.loc.end()), global)) + .transpose()?; + + let write_for_def = |fmt: &mut Self| { + fmt.grouped(|fmt| { + fmt.write_chunk(&for_chunk)?; + fmt.write_chunk(&ty_chunk)?; + if let Some(global_chunk) = global_chunk.as_ref() { + fmt.write_chunk(global_chunk)?; + } + Ok(()) + })?; + Ok(()) + }; + + let simulated_for_def = self.simulate_to_string(write_for_def)?; + + if is_library { + let chunk = list_chunks.pop().unwrap(); + if self.will_chunk_fit(&format!("{{}} {simulated_for_def};"), &chunk)? { + self.write_chunk(&chunk)?; + write_for_def(self)?; + } else { + self.write_whitespace_separator(true)?; + self.grouped(|fmt| { + fmt.write_chunk(&chunk)?; + Ok(()) + })?; + self.write_whitespace_separator(true)?; + write_for_def(self)?; + } + } else { + self.surrounded( + SurroundingChunk::new("{", Some(using.loc.start()), None), + SurroundingChunk::new( + "}", + None, + Some(ty_start.or(global_start).unwrap_or(loc_end)), + ), + |fmt, _multiline| { + let multiline = fmt.are_chunks_separated_multiline( + &format!("{{ {{}} }} {simulated_for_def};"), + &list_chunks, + ",", + )?; + fmt.write_chunks_separated(&list_chunks, ",", multiline)?; + Ok(()) + }, + )?; + write_for_def(self)?; + } + + self.write_semicolon()?; + + Ok(()) + } + + #[instrument(name = "yul_block", skip_all)] + fn visit_yul_block( + &mut self, + loc: Loc, + statements: &mut Vec, + attempt_single_line: bool, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + self.visit_block(loc, statements, attempt_single_line, false)?; + Ok(()) + } + + #[instrument(name = "yul_expr", skip_all)] + fn visit_yul_expr(&mut self, expr: &mut YulExpression) -> Result<(), Self::Error> { + return_source_if_disabled!(self, expr.loc()); + + match expr { + YulExpression::BoolLiteral(loc, val, ident) => { + let val = if *val { "true" } else { "false" }; + self.visit_yul_string_with_ident(*loc, val, ident) + } + YulExpression::FunctionCall(expr) => self.visit_yul_function_call(expr), + YulExpression::HexNumberLiteral(loc, val, ident) => { + self.visit_yul_string_with_ident(*loc, val, ident) + } + YulExpression::HexStringLiteral(val, ident) => self.visit_yul_string_with_ident( + val.loc, + &self.quote_str(val.loc, Some("hex"), &val.hex), + ident, + ), + YulExpression::NumberLiteral(loc, val, expr, ident) => { + let val = if expr.is_empty() { val.to_owned() } else { format!("{val}e{expr}") }; + self.visit_yul_string_with_ident(*loc, &val, ident) + } + YulExpression::StringLiteral(val, ident) => self.visit_yul_string_with_ident( + val.loc, + &self.quote_str(val.loc, None, &val.string), + ident, + ), + YulExpression::SuffixAccess(_, expr, ident) => { + self.visit_member_access(expr, ident, |fmt, expr| match expr.as_mut() { + YulExpression::SuffixAccess(_, inner_expr, inner_ident) => { + Ok(Some((inner_expr, inner_ident))) + } + expr => { + expr.visit(fmt)?; + Ok(None) + } + }) + } + YulExpression::Variable(ident) => { + write_chunk!(self, ident.loc.start(), ident.loc.end(), "{}", ident.name) + } + } + } + + #[instrument(name = "yul_assignment", skip_all)] + fn visit_yul_assignment( + &mut self, + loc: Loc, + exprs: &mut Vec, + expr: &mut Option<&mut YulExpression>, + ) -> Result<(), Self::Error> + where + T: Visitable + CodeLocation, + { + return_source_if_disabled!(self, loc); + + self.grouped(|fmt| { + let chunks = + fmt.items_to_chunks(None, exprs.iter_mut().map(|expr| (expr.loc(), expr)))?; + + let multiline = fmt.are_chunks_separated_multiline("{} := ", &chunks, ",")?; + fmt.write_chunks_separated(&chunks, ",", multiline)?; + + if let Some(expr) = expr { + write_chunk!(fmt, expr.loc().start(), ":=")?; + let chunk = fmt.visit_to_chunk(expr.loc().start(), Some(loc.end()), expr)?; + if !fmt.will_chunk_fit("{}", &chunk)? { + fmt.write_whitespace_separator(true)?; + } + fmt.write_chunk(&chunk)?; + } + Ok(()) + })?; + Ok(()) + } + + #[instrument(name = "yul_for", skip_all)] + fn visit_yul_for(&mut self, stmt: &mut YulFor) -> Result<(), Self::Error> { + return_source_if_disabled!(self, stmt.loc); + write_chunk!(self, stmt.loc.start(), "for")?; + self.visit_yul_block(stmt.init_block.loc, &mut stmt.init_block.statements, true)?; + stmt.condition.visit(self)?; + self.visit_yul_block(stmt.post_block.loc, &mut stmt.post_block.statements, true)?; + self.visit_yul_block(stmt.execution_block.loc, &mut stmt.execution_block.statements, true)?; + Ok(()) + } + + #[instrument(name = "yul_function_call", skip_all)] + fn visit_yul_function_call(&mut self, stmt: &mut YulFunctionCall) -> Result<(), Self::Error> { + return_source_if_disabled!(self, stmt.loc); + write_chunk!(self, stmt.loc.start(), "{}", stmt.id.name)?; + self.visit_list("", &mut stmt.arguments, None, Some(stmt.loc.end()), true) + } + + #[instrument(name = "yul_fun_def", skip_all)] + fn visit_yul_fun_def(&mut self, stmt: &mut YulFunctionDefinition) -> Result<(), Self::Error> { + return_source_if_disabled!(self, stmt.loc); + + write_chunk!(self, stmt.loc.start(), "function {}", stmt.id.name)?; + + self.visit_list("", &mut stmt.params, None, None, true)?; + + if !stmt.returns.is_empty() { + self.grouped(|fmt| { + write_chunk!(fmt, "->")?; + + let chunks = fmt.items_to_chunks( + Some(stmt.body.loc.start()), + stmt.returns.iter_mut().map(|param| (param.loc, param)), + )?; + let multiline = fmt.are_chunks_separated_multiline("{}", &chunks, ",")?; + fmt.write_chunks_separated(&chunks, ",", multiline)?; + if multiline { + fmt.write_whitespace_separator(true)?; + } + Ok(()) + })?; + } + + stmt.body.visit(self)?; + + Ok(()) + } + + #[instrument(name = "yul_if", skip_all)] + fn visit_yul_if( + &mut self, + loc: Loc, + expr: &mut YulExpression, + block: &mut YulBlock, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + write_chunk!(self, loc.start(), "if")?; + expr.visit(self)?; + self.visit_yul_block(block.loc, &mut block.statements, true) + } + + #[instrument(name = "yul_leave", skip_all)] + fn visit_yul_leave(&mut self, loc: Loc) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + write_chunk!(self, loc.start(), loc.end(), "leave") + } + + #[instrument(name = "yul_switch", skip_all)] + fn visit_yul_switch(&mut self, stmt: &mut YulSwitch) -> Result<(), Self::Error> { + return_source_if_disabled!(self, stmt.loc); + + write_chunk!(self, stmt.loc.start(), "switch")?; + stmt.condition.visit(self)?; + writeln_chunk!(self)?; + let mut cases = stmt.cases.iter_mut().peekable(); + while let Some(YulSwitchOptions::Case(loc, expr, block)) = cases.next() { + write_chunk!(self, loc.start(), "case")?; + expr.visit(self)?; + self.visit_yul_block(block.loc, &mut block.statements, true)?; + let is_last = cases.peek().is_none(); + if !is_last || stmt.default.is_some() { + writeln_chunk!(self)?; + } + } + if let Some(YulSwitchOptions::Default(loc, ref mut block)) = stmt.default { + write_chunk!(self, loc.start(), "default")?; + self.visit_yul_block(block.loc, &mut block.statements, true)?; + } + Ok(()) + } + + #[instrument(name = "yul_var_declaration", skip_all)] + fn visit_yul_var_declaration( + &mut self, + loc: Loc, + idents: &mut Vec, + expr: &mut Option, + ) -> Result<(), Self::Error> { + return_source_if_disabled!(self, loc); + self.grouped(|fmt| { + write_chunk!(fmt, loc.start(), "let")?; + fmt.visit_yul_assignment(loc, idents, &mut expr.as_mut()) + })?; + Ok(()) + } + + #[instrument(name = "yul_typed_ident", skip_all)] + fn visit_yul_typed_ident(&mut self, ident: &mut YulTypedIdentifier) -> Result<(), Self::Error> { + return_source_if_disabled!(self, ident.loc); + self.visit_yul_string_with_ident(ident.loc, &ident.id.name, &mut ident.ty) + } + + #[instrument(name = "parser_error", skip_all)] + fn visit_parser_error(&mut self, loc: Loc) -> Result<()> { + Err(FormatterError::InvalidParsedItem(loc)) + } +} + +/// An action which may be committed to a Formatter +struct Transaction<'f, 'a, W> { + fmt: &'f mut Formatter<'a, W>, + buffer: String, + comments: Comments, +} + +impl<'a, W> std::ops::Deref for Transaction<'_, 'a, W> { + type Target = Formatter<'a, W>; + fn deref(&self) -> &Self::Target { + self.fmt + } +} + +impl std::ops::DerefMut for Transaction<'_, '_, W> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.fmt + } +} + +impl<'f, 'a, W: Write> Transaction<'f, 'a, W> { + /// Create a new transaction from a callback + fn new( + fmt: &'f mut Formatter<'a, W>, + fun: impl FnMut(&mut Formatter<'a, W>) -> Result<()>, + ) -> Result { + let mut comments = fmt.comments.clone(); + let buffer = fmt.with_temp_buf(fun)?.w; + comments = std::mem::replace(&mut fmt.comments, comments); + Ok(Self { fmt, buffer, comments }) + } + + /// Commit the transaction to the Formatter + fn commit(self) -> Result { + self.fmt.comments = self.comments; + write_chunk!(self.fmt, "{}", self.buffer)?; + Ok(self.buffer) + } +} diff --git a/crates/fmt-2/src/helpers.rs b/crates/fmt-2/src/helpers.rs new file mode 100644 index 0000000000000..1d036ba6b66d0 --- /dev/null +++ b/crates/fmt-2/src/helpers.rs @@ -0,0 +1,135 @@ +use crate::{ + inline_config::{InlineConfig, InvalidInlineConfigItem}, + Comments, Formatter, FormatterConfig, FormatterError, Visitable, +}; +use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; +use itertools::Itertools; +use solang_parser::{diagnostics::Diagnostic, pt::*}; +use std::{fmt::Write, path::Path}; + +/// Result of parsing the source code +#[derive(Debug)] +pub struct Parsed<'a> { + /// The original source code. + pub src: &'a str, + /// The parse tree. + pub pt: SourceUnit, + /// Parsed comments. + pub comments: Comments, + /// Parsed inline config. + pub inline_config: InlineConfig, + /// Invalid inline config items parsed. + pub invalid_inline_config_items: Vec<(Loc, InvalidInlineConfigItem)>, +} + +/// Parse source code. +pub fn parse(src: &str) -> Result, FormatterError> { + parse_raw(src).map_err(|diag| FormatterError::Parse(src.to_string(), None, diag)) +} + +/// Parse source code with a path for diagnostics. +pub fn parse2<'s>(src: &'s str, path: Option<&Path>) -> Result, FormatterError> { + parse_raw(src) + .map_err(|diag| FormatterError::Parse(src.to_string(), path.map(ToOwned::to_owned), diag)) +} + +/// Parse source code, returning a list of diagnostics on failure. +pub fn parse_raw(src: &str) -> Result, Vec> { + let (pt, comments) = solang_parser::parse(src, 0)?; + let comments = Comments::new(comments, src); + let (inline_config_items, invalid_inline_config_items): (Vec<_>, Vec<_>) = + comments.parse_inline_config_items().partition_result(); + let inline_config = InlineConfig::new(inline_config_items, src); + Ok(Parsed { src, pt, comments, inline_config, invalid_inline_config_items }) +} + +/// Format parsed code +pub fn format_to( + writer: W, + mut parsed: Parsed<'_>, + config: FormatterConfig, +) -> Result<(), FormatterError> { + trace!(?parsed, ?config, "Formatting"); + let mut formatter = + Formatter::new(writer, parsed.src, parsed.comments, parsed.inline_config, config); + parsed.pt.visit(&mut formatter) +} + +/// Parse and format a string with default settings +pub fn format(src: &str) -> Result { + let parsed = parse(src)?; + + let mut output = String::new(); + format_to(&mut output, parsed, FormatterConfig::default())?; + + Ok(output) +} + +/// Converts the start offset of a `Loc` to `(line, col)` +pub fn offset_to_line_column(content: &str, start: usize) -> (usize, usize) { + debug_assert!(content.len() > start); + + // first line is `1` + let mut line_counter = 1; + for (offset, c) in content.chars().enumerate() { + if c == '\n' { + line_counter += 1; + } + if offset > start { + return (line_counter, offset - start) + } + } + + unreachable!("content.len() > start") +} + +/// Formats parser diagnostics +pub fn format_diagnostics_report( + content: &str, + path: Option<&Path>, + diagnostics: &[Diagnostic], +) -> String { + if diagnostics.is_empty() { + return String::new(); + } + + let filename = + path.map(|p| p.file_name().unwrap().to_string_lossy().to_string()).unwrap_or_default(); + let mut s = Vec::new(); + for diag in diagnostics { + let span = (filename.as_str(), diag.loc.start()..diag.loc.end()); + let mut report = Report::build(ReportKind::Error, span.clone()) + .with_message(format!("{:?}", diag.ty)) + .with_label( + Label::new(span) + .with_color(Color::Red) + .with_message(diag.message.as_str().fg(Color::Red)), + ); + + for note in &diag.notes { + report = report.with_note(¬e.message); + } + + report.finish().write((filename.as_str(), Source::from(content)), &mut s).unwrap(); + } + String::from_utf8(s).unwrap() +} + +pub fn import_path_string(path: &ImportPath) -> String { + match path { + ImportPath::Filename(s) => s.string.clone(), + ImportPath::Path(p) => p.to_string(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // + #[test] + fn test_interface_format() { + let s = "interface I {\n function increment() external;\n function number() external view returns (uint256);\n function setNumber(uint256 newNumber) external;\n}"; + let _formatted = format(s).unwrap(); + } +} diff --git a/crates/fmt-2/src/inline_config.rs b/crates/fmt-2/src/inline_config.rs new file mode 100644 index 0000000000000..fb0a3dfb0977e --- /dev/null +++ b/crates/fmt-2/src/inline_config.rs @@ -0,0 +1,155 @@ +use crate::comments::{CommentState, CommentStringExt}; +use itertools::Itertools; +use solang_parser::pt::Loc; +use std::{fmt, str::FromStr}; + +/// An inline config item +#[derive(Clone, Copy, Debug)] +pub enum InlineConfigItem { + /// Disables the next code item regardless of newlines + DisableNextItem, + /// Disables formatting on the current line + DisableLine, + /// Disables formatting between the next newline and the newline after + DisableNextLine, + /// Disables formatting for any code that follows this and before the next "disable-end" + DisableStart, + /// Disables formatting for any code that precedes this and after the previous "disable-start" + DisableEnd, +} + +impl FromStr for InlineConfigItem { + type Err = InvalidInlineConfigItem; + fn from_str(s: &str) -> Result { + Ok(match s { + "disable-next-item" => Self::DisableNextItem, + "disable-line" => Self::DisableLine, + "disable-next-line" => Self::DisableNextLine, + "disable-start" => Self::DisableStart, + "disable-end" => Self::DisableEnd, + s => return Err(InvalidInlineConfigItem(s.into())), + }) + } +} + +#[derive(Debug)] +pub struct InvalidInlineConfigItem(String); + +impl fmt::Display for InvalidInlineConfigItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("Invalid inline config item: {}", self.0)) + } +} + +/// A disabled formatting range. `loose` designates that the range includes any loc which +/// may start in between start and end, whereas the strict version requires that +/// `range.start >= loc.start <=> loc.end <= range.end` +#[derive(Debug)] +struct DisabledRange { + start: usize, + end: usize, + loose: bool, +} + +impl DisabledRange { + fn includes(&self, loc: Loc) -> bool { + loc.start() >= self.start && (if self.loose { loc.start() } else { loc.end() } <= self.end) + } +} + +/// An inline config. Keeps track of disabled ranges. +/// +/// This is a list of Inline Config items for locations in a source file. This is +/// usually acquired by parsing the comments for an `forgefmt:` items. +/// +/// See [`Comments::parse_inline_config_items`](crate::Comments::parse_inline_config_items) for +/// details. +#[derive(Debug, Default)] +pub struct InlineConfig { + disabled_ranges: Vec, +} + +impl InlineConfig { + /// Build a new inline config with an iterator of inline config items and their locations in a + /// source file + pub fn new(items: impl IntoIterator, src: &str) -> Self { + let mut disabled_ranges = vec![]; + let mut disabled_range_start = None; + let mut disabled_depth = 0usize; + for (loc, item) in items.into_iter().sorted_by_key(|(loc, _)| loc.start()) { + match item { + InlineConfigItem::DisableNextItem => { + let offset = loc.end(); + let mut char_indices = src[offset..] + .comment_state_char_indices() + .filter_map(|(state, idx, ch)| match state { + CommentState::None => Some((idx, ch)), + _ => None, + }) + .skip_while(|(_, ch)| ch.is_whitespace()); + if let Some((mut start, _)) = char_indices.next() { + start += offset; + let end = char_indices + .find(|(_, ch)| !ch.is_whitespace()) + .map(|(idx, _)| offset + idx) + .unwrap_or(src.len()); + disabled_ranges.push(DisabledRange { start, end, loose: true }); + } + } + InlineConfigItem::DisableLine => { + let mut prev_newline = + src[..loc.start()].char_indices().rev().skip_while(|(_, ch)| *ch != '\n'); + let start = prev_newline.next().map(|(idx, _)| idx).unwrap_or_default(); + + let end_offset = loc.end(); + let mut next_newline = + src[end_offset..].char_indices().skip_while(|(_, ch)| *ch != '\n'); + let end = + end_offset + next_newline.next().map(|(idx, _)| idx).unwrap_or_default(); + + disabled_ranges.push(DisabledRange { start, end, loose: false }); + } + InlineConfigItem::DisableNextLine => { + let offset = loc.end(); + let mut char_indices = + src[offset..].char_indices().skip_while(|(_, ch)| *ch != '\n').skip(1); + if let Some((mut start, _)) = char_indices.next() { + start += offset; + let end = char_indices + .find(|(_, ch)| *ch == '\n') + .map(|(idx, _)| offset + idx + 1) + .unwrap_or(src.len()); + disabled_ranges.push(DisabledRange { start, end, loose: false }); + } + } + InlineConfigItem::DisableStart => { + if disabled_depth == 0 { + disabled_range_start = Some(loc.end()); + } + disabled_depth += 1; + } + InlineConfigItem::DisableEnd => { + disabled_depth = disabled_depth.saturating_sub(1); + if disabled_depth == 0 { + if let Some(start) = disabled_range_start.take() { + disabled_ranges.push(DisabledRange { + start, + end: loc.start(), + loose: false, + }) + } + } + } + } + } + if let Some(start) = disabled_range_start.take() { + disabled_ranges.push(DisabledRange { start, end: src.len(), loose: false }) + } + Self { disabled_ranges } + } + + /// Check if the location is in a disabled range + pub fn is_disabled(&self, loc: Loc) -> bool { + self.disabled_ranges.iter().any(|range| range.includes(loc)) + } +} diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs new file mode 100644 index 0000000000000..006b4db02abe8 --- /dev/null +++ b/crates/fmt-2/src/lib.rs @@ -0,0 +1,27 @@ +#![doc = include_str!("../README.md")] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +#[macro_use] +extern crate tracing; + +mod buffer; +pub mod chunk; +mod comments; +mod formatter; +mod helpers; +pub mod inline_config; +mod macros; +pub mod solang_ext; +mod string; +pub mod visit; + +pub use foundry_config::fmt::*; + +pub use comments::Comments; +pub use formatter::{Formatter, FormatterError}; +pub use helpers::{ + format, format_diagnostics_report, format_to, offset_to_line_column, parse, parse2, Parsed, +}; +pub use inline_config::InlineConfig; +pub use visit::{Visitable, Visitor}; diff --git a/crates/fmt-2/src/macros.rs b/crates/fmt-2/src/macros.rs new file mode 100644 index 0000000000000..c5c9d31a7d0e0 --- /dev/null +++ b/crates/fmt-2/src/macros.rs @@ -0,0 +1,125 @@ +macro_rules! write_chunk { + ($self:expr, $format_str:literal) => {{ + write_chunk!($self, $format_str,) + }}; + ($self:expr, $format_str:literal, $($arg:tt)*) => {{ + $self.write_chunk(&format!($format_str, $($arg)*).into()) + }}; + ($self:expr, $loc:expr) => {{ + write_chunk!($self, $loc, "") + }}; + ($self:expr, $loc:expr, $format_str:literal) => {{ + write_chunk!($self, $loc, $format_str,) + }}; + ($self:expr, $loc:expr, $format_str:literal, $($arg:tt)*) => {{ + let chunk = $self.chunk_at($loc, None, None, format_args!($format_str, $($arg)*),); + $self.write_chunk(&chunk) + }}; + ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal) => {{ + write_chunk!($self, $loc, $end_loc, $format_str,) + }}; + ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal, $($arg:tt)*) => {{ + let chunk = $self.chunk_at($loc, Some($end_loc), None, format_args!($format_str, $($arg)*),); + $self.write_chunk(&chunk) + }}; + ($self:expr, $loc:expr, $end_loc:expr, $needs_space:expr, $format_str:literal, $($arg:tt)*) => {{ + let chunk = $self.chunk_at($loc, Some($end_loc), Some($needs_space), format_args!($format_str, $($arg)*),); + $self.write_chunk(&chunk) + }}; +} + +macro_rules! writeln_chunk { + ($self:expr) => {{ + writeln_chunk!($self, "") + }}; + ($self:expr, $format_str:literal) => {{ + writeln_chunk!($self, $format_str,) + }}; + ($self:expr, $format_str:literal, $($arg:tt)*) => {{ + write_chunk!($self, "{}\n", format_args!($format_str, $($arg)*)) + }}; + ($self:expr, $loc:expr) => {{ + writeln_chunk!($self, $loc, "") + }}; + ($self:expr, $loc:expr, $format_str:literal) => {{ + writeln_chunk!($self, $loc, $format_str,) + }}; + ($self:expr, $loc:expr, $format_str:literal, $($arg:tt)*) => {{ + write_chunk!($self, $loc, "{}\n", format_args!($format_str, $($arg)*)) + }}; + ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal) => {{ + writeln_chunk!($self, $loc, $end_loc, $format_str,) + }}; + ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal, $($arg:tt)*) => {{ + write_chunk!($self, $loc, $end_loc, "{}\n", format_args!($format_str, $($arg)*)) + }}; +} + +macro_rules! write_chunk_spaced { + ($self:expr, $loc:expr, $needs_space:expr, $format_str:literal) => {{ + write_chunk_spaced!($self, $loc, $needs_space, $format_str,) + }}; + ($self:expr, $loc:expr, $needs_space:expr, $format_str:literal, $($arg:tt)*) => {{ + let chunk = $self.chunk_at($loc, None, $needs_space, format_args!($format_str, $($arg)*),); + $self.write_chunk(&chunk) + }}; +} + +macro_rules! buf_fn { + ($vis:vis fn $name:ident(&self $(,)? $($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)?) => { + $vis fn $name(&self, $($arg_name : $arg_ty),*) $(-> $ret)? { + if self.temp_bufs.is_empty() { + self.buf.$name($($arg_name),*) + } else { + self.temp_bufs.last().unwrap().$name($($arg_name),*) + } + } + }; + ($vis:vis fn $name:ident(&mut self $(,)? $($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)?) => { + $vis fn $name(&mut self, $($arg_name : $arg_ty),*) $(-> $ret)? { + if self.temp_bufs.is_empty() { + self.buf.$name($($arg_name),*) + } else { + self.temp_bufs.last_mut().unwrap().$name($($arg_name),*) + } + } + }; +} + +macro_rules! return_source_if_disabled { + ($self:expr, $loc:expr) => {{ + let loc = $loc; + if $self.inline_config.is_disabled(loc) { + trace!("Returning because disabled: {loc:?}"); + return $self.visit_source(loc) + } + }}; + ($self:expr, $loc:expr, $suffix:literal) => {{ + let mut loc = $loc; + let has_suffix = $self.extend_loc_until(&mut loc, $suffix); + if $self.inline_config.is_disabled(loc) { + $self.visit_source(loc)?; + trace!("Returning because disabled: {loc:?}"); + if !has_suffix { + write!($self.buf(), "{}", $suffix)?; + } + return Ok(()) + } + }}; +} + +macro_rules! visit_source_if_disabled_else { + ($self:expr, $loc:expr, $block:block) => {{ + let loc = $loc; + if $self.inline_config.is_disabled(loc) { + $self.visit_source(loc)?; + } else $block + }}; +} + +pub(crate) use buf_fn; +pub(crate) use return_source_if_disabled; +pub(crate) use visit_source_if_disabled_else; +pub(crate) use write_chunk; +pub(crate) use write_chunk_spaced; +pub(crate) use writeln_chunk; diff --git a/crates/fmt-2/src/solang_ext/ast_eq.rs b/crates/fmt-2/src/solang_ext/ast_eq.rs new file mode 100644 index 0000000000000..3c796a0d00128 --- /dev/null +++ b/crates/fmt-2/src/solang_ext/ast_eq.rs @@ -0,0 +1,683 @@ +use alloy_primitives::{Address, I256, U256}; +use solang_parser::pt::*; +use std::str::FromStr; + +/// Helper to convert a string number into a comparable one +fn to_num(string: &str) -> I256 { + if string.is_empty() { + return I256::ZERO + } + string.replace('_', "").trim().parse().unwrap() +} + +/// Helper to convert the fractional part of a number into a comparable one. +/// This will reverse the number so that 0's can be ignored +fn to_num_reversed(string: &str) -> U256 { + if string.is_empty() { + return U256::from(0) + } + string.replace('_', "").trim().chars().rev().collect::().parse().unwrap() +} + +/// Helper to filter [ParameterList] to omit empty +/// parameters +fn filter_params(list: &ParameterList) -> ParameterList { + list.iter().filter(|(_, param)| param.is_some()).cloned().collect::>() +} + +/// Check if two ParseTrees are equal ignoring location information or ordering if ordering does +/// not matter +pub trait AstEq { + fn ast_eq(&self, other: &Self) -> bool; +} + +impl AstEq for Loc { + fn ast_eq(&self, _other: &Self) -> bool { + true + } +} + +impl AstEq for IdentifierPath { + fn ast_eq(&self, other: &Self) -> bool { + self.identifiers.ast_eq(&other.identifiers) + } +} + +impl AstEq for SourceUnit { + fn ast_eq(&self, other: &Self) -> bool { + self.0.ast_eq(&other.0) + } +} + +impl AstEq for VariableDefinition { + fn ast_eq(&self, other: &Self) -> bool { + let sorted_attrs = |def: &Self| { + let mut attrs = def.attrs.clone(); + attrs.sort(); + attrs + }; + self.ty.ast_eq(&other.ty) && + self.name.ast_eq(&other.name) && + self.initializer.ast_eq(&other.initializer) && + sorted_attrs(self).ast_eq(&sorted_attrs(other)) + } +} + +impl AstEq for FunctionDefinition { + fn ast_eq(&self, other: &Self) -> bool { + // attributes + let sorted_attrs = |def: &Self| { + let mut attrs = def.attributes.clone(); + attrs.sort(); + attrs + }; + + // params + let left_params = filter_params(&self.params); + let right_params = filter_params(&other.params); + let left_returns = filter_params(&self.returns); + let right_returns = filter_params(&other.returns); + + self.ty.ast_eq(&other.ty) && + self.name.ast_eq(&other.name) && + left_params.ast_eq(&right_params) && + self.return_not_returns.ast_eq(&other.return_not_returns) && + left_returns.ast_eq(&right_returns) && + self.body.ast_eq(&other.body) && + sorted_attrs(self).ast_eq(&sorted_attrs(other)) + } +} + +impl AstEq for Base { + fn ast_eq(&self, other: &Self) -> bool { + self.name.ast_eq(&other.name) && + self.args.clone().unwrap_or_default().ast_eq(&other.args.clone().unwrap_or_default()) + } +} + +impl AstEq for Vec +where + T: AstEq, +{ + fn ast_eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + false + } else { + self.iter().zip(other.iter()).all(|(left, right)| left.ast_eq(right)) + } + } +} + +impl AstEq for Option +where + T: AstEq, +{ + fn ast_eq(&self, other: &Self) -> bool { + match (self, other) { + (Some(left), Some(right)) => left.ast_eq(right), + (None, None) => true, + _ => false, + } + } +} + +impl AstEq for Box +where + T: AstEq, +{ + fn ast_eq(&self, other: &Self) -> bool { + T::ast_eq(self, other) + } +} + +impl AstEq for () { + fn ast_eq(&self, _other: &Self) -> bool { + true + } +} + +impl AstEq for &T +where + T: AstEq, +{ + fn ast_eq(&self, other: &Self) -> bool { + T::ast_eq(self, other) + } +} + +impl AstEq for String { + fn ast_eq(&self, other: &Self) -> bool { + match (Address::from_str(self), Address::from_str(other)) { + (Ok(left), Ok(right)) => left == right, + _ => self == other, + } + } +} + +macro_rules! ast_eq_field { + (#[ast_eq_use($convert_func:ident)] $field:ident) => { + $convert_func($field) + }; + ($field:ident) => { + $field + }; +} + +macro_rules! gen_ast_eq_enum { + ($self:expr, $other:expr, $name:ident { + $($unit_variant:ident),* $(,)? + _ + $($tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? )),* $(,)? + _ + $($struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? }),* $(,)? + }) => { + match $self { + $($name::$unit_variant => gen_ast_eq_enum!($other, $name, $unit_variant),)* + $($name::$tuple_variant($($tuple_field),*) => + gen_ast_eq_enum!($other, $name, $tuple_variant ($($(#[ast_eq_use($tuple_convert_func)])? $tuple_field),*)),)* + $($name::$struct_variant { $($struct_field),* } => + gen_ast_eq_enum!($other, $name, $struct_variant {$($(#[ast_eq_use($struct_convert_func)])? $struct_field),*}),)* + } + }; + ($other:expr, $name:ident, $unit_variant:ident) => { + { + matches!($other, $name::$unit_variant) + } + }; + ($other:expr, $name:ident, $tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? ) ) => { + { + let left = ($(ast_eq_field!($(#[ast_eq_use($tuple_convert_func)])? $tuple_field)),*); + if let $name::$tuple_variant($($tuple_field),*) = $other { + let right = ($(ast_eq_field!($(#[ast_eq_use($tuple_convert_func)])? $tuple_field)),*); + left.ast_eq(&right) + } else { + false + } + } + }; + ($other:expr, $name:ident, $struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? } ) => { + { + let left = ($(ast_eq_field!($(#[ast_eq_use($struct_convert_func)])? $struct_field)),*); + if let $name::$struct_variant { $($struct_field),* } = $other { + let right = ($(ast_eq_field!($(#[ast_eq_use($struct_convert_func)])? $struct_field)),*); + left.ast_eq(&right) + } else { + false + } + } + }; +} + +macro_rules! wrap_in_box { + ($stmt:expr, $loc:expr) => { + if !matches!(**$stmt, Statement::Block { .. }) { + Box::new(Statement::Block { + loc: $loc, + unchecked: false, + statements: vec![*$stmt.clone()], + }) + } else { + $stmt.clone() + } + }; +} + +impl AstEq for Statement { + fn ast_eq(&self, other: &Self) -> bool { + match self { + Self::If(loc, expr, stmt1, stmt2) => { + #[expect(clippy::borrowed_box)] + let wrap_if = |stmt1: &Box, stmt2: &Option>| { + ( + wrap_in_box!(stmt1, *loc), + stmt2.as_ref().map(|stmt2| { + if matches!(**stmt2, Self::If(..)) { + stmt2.clone() + } else { + wrap_in_box!(stmt2, *loc) + } + }), + ) + }; + let (stmt1, stmt2) = wrap_if(stmt1, stmt2); + let left = (loc, expr, &stmt1, &stmt2); + if let Self::If(loc, expr, stmt1, stmt2) = other { + let (stmt1, stmt2) = wrap_if(stmt1, stmt2); + let right = (loc, expr, &stmt1, &stmt2); + left.ast_eq(&right) + } else { + false + } + } + Self::While(loc, expr, stmt1) => { + let stmt1 = wrap_in_box!(stmt1, *loc); + let left = (loc, expr, &stmt1); + if let Self::While(loc, expr, stmt1) = other { + let stmt1 = wrap_in_box!(stmt1, *loc); + let right = (loc, expr, &stmt1); + left.ast_eq(&right) + } else { + false + } + } + Self::DoWhile(loc, stmt1, expr) => { + let stmt1 = wrap_in_box!(stmt1, *loc); + let left = (loc, &stmt1, expr); + if let Self::DoWhile(loc, stmt1, expr) = other { + let stmt1 = wrap_in_box!(stmt1, *loc); + let right = (loc, &stmt1, expr); + left.ast_eq(&right) + } else { + false + } + } + Self::For(loc, stmt1, expr, stmt2, stmt3) => { + let stmt3 = stmt3.as_ref().map(|stmt3| wrap_in_box!(stmt3, *loc)); + let left = (loc, stmt1, expr, stmt2, &stmt3); + if let Self::For(loc, stmt1, expr, stmt2, stmt3) = other { + let stmt3 = stmt3.as_ref().map(|stmt3| wrap_in_box!(stmt3, *loc)); + let right = (loc, stmt1, expr, stmt2, &stmt3); + left.ast_eq(&right) + } else { + false + } + } + Self::Try(loc, expr, returns, catch) => { + let left_returns = + returns.as_ref().map(|(params, stmt)| (filter_params(params), stmt)); + let left = (loc, expr, left_returns, catch); + if let Self::Try(loc, expr, returns, catch) = other { + let right_returns = + returns.as_ref().map(|(params, stmt)| (filter_params(params), stmt)); + let right = (loc, expr, right_returns, catch); + left.ast_eq(&right) + } else { + false + } + } + _ => gen_ast_eq_enum!(self, other, Statement { + _ + Args(loc, args), + Expression(loc, expr), + VariableDefinition(loc, decl, expr), + Continue(loc, ), + Break(loc, ), + Return(loc, expr), + Revert(loc, expr, expr2), + RevertNamedArgs(loc, expr, args), + Emit(loc, expr), + // provide overridden variants regardless + If(loc, expr, stmt1, stmt2), + While(loc, expr, stmt1), + DoWhile(loc, stmt1, expr), + For(loc, stmt1, expr, stmt2, stmt3), + Try(loc, expr, params, clause), + Error(loc) + _ + Block { + loc, + unchecked, + statements, + }, + Assembly { + loc, + dialect, + block, + flags, + }, + }), + } + } +} + +macro_rules! derive_ast_eq { + ($name:ident) => { + impl AstEq for $name { + fn ast_eq(&self, other: &Self) -> bool { + self == other + } + } + }; + (($($index:tt $gen:tt),*)) => { + impl < $( $gen ),* > AstEq for ($($gen,)*) where $($gen: AstEq),* { + fn ast_eq(&self, other: &Self) -> bool { + $( + if !self.$index.ast_eq(&other.$index) { + return false + } + )* + true + } + } + }; + (struct $name:ident { $($field:ident),* $(,)? }) => { + impl AstEq for $name { + fn ast_eq(&self, other: &Self) -> bool { + let $name { $($field),* } = self; + let left = ($($field),*); + let $name { $($field),* } = other; + let right = ($($field),*); + left.ast_eq(&right) + } + } + }; + (enum $name:ident { + $($unit_variant:ident),* $(,)? + _ + $($tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? )),* $(,)? + _ + $($struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? }),* $(,)? + }) => { + impl AstEq for $name { + fn ast_eq(&self, other: &Self) -> bool { + gen_ast_eq_enum!(self, other, $name { + $($unit_variant),* + _ + $($tuple_variant ( $($(#[ast_eq_use($tuple_convert_func)])? $tuple_field),* )),* + _ + $($struct_variant { $($(#[ast_eq_use($struct_convert_func)])? $struct_field),* }),* + }) + } + } + } +} + +derive_ast_eq! { (0 A) } +derive_ast_eq! { (0 A, 1 B) } +derive_ast_eq! { (0 A, 1 B, 2 C) } +derive_ast_eq! { (0 A, 1 B, 2 C, 3 D) } +derive_ast_eq! { (0 A, 1 B, 2 C, 3 D, 4 E) } +derive_ast_eq! { bool } +derive_ast_eq! { u8 } +derive_ast_eq! { u16 } +derive_ast_eq! { I256 } +derive_ast_eq! { U256 } +derive_ast_eq! { struct Identifier { loc, name } } +derive_ast_eq! { struct HexLiteral { loc, hex } } +derive_ast_eq! { struct StringLiteral { loc, unicode, string } } +derive_ast_eq! { struct Parameter { loc, annotation, ty, storage, name } } +derive_ast_eq! { struct NamedArgument { loc, name, expr } } +derive_ast_eq! { struct YulBlock { loc, statements } } +derive_ast_eq! { struct YulFunctionCall { loc, id, arguments } } +derive_ast_eq! { struct YulFunctionDefinition { loc, id, params, returns, body } } +derive_ast_eq! { struct YulSwitch { loc, condition, cases, default } } +derive_ast_eq! { struct YulFor { + loc, + init_block, + condition, + post_block, + execution_block, +}} +derive_ast_eq! { struct YulTypedIdentifier { loc, id, ty } } +derive_ast_eq! { struct VariableDeclaration { loc, ty, storage, name } } +derive_ast_eq! { struct Using { loc, list, ty, global } } +derive_ast_eq! { struct UsingFunction { loc, path, oper } } +derive_ast_eq! { struct TypeDefinition { loc, name, ty } } +derive_ast_eq! { struct ContractDefinition { loc, ty, name, base, parts } } +derive_ast_eq! { struct EventParameter { loc, ty, indexed, name } } +derive_ast_eq! { struct ErrorParameter { loc, ty, name } } +derive_ast_eq! { struct EventDefinition { loc, name, fields, anonymous } } +derive_ast_eq! { struct ErrorDefinition { loc, keyword, name, fields } } +derive_ast_eq! { struct StructDefinition { loc, name, fields } } +derive_ast_eq! { struct EnumDefinition { loc, name, values } } +derive_ast_eq! { struct Annotation { loc, id, value } } +derive_ast_eq! { enum UsingList { + Error, + _ + Library(expr), + Functions(exprs), + _ +}} +derive_ast_eq! { enum UserDefinedOperator { + BitwiseAnd, + BitwiseNot, + Negate, + BitwiseOr, + BitwiseXor, + Add, + Divide, + Modulo, + Multiply, + Subtract, + Equal, + More, + MoreEqual, + Less, + LessEqual, + NotEqual, + _ + _ +}} +derive_ast_eq! { enum Visibility { + _ + External(loc), + Public(loc), + Internal(loc), + Private(loc), + _ +}} +derive_ast_eq! { enum Mutability { + _ + Pure(loc), + View(loc), + Constant(loc), + Payable(loc), + _ +}} +derive_ast_eq! { enum FunctionAttribute { + _ + Mutability(muta), + Visibility(visi), + Virtual(loc), + Immutable(loc), + Override(loc, idents), + BaseOrModifier(loc, base), + Error(loc), + _ +}} +derive_ast_eq! { enum StorageLocation { + _ + Memory(loc), + Storage(loc), + Calldata(loc), + _ +}} +derive_ast_eq! { enum Type { + Address, + AddressPayable, + Payable, + Bool, + Rational, + DynamicBytes, + String, + _ + Int(int), + Uint(int), + Bytes(int), + _ + Mapping{ loc, key, key_name, value, value_name }, + Function { params, attributes, returns }, +}} +derive_ast_eq! { enum Expression { + _ + PostIncrement(loc, expr1), + PostDecrement(loc, expr1), + New(loc, expr1), + ArraySubscript(loc, expr1, expr2), + ArraySlice( + loc, + expr1, + expr2, + expr3, + ), + MemberAccess(loc, expr1, ident1), + FunctionCall(loc, expr1, exprs1), + FunctionCallBlock(loc, expr1, stmt), + NamedFunctionCall(loc, expr1, args), + Not(loc, expr1), + BitwiseNot(loc, expr1), + Delete(loc, expr1), + PreIncrement(loc, expr1), + PreDecrement(loc, expr1), + UnaryPlus(loc, expr1), + Negate(loc, expr1), + Power(loc, expr1, expr2), + Multiply(loc, expr1, expr2), + Divide(loc, expr1, expr2), + Modulo(loc, expr1, expr2), + Add(loc, expr1, expr2), + Subtract(loc, expr1, expr2), + ShiftLeft(loc, expr1, expr2), + ShiftRight(loc, expr1, expr2), + BitwiseAnd(loc, expr1, expr2), + BitwiseXor(loc, expr1, expr2), + BitwiseOr(loc, expr1, expr2), + Less(loc, expr1, expr2), + More(loc, expr1, expr2), + LessEqual(loc, expr1, expr2), + MoreEqual(loc, expr1, expr2), + Equal(loc, expr1, expr2), + NotEqual(loc, expr1, expr2), + And(loc, expr1, expr2), + Or(loc, expr1, expr2), + ConditionalOperator(loc, expr1, expr2, expr3), + Assign(loc, expr1, expr2), + AssignOr(loc, expr1, expr2), + AssignAnd(loc, expr1, expr2), + AssignXor(loc, expr1, expr2), + AssignShiftLeft(loc, expr1, expr2), + AssignShiftRight(loc, expr1, expr2), + AssignAdd(loc, expr1, expr2), + AssignSubtract(loc, expr1, expr2), + AssignMultiply(loc, expr1, expr2), + AssignDivide(loc, expr1, expr2), + AssignModulo(loc, expr1, expr2), + BoolLiteral(loc, bool1), + NumberLiteral(loc, #[ast_eq_use(to_num)] str1, #[ast_eq_use(to_num)] str2, unit), + RationalNumberLiteral( + loc, + #[ast_eq_use(to_num)] str1, + #[ast_eq_use(to_num_reversed)] str2, + #[ast_eq_use(to_num)] str3, + unit + ), + HexNumberLiteral(loc, str1, unit), + StringLiteral(strs1), + Type(loc, ty1), + HexLiteral(hexs1), + AddressLiteral(loc, str1), + Variable(ident1), + List(loc, params1), + ArrayLiteral(loc, exprs1), + Parenthesis(loc, expr) + _ +}} +derive_ast_eq! { enum CatchClause { + _ + Simple(param, ident, stmt), + Named(loc, ident, param, stmt), + _ +}} +derive_ast_eq! { enum YulStatement { + _ + Assign(loc, exprs, expr), + VariableDeclaration(loc, idents, expr), + If(loc, expr, block), + For(yul_for), + Switch(switch), + Leave(loc), + Break(loc), + Continue(loc), + Block(block), + FunctionDefinition(def), + FunctionCall(func), + Error(loc), + _ +}} +derive_ast_eq! { enum YulExpression { + _ + BoolLiteral(loc, boo, ident), + NumberLiteral(loc, string1, string2, ident), + HexNumberLiteral(loc, string, ident), + HexStringLiteral(hex, ident), + StringLiteral(string, ident), + Variable(ident), + FunctionCall(func), + SuffixAccess(loc, expr, ident), + _ +}} +derive_ast_eq! { enum YulSwitchOptions { + _ + Case(loc, expr, block), + Default(loc, block), + _ +}} +derive_ast_eq! { enum SourceUnitPart { + _ + ContractDefinition(def), + PragmaDirective(loc, ident, string), + ImportDirective(import), + EnumDefinition(def), + StructDefinition(def), + EventDefinition(def), + ErrorDefinition(def), + FunctionDefinition(def), + VariableDefinition(def), + TypeDefinition(def), + Using(using), + StraySemicolon(loc), + Annotation(annotation), + _ +}} +derive_ast_eq! { enum ImportPath { + _ + Filename(lit), + Path(path), + _ +}} +derive_ast_eq! { enum Import { + _ + Plain(string, loc), + GlobalSymbol(string, ident, loc), + Rename(string, idents, loc), + _ +}} +derive_ast_eq! { enum FunctionTy { + Constructor, + Function, + Fallback, + Receive, + Modifier, + _ + _ +}} +derive_ast_eq! { enum ContractPart { + _ + StructDefinition(def), + EventDefinition(def), + EnumDefinition(def), + ErrorDefinition(def), + VariableDefinition(def), + FunctionDefinition(def), + TypeDefinition(def), + StraySemicolon(loc), + Using(using), + Annotation(annotation), + _ +}} +derive_ast_eq! { enum ContractTy { + _ + Abstract(loc), + Contract(loc), + Interface(loc), + Library(loc), + _ +}} +derive_ast_eq! { enum VariableAttribute { + _ + Visibility(visi), + Constant(loc), + Immutable(loc), + Override(loc, idents), + _ +}} diff --git a/crates/fmt-2/src/solang_ext/loc.rs b/crates/fmt-2/src/solang_ext/loc.rs new file mode 100644 index 0000000000000..54bf771c6df90 --- /dev/null +++ b/crates/fmt-2/src/solang_ext/loc.rs @@ -0,0 +1,156 @@ +use solang_parser::pt; +use std::{borrow::Cow, rc::Rc, sync::Arc}; + +/// Returns the code location. +/// +/// Patched version of [`pt::CodeLocation`]: includes the block of a [`pt::FunctionDefinition`] in +/// its `loc`. +pub trait CodeLocationExt { + /// Returns the code location of `self`. + fn loc(&self) -> pt::Loc; +} + +impl CodeLocationExt for &T { + fn loc(&self) -> pt::Loc { + (**self).loc() + } +} + +impl CodeLocationExt for &mut T { + fn loc(&self) -> pt::Loc { + (**self).loc() + } +} + +impl CodeLocationExt for Cow<'_, T> { + fn loc(&self) -> pt::Loc { + (**self).loc() + } +} + +impl CodeLocationExt for Box { + fn loc(&self) -> pt::Loc { + (**self).loc() + } +} + +impl CodeLocationExt for Rc { + fn loc(&self) -> pt::Loc { + (**self).loc() + } +} + +impl CodeLocationExt for Arc { + fn loc(&self) -> pt::Loc { + (**self).loc() + } +} + +// FunctionDefinition patch +impl CodeLocationExt for pt::FunctionDefinition { + #[inline] + #[track_caller] + fn loc(&self) -> pt::Loc { + let mut loc = self.loc; + if let Some(ref body) = self.body { + loc.use_end_from(&pt::CodeLocation::loc(body)); + } + loc + } +} + +impl CodeLocationExt for pt::ContractPart { + #[inline] + #[track_caller] + fn loc(&self) -> pt::Loc { + match self { + Self::FunctionDefinition(f) => f.loc(), + _ => pt::CodeLocation::loc(self), + } + } +} + +impl CodeLocationExt for pt::SourceUnitPart { + #[inline] + #[track_caller] + fn loc(&self) -> pt::Loc { + match self { + Self::FunctionDefinition(f) => f.loc(), + _ => pt::CodeLocation::loc(self), + } + } +} + +impl CodeLocationExt for pt::ImportPath { + fn loc(&self) -> pt::Loc { + match self { + Self::Filename(s) => s.loc(), + Self::Path(i) => i.loc(), + } + } +} + +macro_rules! impl_delegate { + ($($t:ty),+ $(,)?) => {$( + impl CodeLocationExt for $t { + #[inline] + #[track_caller] + fn loc(&self) -> pt::Loc { + pt::CodeLocation::loc(self) + } + } + )+}; +} + +impl_delegate! { + pt::Annotation, + pt::Base, + pt::ContractDefinition, + pt::EnumDefinition, + pt::ErrorDefinition, + pt::ErrorParameter, + pt::EventDefinition, + pt::EventParameter, + // pt::FunctionDefinition, + pt::HexLiteral, + pt::Identifier, + pt::IdentifierPath, + pt::NamedArgument, + pt::Parameter, + // pt::SourceUnit, + pt::StringLiteral, + pt::StructDefinition, + pt::TypeDefinition, + pt::Using, + pt::UsingFunction, + pt::VariableDeclaration, + pt::VariableDefinition, + pt::YulBlock, + pt::YulFor, + pt::YulFunctionCall, + pt::YulFunctionDefinition, + pt::YulSwitch, + pt::YulTypedIdentifier, + + pt::CatchClause, + pt::Comment, + // pt::ContractPart, + pt::ContractTy, + pt::Expression, + pt::FunctionAttribute, + // pt::FunctionTy, + pt::Import, + pt::Loc, + pt::Mutability, + // pt::SourceUnitPart, + pt::Statement, + pt::StorageLocation, + // pt::Type, + // pt::UserDefinedOperator, + pt::UsingList, + pt::VariableAttribute, + // pt::Visibility, + pt::YulExpression, + pt::YulStatement, + pt::YulSwitchOptions, +} diff --git a/crates/fmt-2/src/solang_ext/mod.rs b/crates/fmt-2/src/solang_ext/mod.rs new file mode 100644 index 0000000000000..aa4fe734ee64f --- /dev/null +++ b/crates/fmt-2/src/solang_ext/mod.rs @@ -0,0 +1,28 @@ +//! Extension traits and modules to the [`solang_parser`] crate. + +/// Same as [`solang_parser::pt`], but with the patched `CodeLocation`. +pub mod pt { + #[doc(no_inline)] + pub use super::loc::CodeLocationExt as CodeLocation; + + #[doc(no_inline)] + pub use solang_parser::pt::{ + Annotation, Base, CatchClause, Comment, ContractDefinition, ContractPart, ContractTy, + EnumDefinition, ErrorDefinition, ErrorParameter, EventDefinition, EventParameter, + Expression, FunctionAttribute, FunctionDefinition, FunctionTy, HexLiteral, Identifier, + IdentifierPath, Import, ImportPath, Loc, Mutability, NamedArgument, OptionalCodeLocation, + Parameter, ParameterList, SourceUnit, SourceUnitPart, Statement, StorageLocation, + StringLiteral, StructDefinition, Type, TypeDefinition, UserDefinedOperator, Using, + UsingFunction, UsingList, VariableAttribute, VariableDeclaration, VariableDefinition, + Visibility, YulBlock, YulExpression, YulFor, YulFunctionCall, YulFunctionDefinition, + YulStatement, YulSwitch, YulSwitchOptions, YulTypedIdentifier, + }; +} + +mod ast_eq; +mod loc; +mod safe_unwrap; + +pub use ast_eq::AstEq; +pub use loc::CodeLocationExt; +pub use safe_unwrap::SafeUnwrap; diff --git a/crates/fmt-2/src/solang_ext/safe_unwrap.rs b/crates/fmt-2/src/solang_ext/safe_unwrap.rs new file mode 100644 index 0000000000000..fe2810ad9705a --- /dev/null +++ b/crates/fmt-2/src/solang_ext/safe_unwrap.rs @@ -0,0 +1,52 @@ +use solang_parser::pt; + +/// Trait implemented to unwrap optional parse tree items initially introduced in +/// [hyperledger/solang#1068]. +/// +/// Note that the methods of this trait should only be used on parse tree items' fields, like +/// [pt::VariableDefinition] or [pt::EventDefinition], where the `name` field is `None` only when an +/// error occurred during parsing. +/// +/// [hyperledger/solang#1068]: https://github.com/hyperledger/solang/pull/1068 +pub trait SafeUnwrap { + /// See [SafeUnwrap]. + fn safe_unwrap(&self) -> &T; + + /// See [SafeUnwrap]. + fn safe_unwrap_mut(&mut self) -> &mut T; +} + +#[inline(never)] +#[cold] +#[track_caller] +fn invalid() -> ! { + panic!("invalid parse tree") +} + +macro_rules! impl_ { + ($($t:ty),+ $(,)?) => { + $( + impl SafeUnwrap<$t> for Option<$t> { + #[inline] + #[track_caller] + fn safe_unwrap(&self) -> &$t { + match *self { + Some(ref x) => x, + None => invalid(), + } + } + + #[inline] + #[track_caller] + fn safe_unwrap_mut(&mut self) -> &mut $t { + match *self { + Some(ref mut x) => x, + None => invalid(), + } + } + } + )+ + }; +} + +impl_!(pt::Identifier, pt::StringLiteral); diff --git a/crates/fmt-2/src/string.rs b/crates/fmt-2/src/string.rs new file mode 100644 index 0000000000000..ae570a39b827a --- /dev/null +++ b/crates/fmt-2/src/string.rs @@ -0,0 +1,181 @@ +//! Helpers for dealing with quoted strings + +/// The state of a character in a string with quotable components +/// This is a simplified version of the +/// [actual parser](https://docs.soliditylang.org/en/v0.8.15/grammar.html#a4.SolidityLexer.EscapeSequence) +/// as we don't care about hex or other character meanings +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum QuoteState { + /// Not currently in quoted string + #[default] + None, + /// The opening character of a quoted string + Opening(char), + /// A character in a quoted string + String(char), + /// The `\` in an escape sequence `"\n"` + Escaping(char), + /// The escaped character e.g. `n` in `"\n"` + Escaped(char), + /// The closing character + Closing(char), +} + +/// An iterator over characters and indices in a string slice with information about quoted string +/// states +pub struct QuoteStateCharIndices<'a> { + iter: std::str::CharIndices<'a>, + state: QuoteState, +} + +impl<'a> QuoteStateCharIndices<'a> { + fn new(string: &'a str) -> Self { + Self { iter: string.char_indices(), state: QuoteState::None } + } + pub fn with_state(mut self, state: QuoteState) -> Self { + self.state = state; + self + } +} + +impl Iterator for QuoteStateCharIndices<'_> { + type Item = (QuoteState, usize, char); + fn next(&mut self) -> Option { + let (idx, ch) = self.iter.next()?; + match self.state { + QuoteState::None | QuoteState::Closing(_) => { + if ch == '\'' || ch == '"' { + self.state = QuoteState::Opening(ch); + } else { + self.state = QuoteState::None + } + } + QuoteState::String(quote) | QuoteState::Opening(quote) | QuoteState::Escaped(quote) => { + if ch == quote { + self.state = QuoteState::Closing(quote) + } else if ch == '\\' { + self.state = QuoteState::Escaping(quote) + } else { + self.state = QuoteState::String(quote) + } + } + QuoteState::Escaping(quote) => self.state = QuoteState::Escaped(quote), + } + Some((self.state, idx, ch)) + } +} + +/// An iterator over the indices of quoted string locations +pub struct QuotedRanges<'a>(QuoteStateCharIndices<'a>); + +impl QuotedRanges<'_> { + pub fn with_state(mut self, state: QuoteState) -> Self { + self.0 = self.0.with_state(state); + self + } +} + +impl Iterator for QuotedRanges<'_> { + type Item = (char, usize, usize); + fn next(&mut self) -> Option { + let (quote, start) = loop { + let (state, idx, _) = self.0.next()?; + match state { + QuoteState::Opening(quote) | + QuoteState::Escaping(quote) | + QuoteState::Escaped(quote) | + QuoteState::String(quote) => break (quote, idx), + QuoteState::Closing(quote) => return Some((quote, idx, idx)), + QuoteState::None => {} + } + }; + for (state, idx, _) in self.0.by_ref() { + if matches!(state, QuoteState::Closing(_)) { + return Some((quote, start, idx)) + } + } + None + } +} + +/// Helpers for iterating over quoted strings +pub trait QuotedStringExt { + /// Returns an iterator of characters, indices and their quoted string state. + fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_>; + + /// Returns an iterator of quoted string ranges. + fn quoted_ranges(&self) -> QuotedRanges<'_> { + QuotedRanges(self.quote_state_char_indices()) + } + + /// Check to see if a string is quoted. This will return true if the first character + /// is a quote and the last character is a quote with no non-quoted sections in between. + fn is_quoted(&self) -> bool { + let mut iter = self.quote_state_char_indices(); + if !matches!(iter.next(), Some((QuoteState::Opening(_), _, _))) { + return false + } + while let Some((state, _, _)) = iter.next() { + if matches!(state, QuoteState::Closing(_)) { + return iter.next().is_none() + } + } + false + } +} + +impl QuotedStringExt for T +where + T: AsRef, +{ + fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_> { + QuoteStateCharIndices::new(self.as_ref()) + } +} + +impl QuotedStringExt for str { + fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_> { + QuoteStateCharIndices::new(self) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use similar_asserts::assert_eq; + + #[test] + fn quote_state_char_indices() { + assert_eq!( + r#"a'a"\'\"\n\\'a"#.quote_state_char_indices().collect::>(), + vec![ + (QuoteState::None, 0, 'a'), + (QuoteState::Opening('\''), 1, '\''), + (QuoteState::String('\''), 2, 'a'), + (QuoteState::String('\''), 3, '"'), + (QuoteState::Escaping('\''), 4, '\\'), + (QuoteState::Escaped('\''), 5, '\''), + (QuoteState::Escaping('\''), 6, '\\'), + (QuoteState::Escaped('\''), 7, '"'), + (QuoteState::Escaping('\''), 8, '\\'), + (QuoteState::Escaped('\''), 9, 'n'), + (QuoteState::Escaping('\''), 10, '\\'), + (QuoteState::Escaped('\''), 11, '\\'), + (QuoteState::Closing('\''), 12, '\''), + (QuoteState::None, 13, 'a'), + ] + ); + } + + #[test] + fn quoted_ranges() { + let string = r#"testing "double quoted" and 'single quoted' strings"#; + assert_eq!( + string + .quoted_ranges() + .map(|(quote, start, end)| (quote, &string[start..=end])) + .collect::>(), + vec![('"', r#""double quoted""#), ('\'', "'single quoted'")] + ); + } +} diff --git a/crates/fmt-2/src/visit.rs b/crates/fmt-2/src/visit.rs new file mode 100644 index 0000000000000..db6287b72209d --- /dev/null +++ b/crates/fmt-2/src/visit.rs @@ -0,0 +1,642 @@ +//! Visitor helpers to traverse the [solang Solidity Parse Tree](solang_parser::pt). + +use crate::solang_ext::pt::*; + +/// A trait that is invoked while traversing the Solidity Parse Tree. +/// Each method of the [Visitor] trait is a hook that can be potentially overridden. +/// +/// Currently the main implementer of this trait is the [`Formatter`](crate::Formatter<'_>) struct. +pub trait Visitor { + type Error: std::error::Error; + + fn visit_source(&mut self, _loc: Loc) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_source_unit(&mut self, _source_unit: &mut SourceUnit) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_contract(&mut self, _contract: &mut ContractDefinition) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_annotation(&mut self, annotation: &mut Annotation) -> Result<(), Self::Error> { + self.visit_source(annotation.loc) + } + + fn visit_pragma( + &mut self, + loc: Loc, + _ident: &mut Option, + _str: &mut Option, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_import_plain( + &mut self, + _loc: Loc, + _import: &mut ImportPath, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_import_global( + &mut self, + _loc: Loc, + _global: &mut ImportPath, + _alias: &mut Identifier, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_import_renames( + &mut self, + _loc: Loc, + _imports: &mut [(Identifier, Option)], + _from: &mut ImportPath, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_enum(&mut self, _enum: &mut EnumDefinition) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_assembly( + &mut self, + loc: Loc, + _dialect: &mut Option, + _block: &mut YulBlock, + _flags: &mut Option>, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_block( + &mut self, + loc: Loc, + _unchecked: bool, + _statements: &mut Vec, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_args(&mut self, loc: Loc, _args: &mut Vec) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + /// Don't write semicolon at the end because expressions can appear as both + /// part of other node and a statement in the function body + fn visit_expr(&mut self, loc: Loc, _expr: &mut Expression) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_ident(&mut self, loc: Loc, _ident: &mut Identifier) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_ident_path(&mut self, idents: &mut IdentifierPath) -> Result<(), Self::Error> { + self.visit_source(idents.loc) + } + + fn visit_emit(&mut self, loc: Loc, _event: &mut Expression) -> Result<(), Self::Error> { + self.visit_source(loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<(), Self::Error> { + self.visit_source(var.loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_var_definition_stmt( + &mut self, + loc: Loc, + _declaration: &mut VariableDeclaration, + _expr: &mut Option, + ) -> Result<(), Self::Error> { + self.visit_source(loc)?; + self.visit_stray_semicolon() + } + + fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<(), Self::Error> { + self.visit_source(var.loc) + } + + fn visit_return( + &mut self, + loc: Loc, + _expr: &mut Option, + ) -> Result<(), Self::Error> { + self.visit_source(loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_revert( + &mut self, + loc: Loc, + _error: &mut Option, + _args: &mut Vec, + ) -> Result<(), Self::Error> { + self.visit_source(loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_revert_named_args( + &mut self, + loc: Loc, + _error: &mut Option, + _args: &mut Vec, + ) -> Result<(), Self::Error> { + self.visit_source(loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_break(&mut self, loc: Loc, _semicolon: bool) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_continue(&mut self, loc: Loc, _semicolon: bool) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + #[expect(clippy::type_complexity)] + fn visit_try( + &mut self, + loc: Loc, + _expr: &mut Expression, + _returns: &mut Option<(Vec<(Loc, Option)>, Box)>, + _clauses: &mut Vec, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_if( + &mut self, + loc: Loc, + _cond: &mut Expression, + _if_branch: &mut Box, + _else_branch: &mut Option>, + _is_first_stmt: bool, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_do_while( + &mut self, + loc: Loc, + _body: &mut Statement, + _cond: &mut Expression, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_while( + &mut self, + loc: Loc, + _cond: &mut Expression, + _body: &mut Statement, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_for( + &mut self, + loc: Loc, + _init: &mut Option>, + _cond: &mut Option>, + _update: &mut Option>, + _body: &mut Option>, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<(), Self::Error> { + self.visit_source(func.loc())?; + if func.body.is_none() { + self.visit_stray_semicolon()?; + } + + Ok(()) + } + + fn visit_function_attribute( + &mut self, + attribute: &mut FunctionAttribute, + ) -> Result<(), Self::Error> { + self.visit_source(attribute.loc())?; + Ok(()) + } + + fn visit_var_attribute( + &mut self, + attribute: &mut VariableAttribute, + ) -> Result<(), Self::Error> { + self.visit_source(attribute.loc())?; + Ok(()) + } + + fn visit_base(&mut self, base: &mut Base) -> Result<(), Self::Error> { + self.visit_source(base.loc) + } + + fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<(), Self::Error> { + self.visit_source(parameter.loc) + } + + fn visit_struct(&mut self, structure: &mut StructDefinition) -> Result<(), Self::Error> { + self.visit_source(structure.loc)?; + + Ok(()) + } + + fn visit_event(&mut self, event: &mut EventDefinition) -> Result<(), Self::Error> { + self.visit_source(event.loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_event_parameter(&mut self, param: &mut EventParameter) -> Result<(), Self::Error> { + self.visit_source(param.loc) + } + + fn visit_error(&mut self, error: &mut ErrorDefinition) -> Result<(), Self::Error> { + self.visit_source(error.loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_error_parameter(&mut self, param: &mut ErrorParameter) -> Result<(), Self::Error> { + self.visit_source(param.loc) + } + + fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<(), Self::Error> { + self.visit_source(def.loc) + } + + fn visit_stray_semicolon(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_opening_paren(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_closing_paren(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_newline(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + fn visit_using(&mut self, using: &mut Using) -> Result<(), Self::Error> { + self.visit_source(using.loc)?; + self.visit_stray_semicolon()?; + + Ok(()) + } + + fn visit_yul_block( + &mut self, + loc: Loc, + _stmts: &mut Vec, + _attempt_single_line: bool, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_yul_expr(&mut self, expr: &mut YulExpression) -> Result<(), Self::Error> { + self.visit_source(expr.loc()) + } + + fn visit_yul_assignment( + &mut self, + loc: Loc, + _exprs: &mut Vec, + _expr: &mut Option<&mut YulExpression>, + ) -> Result<(), Self::Error> + where + T: Visitable + CodeLocation, + { + self.visit_source(loc) + } + + fn visit_yul_for(&mut self, stmt: &mut YulFor) -> Result<(), Self::Error> { + self.visit_source(stmt.loc) + } + + fn visit_yul_function_call(&mut self, stmt: &mut YulFunctionCall) -> Result<(), Self::Error> { + self.visit_source(stmt.loc) + } + + fn visit_yul_fun_def(&mut self, stmt: &mut YulFunctionDefinition) -> Result<(), Self::Error> { + self.visit_source(stmt.loc) + } + + fn visit_yul_if( + &mut self, + loc: Loc, + _expr: &mut YulExpression, + _block: &mut YulBlock, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_yul_leave(&mut self, loc: Loc) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_yul_switch(&mut self, stmt: &mut YulSwitch) -> Result<(), Self::Error> { + self.visit_source(stmt.loc) + } + + fn visit_yul_var_declaration( + &mut self, + loc: Loc, + _idents: &mut Vec, + _expr: &mut Option, + ) -> Result<(), Self::Error> { + self.visit_source(loc) + } + + fn visit_yul_typed_ident(&mut self, ident: &mut YulTypedIdentifier) -> Result<(), Self::Error> { + self.visit_source(ident.loc) + } + + fn visit_parser_error(&mut self, loc: Loc) -> Result<(), Self::Error> { + self.visit_source(loc) + } +} + +/// Visitable trait for [`solang_parser::pt`] types. +/// +/// All [`solang_parser::pt`] types, such as [Statement], should implement the [Visitable] trait +/// that accepts a trait [Visitor] implementation, which has various callback handles for Solidity +/// Parse Tree nodes. +/// +/// We want to take a `&mut self` to be able to implement some advanced features in the future such +/// as modifying the Parse Tree before formatting it. +pub trait Visitable { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor; +} + +impl Visitable for &mut T +where + T: Visitable, +{ + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + T::visit(self, v) + } +} + +impl Visitable for Option +where + T: Visitable, +{ + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + if let Some(inner) = self.as_mut() { + inner.visit(v) + } else { + Ok(()) + } + } +} + +impl Visitable for Box +where + T: Visitable, +{ + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + T::visit(self, v) + } +} + +impl Visitable for Vec +where + T: Visitable, +{ + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + for item in self.iter_mut() { + item.visit(v)?; + } + Ok(()) + } +} + +impl Visitable for SourceUnitPart { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + match self { + Self::ContractDefinition(contract) => v.visit_contract(contract), + Self::PragmaDirective(loc, ident, str) => v.visit_pragma(*loc, ident, str), + Self::ImportDirective(import) => import.visit(v), + Self::EnumDefinition(enumeration) => v.visit_enum(enumeration), + Self::StructDefinition(structure) => v.visit_struct(structure), + Self::EventDefinition(event) => v.visit_event(event), + Self::ErrorDefinition(error) => v.visit_error(error), + Self::FunctionDefinition(function) => v.visit_function(function), + Self::VariableDefinition(variable) => v.visit_var_definition(variable), + Self::TypeDefinition(def) => v.visit_type_definition(def), + Self::StraySemicolon(_) => v.visit_stray_semicolon(), + Self::Using(using) => v.visit_using(using), + Self::Annotation(annotation) => v.visit_annotation(annotation), + } + } +} + +impl Visitable for Import { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + match self { + Self::Plain(import, loc) => v.visit_import_plain(*loc, import), + Self::GlobalSymbol(global, import_as, loc) => { + v.visit_import_global(*loc, global, import_as) + } + Self::Rename(from, imports, loc) => v.visit_import_renames(*loc, imports, from), + } + } +} + +impl Visitable for ContractPart { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + match self { + Self::StructDefinition(structure) => v.visit_struct(structure), + Self::EventDefinition(event) => v.visit_event(event), + Self::ErrorDefinition(error) => v.visit_error(error), + Self::EnumDefinition(enumeration) => v.visit_enum(enumeration), + Self::VariableDefinition(variable) => v.visit_var_definition(variable), + Self::FunctionDefinition(function) => v.visit_function(function), + Self::TypeDefinition(def) => v.visit_type_definition(def), + Self::StraySemicolon(_) => v.visit_stray_semicolon(), + Self::Using(using) => v.visit_using(using), + Self::Annotation(annotation) => v.visit_annotation(annotation), + } + } +} + +impl Visitable for Statement { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + match self { + Self::Block { loc, unchecked, statements } => { + v.visit_block(*loc, *unchecked, statements) + } + Self::Assembly { loc, dialect, block, flags } => { + v.visit_assembly(*loc, dialect, block, flags) + } + Self::Args(loc, args) => v.visit_args(*loc, args), + Self::If(loc, cond, if_branch, else_branch) => { + v.visit_if(*loc, cond, if_branch, else_branch, true) + } + Self::While(loc, cond, body) => v.visit_while(*loc, cond, body), + Self::Expression(loc, expr) => { + v.visit_expr(*loc, expr)?; + v.visit_stray_semicolon() + } + Self::VariableDefinition(loc, declaration, expr) => { + v.visit_var_definition_stmt(*loc, declaration, expr) + } + Self::For(loc, init, cond, update, body) => v.visit_for(*loc, init, cond, update, body), + Self::DoWhile(loc, body, cond) => v.visit_do_while(*loc, body, cond), + Self::Continue(loc) => v.visit_continue(*loc, true), + Self::Break(loc) => v.visit_break(*loc, true), + Self::Return(loc, expr) => v.visit_return(*loc, expr), + Self::Revert(loc, error, args) => v.visit_revert(*loc, error, args), + Self::RevertNamedArgs(loc, error, args) => v.visit_revert_named_args(*loc, error, args), + Self::Emit(loc, event) => v.visit_emit(*loc, event), + Self::Try(loc, expr, returns, clauses) => v.visit_try(*loc, expr, returns, clauses), + Self::Error(loc) => v.visit_parser_error(*loc), + } + } +} + +impl Visitable for Loc { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + v.visit_source(*self) + } +} + +impl Visitable for Expression { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + v.visit_expr(self.loc(), self) + } +} + +impl Visitable for Identifier { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + v.visit_ident(self.loc, self) + } +} + +impl Visitable for VariableDeclaration { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + v.visit_var_declaration(self) + } +} + +impl Visitable for YulBlock { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + v.visit_yul_block(self.loc, self.statements.as_mut(), false) + } +} + +impl Visitable for YulStatement { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + match self { + Self::Assign(loc, exprs, expr) => v.visit_yul_assignment(*loc, exprs, &mut Some(expr)), + Self::Block(block) => v.visit_yul_block(block.loc, block.statements.as_mut(), false), + Self::Break(loc) => v.visit_break(*loc, false), + Self::Continue(loc) => v.visit_continue(*loc, false), + Self::For(stmt) => v.visit_yul_for(stmt), + Self::FunctionCall(stmt) => v.visit_yul_function_call(stmt), + Self::FunctionDefinition(stmt) => v.visit_yul_fun_def(stmt), + Self::If(loc, expr, block) => v.visit_yul_if(*loc, expr, block), + Self::Leave(loc) => v.visit_yul_leave(*loc), + Self::Switch(stmt) => v.visit_yul_switch(stmt), + Self::VariableDeclaration(loc, idents, expr) => { + v.visit_yul_var_declaration(*loc, idents, expr) + } + Self::Error(loc) => v.visit_parser_error(*loc), + } + } +} + +macro_rules! impl_visitable { + ($type:ty, $func:ident) => { + impl Visitable for $type { + fn visit(&mut self, v: &mut V) -> Result<(), V::Error> + where + V: Visitor, + { + v.$func(self) + } + } + }; +} + +impl_visitable!(SourceUnit, visit_source_unit); +impl_visitable!(FunctionAttribute, visit_function_attribute); +impl_visitable!(VariableAttribute, visit_var_attribute); +impl_visitable!(Parameter, visit_parameter); +impl_visitable!(Base, visit_base); +impl_visitable!(EventParameter, visit_event_parameter); +impl_visitable!(ErrorParameter, visit_error_parameter); +impl_visitable!(IdentifierPath, visit_ident_path); +impl_visitable!(YulExpression, visit_yul_expr); +impl_visitable!(YulTypedIdentifier, visit_yul_typed_ident); diff --git a/crates/fmt-2/testdata/Annotation/fmt.sol b/crates/fmt-2/testdata/Annotation/fmt.sol new file mode 100644 index 0000000000000..75bbcf2dd3521 --- /dev/null +++ b/crates/fmt-2/testdata/Annotation/fmt.sol @@ -0,0 +1,15 @@ +// Support for Solana/Substrate annotations +contract A { + @selector([1, 2, 3, 4]) + function foo() public {} + + @selector("another one") + function bar() public {} + + @first("") + @second("") + function foobar() public {} +} + +@topselector(2) +contract B {} diff --git a/crates/fmt-2/testdata/Annotation/original.sol b/crates/fmt-2/testdata/Annotation/original.sol new file mode 100644 index 0000000000000..4551f7d1e6bec --- /dev/null +++ b/crates/fmt-2/testdata/Annotation/original.sol @@ -0,0 +1,15 @@ +// Support for Solana/Substrate annotations +contract A { + @selector([1,2,3,4]) + function foo() public {} + + @selector("another one") + function bar() public {} + + @first("") + @second("") + function foobar() public {} +} + +@topselector(2) +contract B {} diff --git a/crates/fmt-2/testdata/ArrayExpressions/fmt.sol b/crates/fmt-2/testdata/ArrayExpressions/fmt.sol new file mode 100644 index 0000000000000..adda7a30e098d --- /dev/null +++ b/crates/fmt-2/testdata/ArrayExpressions/fmt.sol @@ -0,0 +1,69 @@ +contract ArrayExpressions { + function test() external { + /* ARRAY SUBSCRIPT */ + uint256[10] memory sample; + + uint256 length = 10; + uint256[] memory sample2 = new uint256[](length); + + uint256[] /* comment1 */ memory /* comment2 */ sample3; // comment3 + + /* ARRAY SLICE */ + msg.data[4:]; + msg.data[:msg.data.length]; + msg.data[4:msg.data.length]; + + msg.data[ + // comment1 + 4: + ]; + msg.data[ + : /* comment2 */ msg.data.length // comment3 + ]; + msg.data[ + // comment4 + 4: // comment5 + msg.data.length /* comment6 */ + ]; + + uint256 + someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice = + 4; + uint256 + someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice = + msg.data.length; + msg.data[ + someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice: + ]; + msg.data[ + :someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice + ]; + msg.data[ + someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice: + someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice + ]; + + /* ARRAY LITERAL */ + [1, 2, 3]; + + uint256 someVeryVeryLongVariableName = 0; + [ + someVeryVeryLongVariableName, + someVeryVeryLongVariableName, + someVeryVeryLongVariableName + ]; + uint256[3] memory literal = [ + someVeryVeryLongVariableName, + someVeryVeryLongVariableName, + someVeryVeryLongVariableName + ]; + + uint8[3] memory literal2 = /* comment7 */ [ // comment8 + 1, + 2, /* comment9 */ + 3 // comment10 + ]; + uint256[1] memory literal3 = + [ /* comment11 */ someVeryVeryLongVariableName /* comment13 */ ]; + } +} diff --git a/crates/fmt-2/testdata/ArrayExpressions/original.sol b/crates/fmt-2/testdata/ArrayExpressions/original.sol new file mode 100644 index 0000000000000..919cd241fdaed --- /dev/null +++ b/crates/fmt-2/testdata/ArrayExpressions/original.sol @@ -0,0 +1,47 @@ +contract ArrayExpressions { + function test() external { + /* ARRAY SUBSCRIPT */ + uint[10] memory sample; + + uint256 length = 10; + uint[] memory sample2 = new uint[]( + length); + + uint /* comment1 */ [] memory /* comment2 */ sample3 // comment3 + ; + + /* ARRAY SLICE */ + msg.data[4:]; + msg.data[:msg.data.length]; + msg.data[4:msg.data.length]; + + msg.data[ + // comment1 + 4:]; + msg.data[ + : /* comment2 */ msg.data.length // comment3 + ]; + msg.data[ + // comment4 + 4 // comment5 + :msg.data.length /* comment6 */]; + + uint256 someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice = 4; + uint256 someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice = msg.data.length; + msg.data[someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice:]; + msg.data[:someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice]; + msg.data[someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice:someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice]; + + /* ARRAY LITERAL */ + [1, 2, 3]; + + uint256 someVeryVeryLongVariableName = 0; + [someVeryVeryLongVariableName, someVeryVeryLongVariableName, someVeryVeryLongVariableName]; + uint256[3] memory literal = [someVeryVeryLongVariableName,someVeryVeryLongVariableName,someVeryVeryLongVariableName]; + + uint8[3] memory literal2 = /* comment7 */ [ // comment8 + 1, 2, /* comment9 */ 3 // comment10 + ]; + uint256[1] memory literal3 = [ /* comment11 */ someVeryVeryLongVariableName /* comment13 */]; + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/BlockComments/fmt.sol b/crates/fmt-2/testdata/BlockComments/fmt.sol new file mode 100644 index 0000000000000..1d7025f2ad591 --- /dev/null +++ b/crates/fmt-2/testdata/BlockComments/fmt.sol @@ -0,0 +1,25 @@ +contract CounterTest is Test { + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev See {IERC721-balanceOf}. + */ + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } +} diff --git a/crates/fmt-2/testdata/BlockComments/original.sol b/crates/fmt-2/testdata/BlockComments/original.sol new file mode 100644 index 0000000000000..b91934bf7d12b --- /dev/null +++ b/crates/fmt-2/testdata/BlockComments/original.sol @@ -0,0 +1,26 @@ +contract CounterTest is Test { + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev See {IERC721-balanceOf}. + */ + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); +} + +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol b/crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol new file mode 100644 index 0000000000000..368749bf4fdaf --- /dev/null +++ b/crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol @@ -0,0 +1,20 @@ +contract A { + Counter public counter; + /** + * TODO: this fuzz use too much time to execute + * function testGetFuzz(bytes[2][] memory kvs) public { + * for (uint256 i = 0; i < kvs.length; i++) { + * bytes32 root = trie.update(kvs[i][0], kvs[i][1]); + * console.logBytes32(root); + * } + * + * for (uint256 i = 0; i < kvs.length; i++) { + * (bool exist, bytes memory value) = trie.get(kvs[i][0]); + * console.logBool(exist); + * console.logBytes(value); + * require(exist); + * require(BytesSlice.equal(value, trie.getRaw(kvs[i][0]))); + * } + * } + */ +} diff --git a/crates/fmt-2/testdata/BlockCommentsFunction/original.sol b/crates/fmt-2/testdata/BlockCommentsFunction/original.sol new file mode 100644 index 0000000000000..089f1bac430cd --- /dev/null +++ b/crates/fmt-2/testdata/BlockCommentsFunction/original.sol @@ -0,0 +1,20 @@ +contract A { + Counter public counter; + /** + * TODO: this fuzz use too much time to execute + function testGetFuzz(bytes[2][] memory kvs) public { + for (uint256 i = 0; i < kvs.length; i++) { + bytes32 root = trie.update(kvs[i][0], kvs[i][1]); + console.logBytes32(root); + } + + for (uint256 i = 0; i < kvs.length; i++) { + (bool exist, bytes memory value) = trie.get(kvs[i][0]); + console.logBool(exist); + console.logBytes(value); + require(exist); + require(BytesSlice.equal(value, trie.getRaw(kvs[i][0]))); + } + } + */ +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol b/crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol new file mode 100644 index 0000000000000..733ae2d8ca6a1 --- /dev/null +++ b/crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol @@ -0,0 +1,37 @@ +contract TernaryExpression { + function test() external { + bool condition; + bool someVeryVeryLongConditionUsedInTheTernaryExpression; + + condition ? 0 : 1; + + someVeryVeryLongConditionUsedInTheTernaryExpression + ? 1234567890 + : 987654321; + + condition /* comment1 */ /* comment2 */ + ? 1001 /* comment3 */ /* comment4 */ + : 2002; + + // comment5 + someVeryVeryLongConditionUsedInTheTernaryExpression + ? 1 + // comment6 + // comment7 + : 0; // comment8 + + uint256 amount = msg.value > 0 + ? msg.value + : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); + + uint256 amount = msg.value > 0 + ? msg.value + // comment9 + : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); + + uint256 amount = msg.value > 0 + // comment10 + ? msg.value + : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); + } +} diff --git a/crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol b/crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol new file mode 100644 index 0000000000000..f03328873d6c4 --- /dev/null +++ b/crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol @@ -0,0 +1,33 @@ +contract TernaryExpression { + function test() external { + bool condition; + bool someVeryVeryLongConditionUsedInTheTernaryExpression; + + condition ? 0 : 1; + + someVeryVeryLongConditionUsedInTheTernaryExpression ? 1234567890 : 987654321; + + condition /* comment1 */ ? /* comment2 */ 1001 /* comment3 */ : /* comment4 */ 2002; + + // comment5 + someVeryVeryLongConditionUsedInTheTernaryExpression ? 1 + // comment6 + : + // comment7 + 0; // comment8 + + uint256 amount = msg.value > 0 + ? msg.value + : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); + + uint256 amount = msg.value > 0 + ? msg.value + // comment9 + : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); + + uint amount = msg.value > 0 + // comment10 + ? msg.value + : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ConstructorDefinition/fmt.sol b/crates/fmt-2/testdata/ConstructorDefinition/fmt.sol new file mode 100644 index 0000000000000..dd62a0cf43611 --- /dev/null +++ b/crates/fmt-2/testdata/ConstructorDefinition/fmt.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.5.2; + +// comment block starts here +// comment block continues +// + +// comment block 2 starts here +// comment block 2 continues + +contract Constructors is Ownable, Changeable { + function Constructors(variable1) + public + Changeable(variable1) + Ownable() + onlyOwner + {} + + constructor( + variable1, + variable2, + variable3, + variable4, + variable5, + variable6, + variable7 + ) + public + Changeable( + variable1, + variable2, + variable3, + variable4, + variable5, + variable6, + variable7 + ) + Ownable() + onlyOwner + {} +} diff --git a/crates/fmt-2/testdata/ConstructorDefinition/original.sol b/crates/fmt-2/testdata/ConstructorDefinition/original.sol new file mode 100644 index 0000000000000..f69205196ffd1 --- /dev/null +++ b/crates/fmt-2/testdata/ConstructorDefinition/original.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.5.2; + +// comment block starts here +// comment block continues +// + +// comment block 2 starts here +// comment block 2 continues + +contract Constructors is Ownable, Changeable { + function Constructors(variable1) public Changeable(variable1) Ownable() onlyOwner { + } + + constructor(variable1, variable2, variable3, variable4, variable5, variable6, variable7) public Changeable(variable1, variable2, variable3, variable4, variable5, variable6, variable7) Ownable() onlyOwner {} +} diff --git a/crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol b/crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol new file mode 100644 index 0000000000000..88694860aded2 --- /dev/null +++ b/crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.5.2; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ERC1155} from "solmate/tokens/ERC1155.sol"; + +import {IAchievements} from "./interfaces/IAchievements.sol"; +import {SoulBound1155} from "./abstracts/SoulBound1155.sol"; + +contract Achievements is IAchievements, SoulBound1155, Ownable { + constructor(address owner) Ownable() ERC1155() {} +} diff --git a/crates/fmt-2/testdata/ConstructorModifierStyle/original.sol b/crates/fmt-2/testdata/ConstructorModifierStyle/original.sol new file mode 100644 index 0000000000000..88694860aded2 --- /dev/null +++ b/crates/fmt-2/testdata/ConstructorModifierStyle/original.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.5.2; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ERC1155} from "solmate/tokens/ERC1155.sol"; + +import {IAchievements} from "./interfaces/IAchievements.sol"; +import {SoulBound1155} from "./abstracts/SoulBound1155.sol"; + +contract Achievements is IAchievements, SoulBound1155, Ownable { + constructor(address owner) Ownable() ERC1155() {} +} diff --git a/crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..dca4e325d39c9 --- /dev/null +++ b/crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol @@ -0,0 +1,37 @@ +// config: line_length = 160 +// config: bracket_spacing = true +contract ContractDefinition is Contract1, Contract2, Contract3, Contract4, Contract5 { } + +// comment 7 +contract SampleContract { + // spaced comment 1 + + // spaced comment 2 + // that spans multiple lines + + // comment 8 + constructor() { /* comment 9 */ } // comment 10 + + // comment 11 + function max( /* comment 13 */ uint256 arg1, uint256 /* comment 14 */ arg2, uint256 /* comment 15 */ ) + // comment 16 + external /* comment 17 */ + pure + returns (uint256) + // comment 18 + { + // comment 19 + return arg1 > arg2 ? arg1 : arg2; + } +} + +// comment 20 +contract /* comment 21 */ ExampleContract is /* comment 22 */ SampleContract { } + +contract ERC20DecimalsMock is ERC20 { + uint8 private immutable _decimals; + + constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { + _decimals = decimals_; + } +} diff --git a/crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol b/crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol new file mode 100644 index 0000000000000..2e9661f956dcf --- /dev/null +++ b/crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol @@ -0,0 +1,52 @@ +// config: contract_new_lines = true +contract ContractDefinition is + Contract1, + Contract2, + Contract3, + Contract4, + Contract5 +{} + +// comment 7 +contract SampleContract { + + // spaced comment 1 + + // spaced comment 2 + // that spans multiple lines + + // comment 8 + constructor() { /* comment 9 */ } // comment 10 + + // comment 11 + function max( /* comment 13 */ + uint256 arg1, + uint256 /* comment 14 */ arg2, + uint256 /* comment 15 */ + ) + // comment 16 + external /* comment 17 */ + pure + returns (uint256) + // comment 18 + { + // comment 19 + return arg1 > arg2 ? arg1 : arg2; + } + +} + +// comment 20 +contract /* comment 21 */ ExampleContract is /* comment 22 */ SampleContract {} + +contract ERC20DecimalsMock is ERC20 { + + uint8 private immutable _decimals; + + constructor(string memory name_, string memory symbol_, uint8 decimals_) + ERC20(name_, symbol_) + { + _decimals = decimals_; + } + +} diff --git a/crates/fmt-2/testdata/ContractDefinition/fmt.sol b/crates/fmt-2/testdata/ContractDefinition/fmt.sol new file mode 100644 index 0000000000000..551e84decfc5b --- /dev/null +++ b/crates/fmt-2/testdata/ContractDefinition/fmt.sol @@ -0,0 +1,47 @@ +contract ContractDefinition is + Contract1, + Contract2, + Contract3, + Contract4, + Contract5 +{} + +// comment 7 +contract SampleContract { + // spaced comment 1 + + // spaced comment 2 + // that spans multiple lines + + // comment 8 + constructor() { /* comment 9 */ } // comment 10 + + // comment 11 + function max( /* comment 13 */ + uint256 arg1, + uint256 /* comment 14 */ arg2, + uint256 /* comment 15 */ + ) + // comment 16 + external /* comment 17 */ + pure + returns (uint256) + // comment 18 + { + // comment 19 + return arg1 > arg2 ? arg1 : arg2; + } +} + +// comment 20 +contract /* comment 21 */ ExampleContract is /* comment 22 */ SampleContract {} + +contract ERC20DecimalsMock is ERC20 { + uint8 private immutable _decimals; + + constructor(string memory name_, string memory symbol_, uint8 decimals_) + ERC20(name_, symbol_) + { + _decimals = decimals_; + } +} diff --git a/crates/fmt-2/testdata/ContractDefinition/original.sol b/crates/fmt-2/testdata/ContractDefinition/original.sol new file mode 100644 index 0000000000000..4c671985bda7b --- /dev/null +++ b/crates/fmt-2/testdata/ContractDefinition/original.sol @@ -0,0 +1,40 @@ +contract ContractDefinition is Contract1, Contract2, Contract3, Contract4, Contract5 { +} + +// comment 7 +contract SampleContract { + + // spaced comment 1 + + // spaced comment 2 + // that spans multiple lines + + // comment 8 + constructor() { /* comment 9 */ } // comment 10 + + // comment 11 + function max(/* comment 13 */ uint256 arg1, uint256 /* comment 14 */ arg2, uint256 /* comment 15 */) + // comment 16 + external /* comment 17 */ + pure + returns(uint256) + // comment 18 + { // comment 19 + return arg1 > arg2 ? arg1 : arg2; + } +} + +// comment 20 +contract /* comment 21 */ ExampleContract /* comment 22 */ is SampleContract {} + +contract ERC20DecimalsMock is ERC20 { + uint8 private immutable _decimals; + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_ + ) ERC20(name_, symbol_) { + _decimals = decimals_; + } +} diff --git a/crates/fmt-2/testdata/DoWhileStatement/fmt.sol b/crates/fmt-2/testdata/DoWhileStatement/fmt.sol new file mode 100644 index 0000000000000..c3c8c71c5da91 --- /dev/null +++ b/crates/fmt-2/testdata/DoWhileStatement/fmt.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.8.8; + +contract DoWhileStatement { + function test() external { + uint256 i; + do { + "test"; + } while (i != 0); + + do {} while (i != 0); + + bool someVeryVeryLongCondition; + do { + "test"; + } while ( + someVeryVeryLongCondition && !someVeryVeryLongCondition + && !someVeryVeryLongCondition && someVeryVeryLongCondition + ); + + do { + i++; + } while (i < 10); + + do { + do { + i++; + } while (i < 30); + } while (i < 20); + } +} diff --git a/crates/fmt-2/testdata/DoWhileStatement/original.sol b/crates/fmt-2/testdata/DoWhileStatement/original.sol new file mode 100644 index 0000000000000..51063c8784e10 --- /dev/null +++ b/crates/fmt-2/testdata/DoWhileStatement/original.sol @@ -0,0 +1,24 @@ +pragma solidity ^0.8.8; + + contract DoWhileStatement { + function test() external { + uint256 i; + do { "test"; } while (i != 0); + + do + {} + while + ( + i != 0); + + bool someVeryVeryLongCondition; + do { "test"; } while( + someVeryVeryLongCondition && !someVeryVeryLongCondition && +!someVeryVeryLongCondition && + someVeryVeryLongCondition); + + do i++; while(i < 10); + + do do i++; while (i < 30); while(i < 20); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/DocComments/fmt.sol b/crates/fmt-2/testdata/DocComments/fmt.sol new file mode 100644 index 0000000000000..4248f0fe587da --- /dev/null +++ b/crates/fmt-2/testdata/DocComments/fmt.sol @@ -0,0 +1,100 @@ +pragma solidity ^0.8.13; + +/// @title A Hello world example +contract HelloWorld { + /// Some example struct + struct Person { + uint256 age; + address wallet; + } + + /** + * Here's a more double asterix comment + */ + Person public theDude; + + /// Constructs the dude + /// @param age The dude's age + constructor(uint256 age) { + theDude = Person({age: age, wallet: msg.sender}); + } + + /** + * @dev does nothing + */ + function example() public { + /** + * Does this add a whitespace error? + * + * Let's find out. + */ + } + + /** + * @dev Calculates a rectangle's surface and perimeter. + * @param w Width of the rectangle. + * @param h Height of the rectangle. + * @return s The calculated surface. + * @return p The calculated perimeter. + */ + function rectangle(uint256 w, uint256 h) + public + pure + returns (uint256 s, uint256 p) + { + s = w * h; + p = 2 * (w + h); + } + + /// A long doc line comment that will be wrapped + function docLineOverflow() external {} + + function docLinePostfixOverflow() external {} + + /// A long doc line comment that will be wrapped + + /** + * @notice Here is my comment + * - item 1 + * - item 2 + * Some equations: + * y = mx + b + */ + function anotherExample() external {} + + /** + * contract A { + * function foo() public { + * // does nothing. + * } + * } + */ + function multilineIndent() external {} + + /** + * contract A { + * function foo() public { + * // does nothing. + * } + * } + */ + function multilineMalformedIndent() external {} + + /** + * contract A { + * function withALongNameThatWillCauseCommentWrap() public { + * // does nothing. + * } + * } + */ + function malformedIndentOverflow() external {} +} + +/** + * contract A { + * function foo() public { + * // does nothing. + * } + * } + */ +function freeFloatingMultilineIndent() {} diff --git a/crates/fmt-2/testdata/DocComments/original.sol b/crates/fmt-2/testdata/DocComments/original.sol new file mode 100644 index 0000000000000..28f654b57903d --- /dev/null +++ b/crates/fmt-2/testdata/DocComments/original.sol @@ -0,0 +1,95 @@ +pragma solidity ^0.8.13; + +/// @title A Hello world example +contract HelloWorld { + + /// Some example struct + struct Person { + uint age; + address wallet; + } + + /** + Here's a more double asterix comment + */ + Person public theDude; + + /// Constructs the dude + /// @param age The dude's age + constructor(uint256 age) { + theDude = Person({ + age: age, + wallet: msg.sender + }); + } + + /** @dev does nothing */ + function example() public { + /** + * Does this add a whitespace error? + * + * Let's find out. + */ + } + + /** @dev Calculates a rectangle's surface and perimeter. + * @param w Width of the rectangle. + * @param h Height of the rectangle. + * @return s The calculated surface. +* @return p The calculated perimeter. + */ + function rectangle(uint256 w, uint256 h) public pure returns (uint256 s, uint256 p) { + s = w * h; + p = 2 * (w + h); + } + + /// A long doc line comment that will be wrapped + function docLineOverflow() external {} + + function docLinePostfixOverflow() external {} /// A long doc line comment that will be wrapped + + /** + * @notice Here is my comment + * - item 1 + * - item 2 + * Some equations: + * y = mx + b + */ + function anotherExample() external {} + + /** + contract A { + function foo() public { + // does nothing. + } + } + */ + function multilineIndent() external {} + + /** + contract A { +function foo() public { + // does nothing. + } + } + */ + function multilineMalformedIndent() external {} + + /** + contract A { +function withALongNameThatWillCauseCommentWrap() public { + // does nothing. + } + } + */ + function malformedIndentOverflow() external {} +} + +/** +contract A { + function foo() public { + // does nothing. + } +} +*/ +function freeFloatingMultilineIndent() {} diff --git a/crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol b/crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol new file mode 100644 index 0000000000000..c3c7fe00c9180 --- /dev/null +++ b/crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol @@ -0,0 +1,128 @@ +// config: line_length = 40 +// config: wrap_comments = true +pragma solidity ^0.8.13; + +/// @title A Hello world example +contract HelloWorld { + /// Some example struct + struct Person { + uint256 age; + address wallet; + } + + /** + * Here's a more double asterix + * comment + */ + Person public theDude; + + /// Constructs the dude + /// @param age The dude's age + constructor(uint256 age) { + theDude = Person({ + age: age, + wallet: msg.sender + }); + } + + /** + * @dev does nothing + */ + function example() public { + /** + * Does this add a whitespace + * error? + * + * Let's find out. + */ + } + + /** + * @dev Calculates a rectangle's + * surface and perimeter. + * @param w Width of the rectangle. + * @param h Height of the rectangle. + * @return s The calculated surface. + * @return p The calculated + * perimeter. + */ + function rectangle( + uint256 w, + uint256 h + ) + public + pure + returns (uint256 s, uint256 p) + { + s = w * h; + p = 2 * (w + h); + } + + /// A long doc line comment that + /// will be wrapped + function docLineOverflow() + external + {} + + function docLinePostfixOverflow() + external + {} + + /// A long doc line comment that + /// will be wrapped + + /** + * @notice Here is my comment + * - item 1 + * - item 2 + * Some equations: + * y = mx + b + */ + function anotherExample() + external + {} + + /** + * contract A { + * function foo() public { + * // does nothing. + * } + * } + */ + function multilineIndent() + external + {} + + /** + * contract A { + * function foo() public { + * // does nothing. + * } + * } + */ + function multilineMalformedIndent() + external + {} + + /** + * contract A { + * function + * withALongNameThatWillCauseCommentWrap() + * public { + * // does nothing. + * } + * } + */ + function malformedIndentOverflow() + external + {} +} + +/** + * contract A { + * function foo() public { + * // does nothing. + * } + * } + */ +function freeFloatingMultilineIndent() {} diff --git a/crates/fmt-2/testdata/EmitStatement/fmt.sol b/crates/fmt-2/testdata/EmitStatement/fmt.sol new file mode 100644 index 0000000000000..0fac66b9b2b80 --- /dev/null +++ b/crates/fmt-2/testdata/EmitStatement/fmt.sol @@ -0,0 +1,31 @@ +// config: line_length = 80 +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 + ); +} diff --git a/crates/fmt-2/testdata/EmitStatement/original.sol b/crates/fmt-2/testdata/EmitStatement/original.sol new file mode 100644 index 0000000000000..661abb78261d7 --- /dev/null +++ b/crates/fmt-2/testdata/EmitStatement/original.sol @@ -0,0 +1,24 @@ +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 + ); +} diff --git a/crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..a4ae0f0192051 --- /dev/null +++ b/crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol @@ -0,0 +1,21 @@ +// config: bracket_spacing = true +contract EnumDefinitions { + enum Empty { } + enum ActionChoices { + GoLeft, + GoRight, + GoStraight, + SitStill + } + enum States { + State1, + State2, + State3, + State4, + State5, + State6, + State7, + State8, + State9 + } +} diff --git a/crates/fmt-2/testdata/EnumDefinition/fmt.sol b/crates/fmt-2/testdata/EnumDefinition/fmt.sol new file mode 100644 index 0000000000000..437268aff6423 --- /dev/null +++ b/crates/fmt-2/testdata/EnumDefinition/fmt.sol @@ -0,0 +1,20 @@ +contract EnumDefinitions { + enum Empty {} + enum ActionChoices { + GoLeft, + GoRight, + GoStraight, + SitStill + } + enum States { + State1, + State2, + State3, + State4, + State5, + State6, + State7, + State8, + State9 + } +} diff --git a/crates/fmt-2/testdata/EnumDefinition/original.sol b/crates/fmt-2/testdata/EnumDefinition/original.sol new file mode 100644 index 0000000000000..69aadf884aec2 --- /dev/null +++ b/crates/fmt-2/testdata/EnumDefinition/original.sol @@ -0,0 +1,7 @@ +contract EnumDefinitions { + enum Empty { + + } + enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } + enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/EnumVariants/fmt.sol b/crates/fmt-2/testdata/EnumVariants/fmt.sol new file mode 100644 index 0000000000000..b33b8846984d2 --- /dev/null +++ b/crates/fmt-2/testdata/EnumVariants/fmt.sol @@ -0,0 +1,19 @@ +interface I { + enum Empty {} + + /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. + enum CallerMode { + /// No caller modification is currently active. + None + } + + /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. + enum CallerMode2 { + /// No caller modification is currently active. + None, + /// No caller modification is currently active2. + Some + } + + function bar() public {} +} diff --git a/crates/fmt-2/testdata/EnumVariants/original.sol b/crates/fmt-2/testdata/EnumVariants/original.sol new file mode 100644 index 0000000000000..8e146ae0fb574 --- /dev/null +++ b/crates/fmt-2/testdata/EnumVariants/original.sol @@ -0,0 +1,23 @@ +interface I { + enum Empty { + + } + + /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. + enum CallerMode + {/// No caller modification is currently active. + None + } + + /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. + enum CallerMode2 + {/// No caller modification is currently active. + None,/// No caller modification is currently active2. + + Some + } + + function bar() public { + + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ErrorDefinition/fmt.sol b/crates/fmt-2/testdata/ErrorDefinition/fmt.sol new file mode 100644 index 0000000000000..b94bbe45d52b0 --- /dev/null +++ b/crates/fmt-2/testdata/ErrorDefinition/fmt.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.8.4; + +error TopLevelCustomError(); +error TopLevelCustomErrorWithArg(uint256 x); +error TopLevelCustomErrorArgWithoutName(string); +error Error1( + uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 +); + +contract Errors { + error ContractCustomError(); + error ContractCustomErrorWithArg(uint256 x); + error ContractCustomErrorArgWithoutName(string); +} diff --git a/crates/fmt-2/testdata/ErrorDefinition/original.sol b/crates/fmt-2/testdata/ErrorDefinition/original.sol new file mode 100644 index 0000000000000..f9524c22a9ddf --- /dev/null +++ b/crates/fmt-2/testdata/ErrorDefinition/original.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.8.4; + +error + TopLevelCustomError(); + error TopLevelCustomErrorWithArg(uint x) ; +error TopLevelCustomErrorArgWithoutName (string); +error Error1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); + +contract Errors { + error + ContractCustomError(); + error ContractCustomErrorWithArg(uint x) ; + error ContractCustomErrorArgWithoutName (string); +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/EventDefinition/fmt.sol b/crates/fmt-2/testdata/EventDefinition/fmt.sol new file mode 100644 index 0000000000000..11d3d8256865a --- /dev/null +++ b/crates/fmt-2/testdata/EventDefinition/fmt.sol @@ -0,0 +1,144 @@ +pragma solidity ^0.5.2; + +contract Events { + event Event1(); + event Event1() anonymous; + + event Event1(uint256); + event Event1(uint256) anonymous; + + event Event1(uint256 a); + event Event1(uint256 a) anonymous; + + event Event1(uint256 indexed); + event Event1(uint256 indexed) anonymous; + + event Event1(uint256 indexed a); + event Event1(uint256 indexed a) anonymous; + + event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256); + event Event1( + uint256, uint256, uint256, uint256, uint256, uint256, uint256 + ) anonymous; + + event Event1( + uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 + ); + event Event1( + uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 + ) anonymous; + + event Event1( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ); + event Event1( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ) anonymous; + + event Event1( + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a + ); + event Event1( + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a, + uint256 a + ) anonymous; + + event Event1( + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed + ); + event Event1( + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed, + uint256 indexed + ) anonymous; + + event Event1( + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a + ); + event Event1( + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a, + uint256 indexed a + ) anonymous; +} diff --git a/crates/fmt-2/testdata/EventDefinition/original.sol b/crates/fmt-2/testdata/EventDefinition/original.sol new file mode 100644 index 0000000000000..d2a61516204d0 --- /dev/null +++ b/crates/fmt-2/testdata/EventDefinition/original.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.5.2; + +contract Events { + event Event1(); + event Event1() anonymous; + + event Event1(uint256); + event Event1(uint256) anonymous; + + event Event1(uint256 a); + event Event1(uint256 a) anonymous; + + event Event1(uint256 indexed); + event Event1(uint256 indexed) anonymous; + + event Event1(uint256 indexed a); + event Event1(uint256 indexed a) anonymous; + + event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256); + event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256) anonymous; + + event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); + event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) anonymous; + + event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); + event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) anonymous; + + event Event1(uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a); + event Event1(uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a) anonymous; + + event Event1(uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed); + event Event1(uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed) anonymous; + + event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); + event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a) anonymous; +} diff --git a/crates/fmt-2/testdata/ForStatement/fmt.sol b/crates/fmt-2/testdata/ForStatement/fmt.sol new file mode 100644 index 0000000000000..a1bb4b2e6a28c --- /dev/null +++ b/crates/fmt-2/testdata/ForStatement/fmt.sol @@ -0,0 +1,37 @@ +pragma solidity ^0.8.8; + +contract ForStatement { + function test() external { + for (uint256 i1; i1 < 10; i1++) { + i1++; + } + + uint256 i2; + for (++i2; i2 < 10; i2++) {} + + uint256 veryLongVariableName = 1000; + for ( + uint256 i3; + i3 < 10 && veryLongVariableName > 999 && veryLongVariableName < 1001; + i3++ + ) { + i3++; + } + + for (type(uint256).min;;) {} + + for (;;) { + "test"; + } + + for (uint256 i4; i4 < 10; i4++) { + i4++; + } + + for (uint256 i5;;) { + for (uint256 i6 = 10; i6 > i5; i6--) { + i5++; + } + } + } +} diff --git a/crates/fmt-2/testdata/ForStatement/original.sol b/crates/fmt-2/testdata/ForStatement/original.sol new file mode 100644 index 0000000000000..e98288dd1cbce --- /dev/null +++ b/crates/fmt-2/testdata/ForStatement/original.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.8.8; + +contract ForStatement { + function test() external { + for + (uint256 i1 + ; i1 < 10; i1++) + { + i1++; + } + + uint256 i2; + for(++i2;i2<10;i2++) + + {} + + uint256 veryLongVariableName = 1000; + for ( uint256 i3; i3 < 10 + && veryLongVariableName>999 && veryLongVariableName< 1001 + ; i3++) + { i3 ++ ; } + + for (type(uint256).min;;) {} + + for (;;) { "test" ; } + + for (uint256 i4; i4< 10; i4++) i4++; + + for (uint256 i5; ;) + for (uint256 i6 = 10; i6 > i5; i6--) + i5++; + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..2bfe9798c5294 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol @@ -0,0 +1,42 @@ +// config: line_length = 120 +// config: bracket_spacing = true +contract FunctionCall { + function foo() public pure { + bar(1111111111111111111111111111111111111111111111111111, 111111111111111111111111111111111111111111111111111); + bar(1111111111111111111111111111111111111111111111111112, 1111111111111111111111111111111111111111111111111112); + bar(1111111111111111111111111111111111111111111111111113, 11111111111111111111111111111111111111111111111111113); // the semicolon is not considered when determining line break + bar( + 1111111111111111111111111111111111111111111111111114, 111111111111111111111111111111111111111111111111111114 + ); + bar( + 111111111111111111111111111111111115, + 11111111111111111111111111111111115, + 11111111111111111111111111111111115 + ); + bar( + 111111111111111111111111111111111111111111111111111116, + 111111111111111111111111111111111111111111111111111116 + ); + bar( + 111111111111111111111111111111111111111111111111111117, + 1111111111111111111111111111111111111111111111111111117 + ); + } + + function bar(uint256, uint256) private pure { + return; + } +} + +function a(uint256 foo) { + foo; + MyContract c = new MyContract(address(0), hex"beef"); +} + +function b() { + a({ foo: 5 }); +} + +contract MyContract { + constructor(address arg, bytes memory data) { } +} diff --git a/crates/fmt-2/testdata/FunctionCall/fmt.sol b/crates/fmt-2/testdata/FunctionCall/fmt.sol new file mode 100644 index 0000000000000..ff22f63aaa5ce --- /dev/null +++ b/crates/fmt-2/testdata/FunctionCall/fmt.sol @@ -0,0 +1,41 @@ +// config: line_length = 120 +contract FunctionCall { + function foo() public pure { + bar(1111111111111111111111111111111111111111111111111111, 111111111111111111111111111111111111111111111111111); + bar(1111111111111111111111111111111111111111111111111112, 1111111111111111111111111111111111111111111111111112); + bar(1111111111111111111111111111111111111111111111111113, 11111111111111111111111111111111111111111111111111113); // the semicolon is not considered when determining line break + bar( + 1111111111111111111111111111111111111111111111111114, 111111111111111111111111111111111111111111111111111114 + ); + bar( + 111111111111111111111111111111111115, + 11111111111111111111111111111111115, + 11111111111111111111111111111111115 + ); + bar( + 111111111111111111111111111111111111111111111111111116, + 111111111111111111111111111111111111111111111111111116 + ); + bar( + 111111111111111111111111111111111111111111111111111117, + 1111111111111111111111111111111111111111111111111111117 + ); + } + + function bar(uint256, uint256) private pure { + return; + } +} + +function a(uint256 foo) { + foo; + MyContract c = new MyContract(address(0), hex"beef"); +} + +function b() { + a({foo: 5}); +} + +contract MyContract { + constructor(address arg, bytes memory data) {} +} diff --git a/crates/fmt-2/testdata/FunctionCall/original.sol b/crates/fmt-2/testdata/FunctionCall/original.sol new file mode 100644 index 0000000000000..ea03850554e25 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionCall/original.sol @@ -0,0 +1,34 @@ +contract FunctionCall { + function foo() public pure { + bar(1111111111111111111111111111111111111111111111111111, 111111111111111111111111111111111111111111111111111); + bar(1111111111111111111111111111111111111111111111111112, 1111111111111111111111111111111111111111111111111112); + bar(1111111111111111111111111111111111111111111111111113, 11111111111111111111111111111111111111111111111111113); // the semicolon is not considered when determining line break + bar(1111111111111111111111111111111111111111111111111114, 111111111111111111111111111111111111111111111111111114); + bar( + 111111111111111111111111111111111115, 11111111111111111111111111111111115, 11111111111111111111111111111111115 + ); + bar( + 111111111111111111111111111111111111111111111111111116, 111111111111111111111111111111111111111111111111111116 + ); + bar( + 111111111111111111111111111111111111111111111111111117, 1111111111111111111111111111111111111111111111111111117 + ); + } + + function bar(uint256, uint256) private pure { + return; + } +} + +function a(uint256 foo) { + foo; + MyContract c = new MyContract(address( 0),hex"beef"); +} + +function b() { + a( {foo: 5} ); +} + +contract MyContract { + constructor(address arg, bytes memory data) {} +} diff --git a/crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..93e5eb1a2e793 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol @@ -0,0 +1,55 @@ +// config: bracket_spacing = true +interface ITarget { + function run() external payable; + function veryAndVeryLongNameOfSomeRunFunction() external payable; +} + +contract FunctionCallArgsStatement { + ITarget public target; + + function estimate() public returns (uint256 gas) { + gas = 1 gwei; + } + + function veryAndVeryLongNameOfSomeGasEstimateFunction() + public + returns (uint256) + { + return gasleft(); + } + + function value(uint256 val) public returns (uint256) { + return val; + } + + function test() external { + target.run{ gas: gasleft(), value: 1 wei }; + + target.run{ gas: 1, value: 0x00 }(); + + target.run{ gas: 1000, value: 1 ether }(); + + target.run{ gas: estimate(), value: value(1) }(); + + target.run{ + value: value(1 ether), + gas: veryAndVeryLongNameOfSomeGasEstimateFunction() + }(); + + target.run{ /* comment 1 */ value: /* comment2 */ 1 }; + + target.run{ /* comment3 */ + value: 1, // comment4 + gas: gasleft() + }; + + target.run{ + // comment5 + value: 1, + // comment6 + gas: gasleft() + }; + + vm.expectEmit({ checkTopic1: false, checkTopic2: false }); + } +} diff --git a/crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol b/crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol new file mode 100644 index 0000000000000..5a5cc5f634281 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol @@ -0,0 +1,54 @@ +interface ITarget { + function run() external payable; + function veryAndVeryLongNameOfSomeRunFunction() external payable; +} + +contract FunctionCallArgsStatement { + ITarget public target; + + function estimate() public returns (uint256 gas) { + gas = 1 gwei; + } + + function veryAndVeryLongNameOfSomeGasEstimateFunction() + public + returns (uint256) + { + return gasleft(); + } + + function value(uint256 val) public returns (uint256) { + return val; + } + + function test() external { + target.run{gas: gasleft(), value: 1 wei}; + + target.run{gas: 1, value: 0x00}(); + + target.run{gas: 1000, value: 1 ether}(); + + target.run{gas: estimate(), value: value(1)}(); + + target.run{ + value: value(1 ether), + gas: veryAndVeryLongNameOfSomeGasEstimateFunction() + }(); + + target.run{ /* comment 1 */ value: /* comment2 */ 1}; + + target.run{ /* comment3 */ + value: 1, // comment4 + gas: gasleft() + }; + + target.run{ + // comment5 + value: 1, + // comment6 + gas: gasleft() + }; + + vm.expectEmit({checkTopic1: false, checkTopic2: false}); + } +} diff --git a/crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol b/crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol new file mode 100644 index 0000000000000..b2cfaa2f28f9a --- /dev/null +++ b/crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol @@ -0,0 +1,50 @@ +interface ITarget { + function run() external payable; + function veryAndVeryLongNameOfSomeRunFunction() external payable; +} + +contract FunctionCallArgsStatement { + ITarget public target; + + function estimate() public returns (uint256 gas) { + gas = 1 gwei; + } + + function veryAndVeryLongNameOfSomeGasEstimateFunction() public returns (uint256) { + return gasleft(); + } + + function value(uint256 val) public returns (uint256) { + return val; + } + + function test() external { + target.run{ gas: gasleft(), value: 1 wei }; + + target.run{gas:1,value:0x00}(); + + target.run{ + gas : 1000, + value: 1 ether + } (); + + target.run{ gas: estimate(), + value: value(1) }(); + + target.run { value: + value(1 ether), gas: veryAndVeryLongNameOfSomeGasEstimateFunction() } (); + + target.run /* comment 1 */ { value: /* comment2 */ 1 }; + + target.run { /* comment3 */ value: 1, // comment4 + gas: gasleft()}; + + target.run { + // comment5 + value: 1, + // comment6 + gas: gasleft()}; + + vm.expectEmit({ checkTopic1: false, checkTopic2: false }); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol new file mode 100644 index 0000000000000..db7164d284a54 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol @@ -0,0 +1,732 @@ +// config: line_length = 60 +// config: multiline_func_header = "all_params" +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam(uint256 x); + + function oneModifier() modifier1; + + function oneReturn() returns (uint256 y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + ) + // public prefix + public // public postfix + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 + ); // y3 postfix + // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ); + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3; + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns (uint256 y1, uint256 y2, uint256 y3); + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3; + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns (uint256 y1, uint256 y2, uint256 y3); + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string); + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address); + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256); +} + +contract FunctionDefinitions { + function() external {} + fallback() external {} + + function() external payable {} + fallback() external payable {} + receive() external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam(uint256 x) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns (uint256 y1) { + a = 1; + } + + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) { + a = 1; + } + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + { + a = 1; + } + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + { + a = 1; + } + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + public + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string) + { + a = 1; + } + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address) + { + a = 1; + } + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is + FunctionInterfaces, + FunctionDefinitions +{ + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam( + uint256 x + ) + override( + FunctionInterfaces, + FunctionDefinitions, + SomeOtherFunctionContract, + SomeImport.AndAnotherFunctionContract + ) + { + a = 1; + } +} diff --git a/crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol new file mode 100644 index 0000000000000..6d90880679199 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol @@ -0,0 +1,730 @@ +// config: line_length = 60 +// config: multiline_func_header = "all" +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam(uint256 x); + + function oneModifier() modifier1; + + function oneReturn() returns (uint256 y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + ) + // public prefix + public // public postfix + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 + ); // y3 postfix + // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ); + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3; + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns (uint256 y1, uint256 y2, uint256 y3); + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3; + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns (uint256 y1, uint256 y2, uint256 y3); + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string); + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address); + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256); +} + +contract FunctionDefinitions { + function() external {} + fallback() external {} + + function() external payable {} + fallback() external payable {} + receive() external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam(uint256 x) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns (uint256 y1) { + a = 1; + } + + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) { + a = 1; + } + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + { + a = 1; + } + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + { + a = 1; + } + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + public + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string) + { + a = 1; + } + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address) + { + a = 1; + } + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is + FunctionInterfaces, + FunctionDefinitions +{ + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam(uint256 x) + override( + FunctionInterfaces, + FunctionDefinitions, + SomeOtherFunctionContract, + SomeImport.AndAnotherFunctionContract + ) + { + a = 1; + } +} diff --git a/crates/fmt-2/testdata/FunctionDefinition/fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/fmt.sol new file mode 100644 index 0000000000000..9e34a8bea2682 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinition/fmt.sol @@ -0,0 +1,709 @@ +// config: line_length = 60 +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam(uint256 x); + + function oneModifier() modifier1; + + function oneReturn() returns (uint256 y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + ) + // public prefix + public // public postfix + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 + ); // y3 postfix + // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ); + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3; + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3; + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string); + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address); + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256); +} + +contract FunctionDefinitions { + function() external {} + fallback() external {} + + function() external payable {} + fallback() external payable {} + receive() external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam(uint256 x) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns (uint256 y1) { + a = 1; + } + + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) { + a = 1; + } + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + public + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string) + { + a = 1; + } + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address) + { + a = 1; + } + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is + FunctionInterfaces, + FunctionDefinitions +{ + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam(uint256 x) + override( + FunctionInterfaces, + FunctionDefinitions, + SomeOtherFunctionContract, + SomeImport.AndAnotherFunctionContract + ) + { + a = 1; + } +} diff --git a/crates/fmt-2/testdata/FunctionDefinition/original.sol b/crates/fmt-2/testdata/FunctionDefinition/original.sol new file mode 100644 index 0000000000000..97db649d55660 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinition/original.sol @@ -0,0 +1,218 @@ +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam(uint x); + + function oneModifier() modifier1; + + function oneReturn() returns(uint y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + + ) + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // public prefix + public // public postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 // y3 postfix + ); // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10); + + function manyModifiers() modifier1() modifier2() modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10; + + function manyReturns() returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); + + function someParamsSomeModifiers(uint x1, uint x2, uint x3) modifier1() modifier2 modifier3; + + function someParamsSomeReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3); + + function someModifiersSomeReturns() modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3); + + function someParamSomeModifiersSomeReturns(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3); + + function someParamsManyModifiers(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10; + + function someParamsManyReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); + + function manyParamsSomeModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3; + + function manyParamsSomeReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3); + + function manyParamsManyModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10; + + function manyParamsManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); + + function manyParamsManyModifiersManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); + + function modifierOrderCorrect01() public view virtual override modifier1 modifier2 returns(uint); + + function modifierOrderCorrect02() private pure virtual modifier1 modifier2 returns(string); + + function modifierOrderCorrect03() external payable override modifier1 modifier2 returns(address); + + function modifierOrderCorrect04() internal virtual override modifier1 modifier2 returns(uint); + + function modifierOrderIncorrect01() public modifier1 modifier2 override virtual view returns(uint); + + function modifierOrderIncorrect02() virtual modifier1 external modifier2 override returns(uint); + + function modifierOrderIncorrect03() modifier1 pure internal virtual modifier2 returns(uint); + + function modifierOrderIncorrect04() override modifier1 payable external modifier2 returns(uint); +} + +contract FunctionDefinitions { + function () external {} + fallback () external {} + + function () external payable {} + fallback () external payable {} + receive () external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam(uint x) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns(uint y1) { + a = 1; + } + + function manyParams(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) { + a = 1; + } + + function manyModifiers() modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 { + a = 1; + } + + function manyReturns() returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { + a = 1; + } + + function someParamsSomeModifiers(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 { + a = 1; + } + + function someParamsSomeReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3) { + a = 1; + } + + function someModifiersSomeReturns() modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3) { + a = 1; + } + + function someParamSomeModifiersSomeReturns(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3) { + a = 1; + } + + function someParamsManyModifiers(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 { + a = 1; + } + + function someParamsManyReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { + a = 1; + } + + function manyParamsSomeModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 { + a = 1; + } + + function manyParamsSomeReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3) { + a = 1; + } + + function manyParamsManyModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 public { + a = 1; + } + + function manyParamsManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { + a = 1; + } + + function manyParamsManyModifiersManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { + a = 1; + } + + function modifierOrderCorrect01() public view virtual override modifier1 modifier2 returns(uint) { + a = 1; + } + + function modifierOrderCorrect02() private pure virtual modifier1 modifier2 returns(string) { + a = 1; + } + + function modifierOrderCorrect03() external payable override modifier1 modifier2 returns(address) { + a = 1; + } + + function modifierOrderCorrect04() internal virtual override modifier1 modifier2 returns(uint) { + a = 1; + } + + function modifierOrderIncorrect01() public modifier1 modifier2 override virtual view returns(uint) { + a = 1; + } + + function modifierOrderIncorrect02() virtual modifier1 external modifier2 override returns(uint) { + a = 1; + } + + function modifierOrderIncorrect03() modifier1 pure internal virtual modifier2 returns(uint) { + a = 1; + } + + function modifierOrderIncorrect04() override modifier1 payable external modifier2 returns(uint) { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is FunctionInterfaces, FunctionDefinitions { + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam(uint256 x) override(FunctionInterfaces, FunctionDefinitions, SomeOtherFunctionContract, SomeImport.AndAnotherFunctionContract) { + a = 1; + } +} + diff --git a/crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol new file mode 100644 index 0000000000000..516e5c2fd42ed --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol @@ -0,0 +1,710 @@ +// config: line_length = 60 +// config: override_spacing = true +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam(uint256 x); + + function oneModifier() modifier1; + + function oneReturn() returns (uint256 y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + ) + // public prefix + public // public postfix + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 + ); // y3 postfix + // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ); + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3; + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3; + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string); + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address); + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256); +} + +contract FunctionDefinitions { + function() external {} + fallback() external {} + + function() external payable {} + fallback() external payable {} + receive() external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam(uint256 x) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns (uint256 y1) { + a = 1; + } + + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) { + a = 1; + } + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + public + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string) + { + a = 1; + } + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address) + { + a = 1; + } + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is + FunctionInterfaces, + FunctionDefinitions +{ + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam(uint256 x) + override ( + FunctionInterfaces, + FunctionDefinitions, + SomeOtherFunctionContract, + SomeImport.AndAnotherFunctionContract + ) + { + a = 1; + } +} diff --git a/crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol new file mode 100644 index 0000000000000..3e7ebfff6b3aa --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol @@ -0,0 +1,716 @@ +// config: line_length = 60 +// config: multiline_func_header = "params_first" +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam( + uint256 x + ); + + function oneModifier() modifier1; + + function oneReturn() returns (uint256 y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + ) + // public prefix + public // public postfix + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 + ); // y3 postfix + // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ); + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3; + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3; + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string); + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address); + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256); +} + +contract FunctionDefinitions { + function() external {} + fallback() external {} + + function() external payable {} + fallback() external payable {} + receive() external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam( + uint256 x + ) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns (uint256 y1) { + a = 1; + } + + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) { + a = 1; + } + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + public + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string) + { + a = 1; + } + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address) + { + a = 1; + } + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is + FunctionInterfaces, + FunctionDefinitions +{ + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam( + uint256 x + ) + override( + FunctionInterfaces, + FunctionDefinitions, + SomeOtherFunctionContract, + SomeImport.AndAnotherFunctionContract + ) + { + a = 1; + } +} diff --git a/crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol new file mode 100644 index 0000000000000..cd2015c9e050e --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol @@ -0,0 +1,710 @@ +// config: line_length = 60 +// config: multiline_func_header = "params_first_multi" +interface FunctionInterfaces { + function noParamsNoModifiersNoReturns(); + + function oneParam(uint256 x); + + function oneModifier() modifier1; + + function oneReturn() returns (uint256 y1); + + // function prefix + function withComments( // function name postfix + // x1 prefix + uint256 x1, // x1 postfix + // x2 prefix + uint256 x2, // x2 postfix + // x2 postfix2 + /* + multi-line x3 prefix + */ + uint256 x3 // x3 postfix + ) + // public prefix + public // public postfix + // pure prefix + pure // pure postfix + // modifier1 prefix + modifier1 // modifier1 postfix + // modifier2 prefix + modifier2 /* + mutliline modifier2 postfix + */ + // modifier3 prefix + modifier3 // modifier3 postfix + returns ( + // y1 prefix + uint256 y1, // y1 postfix + // y2 prefix + uint256 y2, // y2 postfix + // y3 prefix + uint256 y3 + ); // y3 postfix + // function postfix + + /*////////////////////////////////////////////////////////////////////////// + TEST + //////////////////////////////////////////////////////////////////////////*/ + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ); + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3; + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3); + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3; + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3); + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10; + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ); + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string); + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address); + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256); + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256); +} + +contract FunctionDefinitions { + function() external {} + fallback() external {} + + function() external payable {} + fallback() external payable {} + receive() external payable {} + + function noParamsNoModifiersNoReturns() { + a = 1; + } + + function oneParam(uint256 x) { + a = 1; + } + + function oneModifier() modifier1 { + a = 1; + } + + function oneReturn() returns (uint256 y1) { + a = 1; + } + + function manyParams( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) { + a = 1; + } + + function manyModifiers() + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyReturns() + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function someParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function someParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function someModifiersSomeReturns() + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamSomeModifiersSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + returns (uint256 y1, uint256 y2, uint256 y3) + { + a = 1; + } + + function someParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function someParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsSomeModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) modifier1 modifier2 modifier3 { + a = 1; + } + + function manyParamsSomeReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) returns (uint256 y1, uint256 y2, uint256 y3) { + a = 1; + } + + function manyParamsManyModifiers( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + public + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + { + a = 1; + } + + function manyParamsManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function manyParamsManyModifiersManyReturns( + uint256 x1, + uint256 x2, + uint256 x3, + uint256 x4, + uint256 x5, + uint256 x6, + uint256 x7, + uint256 x8, + uint256 x9, + uint256 x10 + ) + modifier1 + modifier2 + modifier3 + modifier4 + modifier5 + modifier6 + modifier7 + modifier8 + modifier9 + modifier10 + returns ( + uint256 y1, + uint256 y2, + uint256 y3, + uint256 y4, + uint256 y5, + uint256 y6, + uint256 y7, + uint256 y8, + uint256 y9, + uint256 y10 + ) + { + a = 1; + } + + function modifierOrderCorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderCorrect02() + private + pure + virtual + modifier1 + modifier2 + returns (string) + { + a = 1; + } + + function modifierOrderCorrect03() + external + payable + override + modifier1 + modifier2 + returns (address) + { + a = 1; + } + + function modifierOrderCorrect04() + internal + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect01() + public + view + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect02() + external + virtual + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect03() + internal + pure + virtual + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + function modifierOrderIncorrect04() + external + payable + override + modifier1 + modifier2 + returns (uint256) + { + a = 1; + } + + fallback() external payable virtual {} + receive() external payable virtual {} +} + +contract FunctionOverrides is + FunctionInterfaces, + FunctionDefinitions +{ + function noParamsNoModifiersNoReturns() override { + a = 1; + } + + function oneParam(uint256 x) + override( + FunctionInterfaces, + FunctionDefinitions, + SomeOtherFunctionContract, + SomeImport.AndAnotherFunctionContract + ) + { + a = 1; + } +} diff --git a/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol b/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol new file mode 100644 index 0000000000000..7b751e22ec26a --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract ReturnFnFormat { + function returnsFunction() + internal + pure + returns ( + function() + internal pure returns (uint256) + ) + {} +} + +// https://github.com/foundry-rs/foundry/issues/7920 +contract ReturnFnDisableFormat { + // forgefmt: disable-next-line + function disableFnFormat() external returns (uint256) { + return 0; + } +} diff --git a/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol b/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol new file mode 100644 index 0000000000000..0c785cde81b14 --- /dev/null +++ b/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract ReturnFnFormat { + function returnsFunction() + internal + pure + returns ( + function() + internal pure returns (uint256) + ) + {} +} + +// https://github.com/foundry-rs/foundry/issues/7920 +contract ReturnFnDisableFormat { + // forgefmt: disable-next-line + function disableFnFormat() external returns (uint256) { + return 0; + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/FunctionType/fmt.sol b/crates/fmt-2/testdata/FunctionType/fmt.sol new file mode 100644 index 0000000000000..39053d816058f --- /dev/null +++ b/crates/fmt-2/testdata/FunctionType/fmt.sol @@ -0,0 +1,31 @@ +// config: line_length = 90 +library ArrayUtils { + function map(uint256[] memory self, function (uint) pure returns (uint) f) + internal + pure + returns (uint256[] memory r) + { + r = new uint256[](self.length); + for (uint256 i = 0; i < self.length; i++) { + r[i] = f(self[i]); + } + } + + function reduce(uint256[] memory self, function (uint, uint) pure returns (uint) f) + internal + pure + returns (uint256 r) + { + r = self[0]; + for (uint256 i = 1; i < self.length; i++) { + r = f(r, self[i]); + } + } + + function range(uint256 length) internal pure returns (uint256[] memory r) { + r = new uint256[](length); + for (uint256 i = 0; i < r.length; i++) { + r[i] = i; + } + } +} diff --git a/crates/fmt-2/testdata/FunctionType/original.sol b/crates/fmt-2/testdata/FunctionType/original.sol new file mode 100644 index 0000000000000..27b402d85ef5f --- /dev/null +++ b/crates/fmt-2/testdata/FunctionType/original.sol @@ -0,0 +1,31 @@ +library ArrayUtils { + function map(uint[] memory self, function (uint) pure returns (uint) f) + internal + pure + returns ( + uint[] memory r + ) + { + r = new uint[]( self.length); + for (uint i = 0; i < self.length; i++) { + r[i] = f(self[i]); + } + } + + function reduce( + uint[] memory self, + function (uint, uint) pure returns (uint) f + ) internal pure returns (uint256 r) { + r = self[0]; + for (uint i = 1; i < self.length; i++) { + r = f(r, self[i]); + } + } + + function range(uint256 length) internal pure returns (uint[] memory r) { + r = new uint256[](length ); + for (uint i = 0; i < r.length; i++) { + r[i] = i; + } + } +} diff --git a/crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol b/crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol new file mode 100644 index 0000000000000..b3be2a8657c18 --- /dev/null +++ b/crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol @@ -0,0 +1,10 @@ +// config: hex_underscore = "bytes" +contract HexLiteral { + function test() external { + hex"01_23_00_00"; + hex"01_23_00_00"; + hex"01_23_00_00"; + hex""; + hex"60_01_60_02_53"; + } +} diff --git a/crates/fmt-2/testdata/HexUnderscore/fmt.sol b/crates/fmt-2/testdata/HexUnderscore/fmt.sol new file mode 100644 index 0000000000000..0c8710a924758 --- /dev/null +++ b/crates/fmt-2/testdata/HexUnderscore/fmt.sol @@ -0,0 +1,9 @@ +contract HexLiteral { + function test() external { + hex"01230000"; + hex"01230000"; + hex"01230000"; + hex""; + hex"6001600253"; + } +} diff --git a/crates/fmt-2/testdata/HexUnderscore/original.sol b/crates/fmt-2/testdata/HexUnderscore/original.sol new file mode 100644 index 0000000000000..5c29187475489 --- /dev/null +++ b/crates/fmt-2/testdata/HexUnderscore/original.sol @@ -0,0 +1,9 @@ +contract HexLiteral { + function test() external { + hex"0123_0000"; + hex"01230000"; + hex"0123_00_00"; + hex""; + hex"6001_6002_53"; + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol b/crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol new file mode 100644 index 0000000000000..0f5db52e3c3f3 --- /dev/null +++ b/crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol @@ -0,0 +1,10 @@ +// config: hex_underscore = "preserve" +contract HexLiteral { + function test() external { + hex"0123_0000"; + hex"01230000"; + hex"0123_00_00"; + hex""; + hex"6001_6002_53"; + } +} diff --git a/crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol b/crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol new file mode 100644 index 0000000000000..39aae1465cf7d --- /dev/null +++ b/crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol @@ -0,0 +1,10 @@ +// config: hex_underscore = "remove" +contract HexLiteral { + function test() external { + hex"01230000"; + hex"01230000"; + hex"01230000"; + hex""; + hex"6001600253"; + } +} diff --git a/crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol b/crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol new file mode 100644 index 0000000000000..dcd8bb83eaa8f --- /dev/null +++ b/crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol @@ -0,0 +1,171 @@ +// config: single_line_statement_blocks = "multi" +function execute() returns (bool) { + if (true) { + // always returns true + return true; + } + return false; +} + +function executeElse() {} + +function executeWithMultipleParameters(bool parameter1, bool parameter2) {} + +function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} + +contract IfStatement { + function test() external { + if (true) { + execute(); + } + + bool condition; + bool anotherLongCondition; + bool andAnotherVeryVeryLongCondition; + if ( + condition && anotherLongCondition || andAnotherVeryVeryLongCondition + ) { + execute(); + } + + // comment + if (condition) { + execute(); + } else if (anotherLongCondition) { + execute(); // differently + } + + /* comment1 */ + if ( /* comment2 */ /* comment3 */ + condition // comment4 + ) { + // comment5 + execute(); + } // comment6 + + if (condition) { + execute(); + } // comment7 + /* comment8 */ + /* comment9 */ + else if ( /* comment10 */ + anotherLongCondition // comment11 + ) { + /* comment12 */ + execute(); + } // comment13 + /* comment14 */ + else {} // comment15 + + if ( + // comment16 + condition /* comment17 */ + ) { + execute(); + } + + if (condition) { + execute(); + } else { + executeElse(); + } + + if (condition) { + if (anotherLongCondition) { + execute(); + } + } + + if (condition) { + execute(); + } + + if ( + condition && anotherLongCondition || andAnotherVeryVeryLongCondition + ) { + execute(); + } + + if (condition) { + if (anotherLongCondition) { + execute(); + } + } + + if (condition) { + execute(); + } // comment18 + + if (condition) { + executeWithMultipleParameters(condition, anotherLongCondition); + } + + if (condition) { + executeWithVeryVeryVeryLongNameAndSomeParameter(condition); + } + + if (condition) { + execute(); + } else { + execute(); + } + + if (condition) {} + + if (condition) { + executeWithMultipleParameters(condition, anotherLongCondition); + } else if (anotherLongCondition) { + execute(); + } + + if (condition && ((condition || anotherLongCondition))) { + execute(); + } + + // if statement + if (condition) { + execute(); + } + // else statement + else { + execute(); + } + + // if statement + if (condition) { + execute(); + } + // else statement + else { + executeWithMultipleParameters( + anotherLongCondition, andAnotherVeryVeryLongCondition + ); + } + + if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } + + if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else { + executeElse(); + } + } +} diff --git a/crates/fmt-2/testdata/IfStatement/block-single.fmt.sol b/crates/fmt-2/testdata/IfStatement/block-single.fmt.sol new file mode 100644 index 0000000000000..ba2b9998b184c --- /dev/null +++ b/crates/fmt-2/testdata/IfStatement/block-single.fmt.sol @@ -0,0 +1,123 @@ +// config: single_line_statement_blocks = "single" +function execute() returns (bool) { + if (true) { + // always returns true + return true; + } + return false; +} + +function executeElse() {} + +function executeWithMultipleParameters(bool parameter1, bool parameter2) {} + +function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} + +contract IfStatement { + function test() external { + if (true) execute(); + + bool condition; + bool anotherLongCondition; + bool andAnotherVeryVeryLongCondition; + if ( + condition && anotherLongCondition || andAnotherVeryVeryLongCondition + ) execute(); + + // comment + if (condition) execute(); + else if (anotherLongCondition) execute(); // differently + + /* comment1 */ + if ( /* comment2 */ /* comment3 */ + condition // comment4 + ) { + // comment5 + execute(); + } // comment6 + + if (condition) { + execute(); + } // comment7 + /* comment8 */ + /* comment9 */ + else if ( /* comment10 */ + anotherLongCondition // comment11 + ) { + /* comment12 */ + execute(); + } // comment13 + /* comment14 */ + else {} // comment15 + + if ( + // comment16 + condition /* comment17 */ + ) execute(); + + if (condition) execute(); + else executeElse(); + + if (condition) if (anotherLongCondition) execute(); + + if (condition) execute(); + + if ( + condition && anotherLongCondition || andAnotherVeryVeryLongCondition + ) execute(); + + if (condition) if (anotherLongCondition) execute(); + + if (condition) execute(); // comment18 + + if (condition) { + executeWithMultipleParameters(condition, anotherLongCondition); + } + + if (condition) { + executeWithVeryVeryVeryLongNameAndSomeParameter(condition); + } + + if (condition) execute(); + else execute(); + + if (condition) {} + + if (condition) { + executeWithMultipleParameters(condition, anotherLongCondition); + } else if (anotherLongCondition) { + execute(); + } + + if (condition && ((condition || anotherLongCondition))) execute(); + + // if statement + if (condition) execute(); + // else statement + else execute(); + + // if statement + if (condition) { + execute(); + } + // else statement + else { + executeWithMultipleParameters( + anotherLongCondition, andAnotherVeryVeryLongCondition + ); + } + + if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + + if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else executeElse(); + } +} diff --git a/crates/fmt-2/testdata/IfStatement/fmt.sol b/crates/fmt-2/testdata/IfStatement/fmt.sol new file mode 100644 index 0000000000000..cb2f8874f83d5 --- /dev/null +++ b/crates/fmt-2/testdata/IfStatement/fmt.sol @@ -0,0 +1,145 @@ +function execute() returns (bool) { + if (true) { + // always returns true + return true; + } + return false; +} + +function executeElse() {} + +function executeWithMultipleParameters(bool parameter1, bool parameter2) {} + +function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} + +contract IfStatement { + function test() external { + if (true) { + execute(); + } + + bool condition; + bool anotherLongCondition; + bool andAnotherVeryVeryLongCondition; + if ( + condition && anotherLongCondition || andAnotherVeryVeryLongCondition + ) { + execute(); + } + + // comment + if (condition) { + execute(); + } else if (anotherLongCondition) { + execute(); // differently + } + + /* comment1 */ + if ( /* comment2 */ /* comment3 */ + condition // comment4 + ) { + // comment5 + execute(); + } // comment6 + + if (condition) { + execute(); + } // comment7 + /* comment8 */ + /* comment9 */ + else if ( /* comment10 */ + anotherLongCondition // comment11 + ) { + /* comment12 */ + execute(); + } // comment13 + /* comment14 */ + else {} // comment15 + + if ( + // comment16 + condition /* comment17 */ + ) { + execute(); + } + + if (condition) { + execute(); + } else { + executeElse(); + } + + if (condition) { + if (anotherLongCondition) { + execute(); + } + } + + if (condition) execute(); + + if ( + condition && anotherLongCondition || andAnotherVeryVeryLongCondition + ) execute(); + + if (condition) if (anotherLongCondition) execute(); + + if (condition) execute(); // comment18 + + if (condition) { + executeWithMultipleParameters(condition, anotherLongCondition); + } + + if (condition) { + executeWithVeryVeryVeryLongNameAndSomeParameter(condition); + } + + if (condition) execute(); + else execute(); + + if (condition) {} + + if (condition) { + executeWithMultipleParameters(condition, anotherLongCondition); + } else if (anotherLongCondition) { + execute(); + } + + if (condition && ((condition || anotherLongCondition))) execute(); + + // if statement + if (condition) execute(); + // else statement + else execute(); + + // if statement + if (condition) { + execute(); + } + // else statement + else { + executeWithMultipleParameters( + anotherLongCondition, andAnotherVeryVeryLongCondition + ); + } + + if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + + if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else if (condition) { + execute(); + } else { + executeElse(); + } + } +} diff --git a/crates/fmt-2/testdata/IfStatement/original.sol b/crates/fmt-2/testdata/IfStatement/original.sol new file mode 100644 index 0000000000000..b36829bbbf6bf --- /dev/null +++ b/crates/fmt-2/testdata/IfStatement/original.sol @@ -0,0 +1,119 @@ +function execute() returns (bool) { + if (true) { + // always returns true + return true; + } + return false; +} + +function executeElse() {} + +function executeWithMultipleParameters(bool parameter1, bool parameter2) {} + +function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} + +contract IfStatement { + + function test() external { + if( true) + { + execute() ; + } + + bool condition; bool anotherLongCondition; bool andAnotherVeryVeryLongCondition ; + if + ( condition && anotherLongCondition || + andAnotherVeryVeryLongCondition + ) + { execute(); } + + // comment + if (condition) { execute(); } + else + if (anotherLongCondition) { + execute(); // differently + } + + /* comment1 */ if /* comment2 */ ( /* comment3 */ condition ) // comment4 + { + // comment5 + execute(); + } // comment6 + + if (condition ) { + execute(); + } // comment7 + /* comment8 */ + /* comment9 */ else if /* comment10 */ (anotherLongCondition) // comment11 + /* comment12 */ { + execute() ; + } // comment13 + /* comment14 */ else { } // comment15 + + if ( + // comment16 + condition /* comment17 */ + ) + { + execute(); + } + + if (condition) + execute(); + else + executeElse(); + + if (condition) + if (anotherLongCondition) + execute(); + + if (condition) execute(); + + if (condition && anotherLongCondition || + andAnotherVeryVeryLongCondition ) execute(); + + if (condition) if (anotherLongCondition) execute(); + + if (condition) execute(); // comment18 + + if (condition) executeWithMultipleParameters(condition, anotherLongCondition); + + if (condition) executeWithVeryVeryVeryLongNameAndSomeParameter(condition); + + if (condition) execute(); else execute(); + + if (condition) {} + + if (condition) executeWithMultipleParameters(condition, anotherLongCondition); else if (anotherLongCondition) execute(); + + if (condition && ((condition || anotherLongCondition) + ) + ) execute(); + + // if statement + if (condition) execute(); + // else statement + else execute(); + + // if statement + if (condition) execute(); + // else statement + else executeWithMultipleParameters(anotherLongCondition, andAnotherVeryVeryLongCondition); + + if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + else if (condition) execute(); + + if (condition) execute(); + else if (condition) + execute(); + else if (condition) execute(); + else if (condition) + execute(); + else if (condition) execute(); + else + executeElse(); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/IfStatement2/fmt.sol b/crates/fmt-2/testdata/IfStatement2/fmt.sol new file mode 100644 index 0000000000000..10ae43601d4ad --- /dev/null +++ b/crates/fmt-2/testdata/IfStatement2/fmt.sol @@ -0,0 +1,7 @@ +contract IfStatement { + function test() external { + bool anotherLongCondition; + + if (condition && ((condition || anotherLongCondition))) execute(); + } +} diff --git a/crates/fmt-2/testdata/IfStatement2/original.sol b/crates/fmt-2/testdata/IfStatement2/original.sol new file mode 100644 index 0000000000000..df020c04bbe33 --- /dev/null +++ b/crates/fmt-2/testdata/IfStatement2/original.sol @@ -0,0 +1,10 @@ +contract IfStatement { + + function test() external { + bool anotherLongCondition; + + if (condition && ((condition || anotherLongCondition) + ) + ) execute(); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..1db94929ab7c7 --- /dev/null +++ b/crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol @@ -0,0 +1,21 @@ +// config: bracket_spacing = true +import "SomeFile.sol"; +import "SomeFile.sol"; +import "SomeFile.sol" as SomeOtherFile; +import "SomeFile.sol" as SomeOtherFile; +import "AnotherFile.sol" as SomeSymbol; +import "AnotherFile.sol" as SomeSymbol; +import { symbol1 as alias, symbol2 } from "File.sol"; +import { symbol1 as alias, symbol2 } from "File.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; diff --git a/crates/fmt-2/testdata/ImportDirective/fmt.sol b/crates/fmt-2/testdata/ImportDirective/fmt.sol new file mode 100644 index 0000000000000..4915b8ab203c8 --- /dev/null +++ b/crates/fmt-2/testdata/ImportDirective/fmt.sol @@ -0,0 +1,20 @@ +import "SomeFile.sol"; +import "SomeFile.sol"; +import "SomeFile.sol" as SomeOtherFile; +import "SomeFile.sol" as SomeOtherFile; +import "AnotherFile.sol" as SomeSymbol; +import "AnotherFile.sol" as SomeSymbol; +import {symbol1 as alias, symbol2} from "File.sol"; +import {symbol1 as alias, symbol2} from "File.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; diff --git a/crates/fmt-2/testdata/ImportDirective/original.sol b/crates/fmt-2/testdata/ImportDirective/original.sol new file mode 100644 index 0000000000000..0e18e10c14dab --- /dev/null +++ b/crates/fmt-2/testdata/ImportDirective/original.sol @@ -0,0 +1,10 @@ +import "SomeFile.sol"; +import 'SomeFile.sol'; +import "SomeFile.sol" as SomeOtherFile; +import 'SomeFile.sol' as SomeOtherFile; +import * as SomeSymbol from "AnotherFile.sol"; +import * as SomeSymbol from 'AnotherFile.sol'; +import {symbol1 as alias, symbol2} from "File.sol"; +import {symbol1 as alias, symbol2} from 'File.sol'; +import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from "File2.sol"; +import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; diff --git a/crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol b/crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol new file mode 100644 index 0000000000000..d1bf9852c02e5 --- /dev/null +++ b/crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol @@ -0,0 +1,21 @@ +// config: quote_style = "preserve" +import "SomeFile.sol"; +import 'SomeFile.sol'; +import "SomeFile.sol" as SomeOtherFile; +import 'SomeFile.sol' as SomeOtherFile; +import "AnotherFile.sol" as SomeSymbol; +import 'AnotherFile.sol' as SomeSymbol; +import {symbol1 as alias, symbol2} from "File.sol"; +import {symbol1 as alias, symbol2} from 'File.sol'; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from 'File2.sol'; diff --git a/crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol b/crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol new file mode 100644 index 0000000000000..10449e079ae81 --- /dev/null +++ b/crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol @@ -0,0 +1,21 @@ +// config: quote_style = "single" +import 'SomeFile.sol'; +import 'SomeFile.sol'; +import 'SomeFile.sol' as SomeOtherFile; +import 'SomeFile.sol' as SomeOtherFile; +import 'AnotherFile.sol' as SomeSymbol; +import 'AnotherFile.sol' as SomeSymbol; +import {symbol1 as alias, symbol2} from 'File.sol'; +import {symbol1 as alias, symbol2} from 'File.sol'; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from 'File2.sol'; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from 'File2.sol'; diff --git a/crates/fmt-2/testdata/InlineDisable/fmt.sol b/crates/fmt-2/testdata/InlineDisable/fmt.sol new file mode 100644 index 0000000000000..d7adea60b32da --- /dev/null +++ b/crates/fmt-2/testdata/InlineDisable/fmt.sol @@ -0,0 +1,507 @@ +pragma solidity ^0.5.2; + +// forgefmt: disable-next-line +pragma solidity ^0.5.2; + +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File2.sol"; + +// forgefmt: disable-next-line +import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; + +enum States { + State1, + State2, + State3, + State4, + State5, + State6, + State7, + State8, + State9 +} + +// forgefmt: disable-next-line +enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } + +// forgefmt: disable-next-line +bytes32 constant private BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + +// forgefmt: disable-start + +// comment1 + + +// comment2 +/* comment 3 */ /* + comment4 + */ // comment 5 + + +/// Doccomment 1 + /// Doccomment 2 + +/** + * docccoment 3 + */ + + +// forgefmt: disable-end + +// forgefmt: disable-start + +function test1() {} + +function test2() {} + +// forgefmt: disable-end + +contract Constructors is Ownable, Changeable { + //forgefmt: disable-next-item + function Constructors(variable1) public Changeable(variable1) Ownable() onlyOwner { + } + + //forgefmt: disable-next-item + constructor(variable1, variable2, variable3, variable4, variable5, variable6, variable7) public Changeable(variable1, variable2, variable3, variable4, variable5, variable6, variable7) Ownable() onlyOwner {} +} + +function test() { + uint256 pi_approx = 666 / 212; + uint256 pi_approx = /* forgefmt: disable-start */ 666 / 212; /* forgefmt: disable-end */ + + // forgefmt: disable-next-item + uint256 pi_approx = 666 / + 212; + + uint256 test_postfix = 1; // forgefmt: disable-start + // comment1 + // comment2 + // comment3 + // forgefmt: disable-end +} + +// forgefmt: disable-next-item +function testFunc(uint256 num, bytes32 data , address receiver) + public payable attr1 Cool( "hello" ) {} + +function testAttrs(uint256 num, bytes32 data, address receiver) + // forgefmt: disable-next-line + public payable attr1 Cool( "hello" ) {} + +// forgefmt: disable-next-line +function testParams(uint256 num, bytes32 data , address receiver) + public + payable + attr1 + Cool("hello") +{} + +function testDoWhile() external { + //forgefmt: disable-start + uint256 i; + do { "test"; } while (i != 0); + + do + {} + while + ( +i != 0); + + bool someVeryVeryLongCondition; + do { "test"; } while( + someVeryVeryLongCondition && !someVeryVeryLongCondition && +!someVeryVeryLongCondition && +someVeryVeryLongCondition); + + do i++; while(i < 10); + + do do i++; while (i < 30); while(i < 20); + //forgefmt: disable-end +} + +function forStatement() { + //forgefmt: disable-start + for + (uint256 i1 + ; i1 < 10; i1++) + { + i1++; + } + + uint256 i2; + for(++i2;i2<10;i2++) + + {} + + uint256 veryLongVariableName = 1000; + for ( uint256 i3; i3 < 10 + && veryLongVariableName>999 && veryLongVariableName< 1001 + ; i3++) + { i3 ++ ; } + + for (type(uint256).min;;) {} + + for (;;) { "test" ; } + + for (uint256 i4; i4< 10; i4++) i4++; + + for (uint256 i5; ;) + for (uint256 i6 = 10; i6 > i5; i6--) + i5++; + //forgefmt: disable-end +} + +function callArgTest() { + //forgefmt: disable-start + target.run{ gas: gasleft(), value: 1 wei }; + + target.run{gas:1,value:0x00}(); + + target.run{ + gas : 1000, + value: 1 ether + } (); + + target.run{ gas: estimate(), + value: value(1) }(); + + target.run { value: + value(1 ether), gas: veryAndVeryLongNameOfSomeGasEstimateFunction() } (); + + target.run /* comment 1 */ { value: /* comment2 */ 1 }; + + target.run { /* comment3 */ value: 1, // comment4 + gas: gasleft()}; + + target.run { + // comment5 + value: 1, + // comment6 + gas: gasleft()}; + //forgefmt: disable-end +} + +function ifTest() { + // forgefmt: disable-start + if (condition) + execute(); + else + executeElse(); + // forgefmt: disable-end + + /* forgefmt: disable-next-line */ + if (condition && anotherLongCondition ) { + execute(); + } +} + +function yulTest() { + // forgefmt: disable-start + assembly { + let payloadSize := sub(calldatasize(), 4) + calldatacopy(0, 4, payloadSize) + mstore(payloadSize, shl(96, caller())) + + let result := + delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) + + returndatacopy(0, 0, returndatasize()) + + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + // forgefmt: disable-end +} + +function literalTest() { + // forgefmt: disable-start + + true; + 0x123_456; + .1; + "foobar"; + hex"001122FF"; + 0xc02aaa39b223Fe8D0A0e5C4F27ead9083c756Cc2; + // forgefmt: disable-end + + // forgefmt: disable-next-line + bytes memory bytecode = hex"ff"; +} + +function returnTest() { + // forgefmt: disable-start + if (val == 0) { + return // return single 1 + 0x00; + } + + if (val == 1) { return + 1; } + + if (val == 2) { + return 3 + - + 1; + } + + if (val == 4) { + /* return single 2 */ return 2** // return single 3 + 3 // return single 4 + ; + } + + return value(); // return single 5 + return ; + return /* return mul 4 */ + ( + 987654321, 1234567890,/* return mul 5 */ false); + // forgefmt: disable-end +} + +function namedFuncCall() { + // forgefmt: disable-start + SimpleStruct memory simple = SimpleStruct({ val: 0 }); + + ComplexStruct memory complex = ComplexStruct({ val: 1, anotherVal: 2, flag: true, timestamp: block.timestamp }); + + StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting memory long = StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ whyNameSoLong: "dunno" }); + + SimpleStruct memory simple2 = SimpleStruct( + { // comment1 + /* comment2 */ val : /* comment3 */ 0 + + } + ); + // forgefmt: disable-end +} + +function revertTest() { + // forgefmt: disable-start + revert ({ }); + + revert EmptyError({}); + + revert SimpleError({ val: 0 }); + + revert ComplexError( + { + val: 0, + ts: block.timestamp, + message: "some reason" + }); + + revert SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ val: 0, ts: 0x00, message: "something unpredictable happened that caused execution to revert"}); + + revert // comment1 + ({}); + // forgefmt: disable-end +} + +function testTernary() { + // forgefmt: disable-start + bool condition; + bool someVeryVeryLongConditionUsedInTheTernaryExpression; + + condition ? 0 : 1; + + someVeryVeryLongConditionUsedInTheTernaryExpression ? 1234567890 : 987654321; + + condition /* comment1 */ ? /* comment2 */ 1001 /* comment3 */ : /* comment4 */ 2002; + + // comment5 + someVeryVeryLongConditionUsedInTheTernaryExpression ? 1 + // comment6 + : + // comment7 + 0; // comment8 + // forgefmt: disable-end +} + +function thisTest() { + // forgefmt: disable-start + this.someFunc(); + this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + this // comment1 + .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + address(this).balance; + + address thisAddress = address( + // comment2 + /* comment3 */ this // comment 4 + ); + // forgefmt: disable-end +} + +function tryTest() { + // forgefmt: disable-start + try unknown.empty() {} catch {} + + try unknown.lookup() returns (uint256) {} catch Error(string memory) {} + + try unknown.lookup() returns (uint256) {} catch Error(string memory) {} catch (bytes memory) {} + + try unknown + .lookup() returns (uint256 + ) { + } catch ( bytes memory ){} + + try unknown.empty() { + unknown.doSomething(); + } catch { + unknown.handleError(); + } + + try unknown.empty() { + unknown.doSomething(); + } catch Error(string memory) {} + catch Panic(uint) {} + catch { + unknown.handleError(); + } + + try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) {} catch Error(string memory) {} catch {} + + try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) { + unknown.doSomething(); + } + catch Error(string memory) { + unknown.handleError(); + } + catch {} + // forgefmt: disable-end +} + +function testArray() { + // forgefmt: disable-start + msg.data[ + // comment1 + 4:]; + msg.data[ + : /* comment2 */ msg.data.length // comment3 + ]; + msg.data[ + // comment4 + 4 // comment5 + :msg.data.length /* comment6 */]; + // forgefmt: disable-end +} + +function testUnit() { + // forgefmt: disable-start + uint256 timestamp; + timestamp = 1 seconds; + timestamp = 1 minutes; + timestamp = 1 hours; + timestamp = 1 days; + timestamp = 1 weeks; + + uint256 value; + value = 1 wei; + value = 1 gwei; + value = 1 ether; + + uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; + + value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue * 1 /* comment1 */ ether; // comment2 + + value = 1 // comment3 + // comment4 + ether; // comment5 + // forgefmt: disable-end +} + +contract UsingExampleContract { + // forgefmt: disable-start + using UsingExampleLibrary for * ; + using UsingExampleLibrary for uint; + using Example.UsingExampleLibrary for uint; + using { M.g, M.f} for uint; + using UsingExampleLibrary for uint global; + using { These, Are, MultipleLibraries, ThatNeedToBePut, OnSeparateLines } for uint; + using { This.isareally.longmember.access.expression.that.needs.to.besplit.into.lines } for uint; + // forgefmt: disable-end +} + +function testAssignment() { + // forgefmt: disable-start + (, uint256 second) = (1, 2); + (uint256 listItem001) = 1; + (uint256 listItem002, uint256 listItem003) = (10, 20); + (uint256 listItem004, uint256 listItem005, uint256 listItem006) = + (10, 20, 30); + // forgefmt: disable-end +} + +function testWhile() { + // forgefmt: disable-start + uint256 i1; + while ( i1 < 10 ) { + i1++; + } + + while (i1<10) i1++; + + while (i1<10) + while (i1<10) + i1++; + + uint256 i2; + while ( i2 < 10) { i2++; } + + uint256 i3; while ( + i3 < 10 + ) { i3++; } + + uint256 i4; while (i4 < 10) + + { i4 ++ ;} + + uint256 someLongVariableName; + while ( + someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12 + ) { someLongVariableName ++; } someLongVariableName++; + // forgefmt: disable-end +} + +function testLine() {} + +function /* forgefmt: disable-line */ testLine( ) { } + +function testLine() {} + +function testLine( ) { } // forgefmt: disable-line + +// forgefmt: disable-start + + type Hello is uint256; + +error + TopLevelCustomError(); + error TopLevelCustomErrorWithArg(uint x) ; +error TopLevelCustomErrorArgWithoutName (string); + + event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); + +// forgefmt: disable-stop + +function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line + number = newNumber; + return (true, true); +} + +function setNumber1(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line +} + +// forgefmt: disable-next-line +function setNumber1(uint256 newNumber , uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public view returns (bool,bool) { number1 = newNumber1; +} + +function setNumber(uint256 newNumber, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public { // forgefmt: disable-line + number = newNumber; + number1 = newNumber1; // forgefmt: disable-line +} diff --git a/crates/fmt-2/testdata/InlineDisable/original.sol b/crates/fmt-2/testdata/InlineDisable/original.sol new file mode 100644 index 0000000000000..7731678940cbf --- /dev/null +++ b/crates/fmt-2/testdata/InlineDisable/original.sol @@ -0,0 +1,486 @@ +pragma solidity ^0.5.2; + +// forgefmt: disable-next-line +pragma solidity ^0.5.2; + +import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; + +// forgefmt: disable-next-line +import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; + +enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } + +// forgefmt: disable-next-line +enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } + +// forgefmt: disable-next-line +bytes32 constant private BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + +// forgefmt: disable-start + +// comment1 + + +// comment2 +/* comment 3 */ /* + comment4 + */ // comment 5 + + +/// Doccomment 1 + /// Doccomment 2 + +/** + * docccoment 3 + */ + + +// forgefmt: disable-end + +// forgefmt: disable-start + +function test1() {} + +function test2() {} + +// forgefmt: disable-end + +contract Constructors is Ownable, Changeable { + //forgefmt: disable-next-item + function Constructors(variable1) public Changeable(variable1) Ownable() onlyOwner { + } + + //forgefmt: disable-next-item + constructor(variable1, variable2, variable3, variable4, variable5, variable6, variable7) public Changeable(variable1, variable2, variable3, variable4, variable5, variable6, variable7) Ownable() onlyOwner {} +} + +function test() { + uint256 pi_approx = 666 / 212; + uint256 pi_approx = /* forgefmt: disable-start */ 666 / 212; /* forgefmt: disable-end */ + + // forgefmt: disable-next-item + uint256 pi_approx = 666 / + 212; + + uint256 test_postfix = 1; // forgefmt: disable-start + // comment1 + // comment2 + // comment3 + // forgefmt: disable-end +} + +// forgefmt: disable-next-item +function testFunc(uint256 num, bytes32 data , address receiver) + public payable attr1 Cool( "hello" ) {} + +function testAttrs(uint256 num, bytes32 data , address receiver) + // forgefmt: disable-next-line + public payable attr1 Cool( "hello" ) {} + +// forgefmt: disable-next-line +function testParams(uint256 num, bytes32 data , address receiver) + public payable attr1 Cool( "hello" ) {} + + +function testDoWhile() external { + //forgefmt: disable-start + uint256 i; + do { "test"; } while (i != 0); + + do + {} + while + ( +i != 0); + + bool someVeryVeryLongCondition; + do { "test"; } while( + someVeryVeryLongCondition && !someVeryVeryLongCondition && +!someVeryVeryLongCondition && +someVeryVeryLongCondition); + + do i++; while(i < 10); + + do do i++; while (i < 30); while(i < 20); + //forgefmt: disable-end +} + +function forStatement() { + //forgefmt: disable-start + for + (uint256 i1 + ; i1 < 10; i1++) + { + i1++; + } + + uint256 i2; + for(++i2;i2<10;i2++) + + {} + + uint256 veryLongVariableName = 1000; + for ( uint256 i3; i3 < 10 + && veryLongVariableName>999 && veryLongVariableName< 1001 + ; i3++) + { i3 ++ ; } + + for (type(uint256).min;;) {} + + for (;;) { "test" ; } + + for (uint256 i4; i4< 10; i4++) i4++; + + for (uint256 i5; ;) + for (uint256 i6 = 10; i6 > i5; i6--) + i5++; + //forgefmt: disable-end +} + +function callArgTest() { + //forgefmt: disable-start + target.run{ gas: gasleft(), value: 1 wei }; + + target.run{gas:1,value:0x00}(); + + target.run{ + gas : 1000, + value: 1 ether + } (); + + target.run{ gas: estimate(), + value: value(1) }(); + + target.run { value: + value(1 ether), gas: veryAndVeryLongNameOfSomeGasEstimateFunction() } (); + + target.run /* comment 1 */ { value: /* comment2 */ 1 }; + + target.run { /* comment3 */ value: 1, // comment4 + gas: gasleft()}; + + target.run { + // comment5 + value: 1, + // comment6 + gas: gasleft()}; + //forgefmt: disable-end +} + +function ifTest() { + // forgefmt: disable-start + if (condition) + execute(); + else + executeElse(); + // forgefmt: disable-end + + /* forgefmt: disable-next-line */ + if (condition && anotherLongCondition ) { + execute(); } +} + +function yulTest() { + // forgefmt: disable-start + assembly { + let payloadSize := sub(calldatasize(), 4) + calldatacopy(0, 4, payloadSize) + mstore(payloadSize, shl(96, caller())) + + let result := + delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) + + returndatacopy(0, 0, returndatasize()) + + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + // forgefmt: disable-end +} + +function literalTest() { + // forgefmt: disable-start + + true; + 0x123_456; + .1; + "foobar"; + hex"001122FF"; + 0xc02aaa39b223Fe8D0A0e5C4F27ead9083c756Cc2; + // forgefmt: disable-end + + // forgefmt: disable-next-line + bytes memory bytecode = + hex"ff"; +} + +function returnTest() { + // forgefmt: disable-start + if (val == 0) { + return // return single 1 + 0x00; + } + + if (val == 1) { return + 1; } + + if (val == 2) { + return 3 + - + 1; + } + + if (val == 4) { + /* return single 2 */ return 2** // return single 3 + 3 // return single 4 + ; + } + + return value(); // return single 5 + return ; + return /* return mul 4 */ + ( + 987654321, 1234567890,/* return mul 5 */ false); + // forgefmt: disable-end +} + +function namedFuncCall() { + // forgefmt: disable-start + SimpleStruct memory simple = SimpleStruct({ val: 0 }); + + ComplexStruct memory complex = ComplexStruct({ val: 1, anotherVal: 2, flag: true, timestamp: block.timestamp }); + + StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting memory long = StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ whyNameSoLong: "dunno" }); + + SimpleStruct memory simple2 = SimpleStruct( + { // comment1 + /* comment2 */ val : /* comment3 */ 0 + + } + ); + // forgefmt: disable-end +} + +function revertTest() { + // forgefmt: disable-start + revert ({ }); + + revert EmptyError({}); + + revert SimpleError({ val: 0 }); + + revert ComplexError( + { + val: 0, + ts: block.timestamp, + message: "some reason" + }); + + revert SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ val: 0, ts: 0x00, message: "something unpredictable happened that caused execution to revert"}); + + revert // comment1 + ({}); + // forgefmt: disable-end +} + +function testTernary() { + // forgefmt: disable-start + bool condition; + bool someVeryVeryLongConditionUsedInTheTernaryExpression; + + condition ? 0 : 1; + + someVeryVeryLongConditionUsedInTheTernaryExpression ? 1234567890 : 987654321; + + condition /* comment1 */ ? /* comment2 */ 1001 /* comment3 */ : /* comment4 */ 2002; + + // comment5 + someVeryVeryLongConditionUsedInTheTernaryExpression ? 1 + // comment6 + : + // comment7 + 0; // comment8 + // forgefmt: disable-end +} + +function thisTest() { + // forgefmt: disable-start + this.someFunc(); + this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + this // comment1 + .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + address(this).balance; + + address thisAddress = address( + // comment2 + /* comment3 */ this // comment 4 + ); + // forgefmt: disable-end +} + +function tryTest() { + // forgefmt: disable-start + try unknown.empty() {} catch {} + + try unknown.lookup() returns (uint256) {} catch Error(string memory) {} + + try unknown.lookup() returns (uint256) {} catch Error(string memory) {} catch (bytes memory) {} + + try unknown + .lookup() returns (uint256 + ) { + } catch ( bytes memory ){} + + try unknown.empty() { + unknown.doSomething(); + } catch { + unknown.handleError(); + } + + try unknown.empty() { + unknown.doSomething(); + } catch Error(string memory) {} + catch Panic(uint) {} + catch { + unknown.handleError(); + } + + try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) {} catch Error(string memory) {} catch {} + + try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) { + unknown.doSomething(); + } + catch Error(string memory) { + unknown.handleError(); + } + catch {} + // forgefmt: disable-end +} + +function testArray() { + // forgefmt: disable-start + msg.data[ + // comment1 + 4:]; + msg.data[ + : /* comment2 */ msg.data.length // comment3 + ]; + msg.data[ + // comment4 + 4 // comment5 + :msg.data.length /* comment6 */]; + // forgefmt: disable-end +} + +function testUnit() { + // forgefmt: disable-start + uint256 timestamp; + timestamp = 1 seconds; + timestamp = 1 minutes; + timestamp = 1 hours; + timestamp = 1 days; + timestamp = 1 weeks; + + uint256 value; + value = 1 wei; + value = 1 gwei; + value = 1 ether; + + uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; + + value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue * 1 /* comment1 */ ether; // comment2 + + value = 1 // comment3 + // comment4 + ether; // comment5 + // forgefmt: disable-end +} + +contract UsingExampleContract { + // forgefmt: disable-start + using UsingExampleLibrary for * ; + using UsingExampleLibrary for uint; + using Example.UsingExampleLibrary for uint; + using { M.g, M.f} for uint; + using UsingExampleLibrary for uint global; + using { These, Are, MultipleLibraries, ThatNeedToBePut, OnSeparateLines } for uint; + using { This.isareally.longmember.access.expression.that.needs.to.besplit.into.lines } for uint; + // forgefmt: disable-end +} + +function testAssignment() { + // forgefmt: disable-start + (, uint256 second) = (1, 2); + (uint256 listItem001) = 1; + (uint256 listItem002, uint256 listItem003) = (10, 20); + (uint256 listItem004, uint256 listItem005, uint256 listItem006) = + (10, 20, 30); + // forgefmt: disable-end +} + +function testWhile() { + // forgefmt: disable-start + uint256 i1; + while ( i1 < 10 ) { + i1++; + } + + while (i1<10) i1++; + + while (i1<10) + while (i1<10) + i1++; + + uint256 i2; + while ( i2 < 10) { i2++; } + + uint256 i3; while ( + i3 < 10 + ) { i3++; } + + uint256 i4; while (i4 < 10) + + { i4 ++ ;} + + uint256 someLongVariableName; + while ( + someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12 + ) { someLongVariableName ++; } someLongVariableName++; + // forgefmt: disable-end +} + +function testLine( ) { } +function /* forgefmt: disable-line */ testLine( ) { } +function testLine( ) { } +function testLine( ) { } // forgefmt: disable-line + +// forgefmt: disable-start + + type Hello is uint256; + +error + TopLevelCustomError(); + error TopLevelCustomErrorWithArg(uint x) ; +error TopLevelCustomErrorArgWithoutName (string); + + event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); + +// forgefmt: disable-stop + +function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line + number = newNumber; + return (true, true); +} + +function setNumber1(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line +} + +// forgefmt: disable-next-line +function setNumber1(uint256 newNumber , uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public view returns (bool,bool) { number1 = newNumber1; +} + +function setNumber(uint256 newNumber, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public { // forgefmt: disable-line + number = newNumber; + number1 = newNumber1; // forgefmt: disable-line +} diff --git a/crates/fmt-2/testdata/IntTypes/fmt.sol b/crates/fmt-2/testdata/IntTypes/fmt.sol new file mode 100644 index 0000000000000..b244d0281d2e7 --- /dev/null +++ b/crates/fmt-2/testdata/IntTypes/fmt.sol @@ -0,0 +1,24 @@ +contract Contract { + uint256 constant UINT256_IMPL = 0; + uint8 constant UINT8 = 1; + uint128 constant UINT128 = 2; + uint256 constant UINT256_EXPL = 3; + + int256 constant INT256_IMPL = 4; + int8 constant INT8 = 5; + int128 constant INT128 = 6; + int256 constant INT256_EXPL = 7; + + function test( + uint256 uint256_impl, + uint8 uint8_var, + uint128 uint128_var, + uint256 uint256_expl, + int256 int256_impl, + int8 int8_var, + int128 int128_var, + int256 int256_expl + ) public { + // do something + } +} diff --git a/crates/fmt-2/testdata/IntTypes/original.sol b/crates/fmt-2/testdata/IntTypes/original.sol new file mode 100644 index 0000000000000..2990aadbb979d --- /dev/null +++ b/crates/fmt-2/testdata/IntTypes/original.sol @@ -0,0 +1,24 @@ +contract Contract { + uint constant UINT256_IMPL = 0; + uint8 constant UINT8 = 1; + uint128 constant UINT128 = 2; + uint256 constant UINT256_EXPL = 3; + + int constant INT256_IMPL = 4; + int8 constant INT8 = 5; + int128 constant INT128 = 6; + int256 constant INT256_EXPL = 7; + + function test( + uint uint256_impl, + uint8 uint8_var, + uint128 uint128_var, + uint256 uint256_expl, + int int256_impl, + int8 int8_var, + int128 int128_var, + int256 int256_expl + ) public { + // do something + } +} diff --git a/crates/fmt-2/testdata/IntTypes/preserve.fmt.sol b/crates/fmt-2/testdata/IntTypes/preserve.fmt.sol new file mode 100644 index 0000000000000..70d780d89e329 --- /dev/null +++ b/crates/fmt-2/testdata/IntTypes/preserve.fmt.sol @@ -0,0 +1,25 @@ +// config: int_types = "preserve" +contract Contract { + uint constant UINT256_IMPL = 0; + uint8 constant UINT8 = 1; + uint128 constant UINT128 = 2; + uint256 constant UINT256_EXPL = 3; + + int constant INT256_IMPL = 4; + int8 constant INT8 = 5; + int128 constant INT128 = 6; + int256 constant INT256_EXPL = 7; + + function test( + uint uint256_impl, + uint8 uint8_var, + uint128 uint128_var, + uint256 uint256_expl, + int int256_impl, + int8 int8_var, + int128 int128_var, + int256 int256_expl + ) public { + // do something + } +} diff --git a/crates/fmt-2/testdata/IntTypes/short.fmt.sol b/crates/fmt-2/testdata/IntTypes/short.fmt.sol new file mode 100644 index 0000000000000..45064d41e34ed --- /dev/null +++ b/crates/fmt-2/testdata/IntTypes/short.fmt.sol @@ -0,0 +1,25 @@ +// config: int_types = "short" +contract Contract { + uint constant UINT256_IMPL = 0; + uint8 constant UINT8 = 1; + uint128 constant UINT128 = 2; + uint constant UINT256_EXPL = 3; + + int constant INT256_IMPL = 4; + int8 constant INT8 = 5; + int128 constant INT128 = 6; + int constant INT256_EXPL = 7; + + function test( + uint uint256_impl, + uint8 uint8_var, + uint128 uint128_var, + uint uint256_expl, + int int256_impl, + int8 int8_var, + int128 int128_var, + int int256_expl + ) public { + // do something + } +} diff --git a/crates/fmt-2/testdata/LiteralExpression/fmt.sol b/crates/fmt-2/testdata/LiteralExpression/fmt.sol new file mode 100644 index 0000000000000..7fa6878e5586d --- /dev/null +++ b/crates/fmt-2/testdata/LiteralExpression/fmt.sol @@ -0,0 +1,59 @@ +contract LiteralExpressions { + function test() external { + // bool literals + true; + false; + /* comment1 */ + true; /* comment2 */ + // comment3 + false; // comment4 + + // number literals + 1; + 123_000; + 1_2e345_678; + -1; + 2e-10; + // comment5 + /* comment6 */ + -1; /* comment7 */ + + // hex number literals + 0x00; + 0x123_456; + 0x2eff_abde; + + // rational number literals + 0.1; + 1.3; + 2.5e1; + + // string literals + ""; + "foobar"; + "foo" // comment8 + " bar"; + // comment9 + "\ +some words"; /* comment10 */ + unicode"Hello 😃"; + + // quoted strings + 'hello "world"'; + "hello 'world'"; + "hello \'world\'"; + "hello \"world\""; + "hello \"world\""; + "hello \'world\'"; + + // hex literals + hex"001122FF"; + hex"001122FF"; + hex"00112233" hex"44556677"; + + // address literals + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // non checksummed address + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + } +} diff --git a/crates/fmt-2/testdata/LiteralExpression/original.sol b/crates/fmt-2/testdata/LiteralExpression/original.sol new file mode 100644 index 0000000000000..5c559531de955 --- /dev/null +++ b/crates/fmt-2/testdata/LiteralExpression/original.sol @@ -0,0 +1,58 @@ +contract LiteralExpressions { + function test() external { + // bool literals + true; + false; + /* comment1 */ true /* comment2 */; + // comment3 + false; // comment4 + + // number literals + 1; + 123_000; + 1_2e345_678; + -1; + 2e-10; + // comment5 + /* comment6 */ -1 /* comment7 */; + + // hex number literals + 0x00; + 0x123_456; + 0x2eff_abde; + + // rational number literals + .1; + 1.3; + 2.5e1; + + // string literals + ""; + "foobar"; + "foo" // comment8 + " bar"; + // comment9 + "\ +some words" /* comment10 */; + unicode"Hello 😃"; + + // quoted strings + 'hello "world"'; + "hello 'world'"; + 'hello \'world\''; + "hello \"world\""; + 'hello \"world\"'; + "hello \'world\'"; + + + // hex literals + hex"001122FF"; + hex'0011_22_FF'; + hex"00112233" hex"44556677"; + + // address literals + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // non checksummed address + 0xc02aaa39b223Fe8D0A0e5C4F27ead9083c756Cc2; + } +} diff --git a/crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol b/crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol new file mode 100644 index 0000000000000..3d9490804f231 --- /dev/null +++ b/crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol @@ -0,0 +1,60 @@ +// config: quote_style = "preserve" +contract LiteralExpressions { + function test() external { + // bool literals + true; + false; + /* comment1 */ + true; /* comment2 */ + // comment3 + false; // comment4 + + // number literals + 1; + 123_000; + 1_2e345_678; + -1; + 2e-10; + // comment5 + /* comment6 */ + -1; /* comment7 */ + + // hex number literals + 0x00; + 0x123_456; + 0x2eff_abde; + + // rational number literals + 0.1; + 1.3; + 2.5e1; + + // string literals + ""; + "foobar"; + "foo" // comment8 + " bar"; + // comment9 + "\ +some words"; /* comment10 */ + unicode"Hello 😃"; + + // quoted strings + 'hello "world"'; + "hello 'world'"; + 'hello \'world\''; + "hello \"world\""; + 'hello \"world\"'; + "hello \'world\'"; + + // hex literals + hex"001122FF"; + hex'001122FF'; + hex"00112233" hex"44556677"; + + // address literals + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // non checksummed address + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + } +} diff --git a/crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol b/crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol new file mode 100644 index 0000000000000..cdc67a2c6c814 --- /dev/null +++ b/crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol @@ -0,0 +1,60 @@ +// config: quote_style = "single" +contract LiteralExpressions { + function test() external { + // bool literals + true; + false; + /* comment1 */ + true; /* comment2 */ + // comment3 + false; // comment4 + + // number literals + 1; + 123_000; + 1_2e345_678; + -1; + 2e-10; + // comment5 + /* comment6 */ + -1; /* comment7 */ + + // hex number literals + 0x00; + 0x123_456; + 0x2eff_abde; + + // rational number literals + 0.1; + 1.3; + 2.5e1; + + // string literals + ''; + 'foobar'; + 'foo' // comment8 + ' bar'; + // comment9 + '\ +some words'; /* comment10 */ + unicode'Hello 😃'; + + // quoted strings + 'hello "world"'; + "hello 'world'"; + 'hello \'world\''; + 'hello \"world\"'; + 'hello \"world\"'; + 'hello \'world\''; + + // hex literals + hex'001122FF'; + hex'001122FF'; + hex'00112233' hex'44556677'; + + // address literals + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // non checksummed address + 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + } +} diff --git a/crates/fmt-2/testdata/MappingType/fmt.sol b/crates/fmt-2/testdata/MappingType/fmt.sol new file mode 100644 index 0000000000000..7f6297cff9455 --- /dev/null +++ b/crates/fmt-2/testdata/MappingType/fmt.sol @@ -0,0 +1,35 @@ +// config: line_length = 40 +contract X { + type Y is bytes32; +} + +type SomeVeryLongTypeName is uint256; + +contract Mapping { + mapping(uint256 => X.Y) mapping1; + mapping( + uint256 key => uint256 value + ) mapping2; + mapping( + uint256 veryLongKeyName + => uint256 veryLongValueName + ) mapping3; + mapping( + string anotherVeryLongKeyName + => uint256 anotherVeryLongValueName + ) mapping4; + mapping( + SomeVeryLongTypeName anotherVeryLongKeyName + => uint256 anotherVeryLongValueName + ) mapping5; + + mapping( + // comment1 + uint256 key => uint256 value + // comment2 + ) mapping6; + mapping( /* comment3 */ + uint256 /* comment4 */ key /* comment5 */ + => /* comment6 */ uint256 /* comment7 */ value /* comment8 */ /* comment9 */ + ) /* comment10 */ mapping7; +} diff --git a/crates/fmt-2/testdata/MappingType/original.sol b/crates/fmt-2/testdata/MappingType/original.sol new file mode 100644 index 0000000000000..58ebb134d6684 --- /dev/null +++ b/crates/fmt-2/testdata/MappingType/original.sol @@ -0,0 +1,23 @@ +contract X { + type Y is bytes32; +} + +type SomeVeryLongTypeName is uint256; + +contract Mapping { + mapping(uint256 => X.Y) mapping1; + mapping(uint256 key => uint256 value) mapping2; + mapping(uint256 veryLongKeyName => uint256 veryLongValueName) mapping3; + mapping(string anotherVeryLongKeyName => uint256 anotherVeryLongValueName) mapping4; + mapping(SomeVeryLongTypeName anotherVeryLongKeyName => uint256 anotherVeryLongValueName) mapping5; + + mapping( + + // comment1 + uint256 key => uint256 value +// comment2 + ) mapping6; + mapping( /* comment3 */ + uint256 /* comment4 */ key /* comment5 */ => /* comment6 */ uint256 /* comment7 */ value /* comment8 */ /* comment9 */ + ) /* comment10 */ mapping7; +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ModifierDefinition/fmt.sol b/crates/fmt-2/testdata/ModifierDefinition/fmt.sol new file mode 100644 index 0000000000000..e735f25b4c51d --- /dev/null +++ b/crates/fmt-2/testdata/ModifierDefinition/fmt.sol @@ -0,0 +1,14 @@ +// config: line_length = 60 +contract ModifierDefinitions { + modifier noParams() {} + modifier oneParam(uint256 a) {} + modifier twoParams(uint256 a, uint256 b) {} + modifier threeParams(uint256 a, uint256 b, uint256 c) {} + modifier fourParams( + uint256 a, + uint256 b, + uint256 c, + uint256 d + ) {} + modifier overridden() override(Base1, Base2) {} +} diff --git a/crates/fmt-2/testdata/ModifierDefinition/original.sol b/crates/fmt-2/testdata/ModifierDefinition/original.sol new file mode 100644 index 0000000000000..f4a1c428429bb --- /dev/null +++ b/crates/fmt-2/testdata/ModifierDefinition/original.sol @@ -0,0 +1,9 @@ +contract ModifierDefinitions { + modifier noParams() {} + modifier oneParam(uint a) {} + modifier twoParams(uint a,uint b) {} + modifier threeParams(uint a,uint b ,uint c) {} + modifier fourParams(uint a,uint b ,uint c, uint d) {} + modifier overridden ( + ) override ( Base1 , Base2) {} +} diff --git a/crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol b/crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol new file mode 100644 index 0000000000000..68906fcdc0f65 --- /dev/null +++ b/crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol @@ -0,0 +1,15 @@ +// config: line_length = 60 +// config: override_spacing = true +contract ModifierDefinitions { + modifier noParams() {} + modifier oneParam(uint256 a) {} + modifier twoParams(uint256 a, uint256 b) {} + modifier threeParams(uint256 a, uint256 b, uint256 c) {} + modifier fourParams( + uint256 a, + uint256 b, + uint256 c, + uint256 d + ) {} + modifier overridden() override (Base1, Base2) {} +} diff --git a/crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol b/crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol new file mode 100644 index 0000000000000..14a24c9003888 --- /dev/null +++ b/crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol @@ -0,0 +1,47 @@ +contract NamedFunctionCallExpression { + struct SimpleStruct { + uint256 val; + } + + struct ComplexStruct { + uint256 val; + uint256 anotherVal; + bool flag; + uint256 timestamp; + } + + struct + StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting { + string whyNameSoLong; + } + + function test() external { + SimpleStruct memory simple = SimpleStruct({val: 0}); + + ComplexStruct memory complex = ComplexStruct({ + val: 1, + anotherVal: 2, + flag: true, + timestamp: block.timestamp + }); + + StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting + memory long = + StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ + whyNameSoLong: "dunno" + }); + + SimpleStruct memory simple2 = SimpleStruct({ // comment1 + /* comment2 */ + val: /* comment3 */ 0 + }); + + SimpleStruct memory simple3 = SimpleStruct({ + /* comment4 */ + // comment5 + val: // comment6 + 0 // comment7 + // comment8 + }); + } +} diff --git a/crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol b/crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol new file mode 100644 index 0000000000000..8b34474a7134f --- /dev/null +++ b/crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol @@ -0,0 +1,28 @@ +contract NamedFunctionCallExpression { + struct SimpleStruct { uint256 val; } + struct ComplexStruct { uint256 val; uint256 anotherVal; bool flag; uint256 timestamp; } + struct StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting { string whyNameSoLong; } + + function test() external { + SimpleStruct memory simple = SimpleStruct({ val: 0 }); + + ComplexStruct memory complex = ComplexStruct({ val: 1, anotherVal: 2, flag: true, timestamp: block.timestamp }); + + StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting memory long = StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ whyNameSoLong: "dunno" }); + + SimpleStruct memory simple2 = SimpleStruct( + { // comment1 + /* comment2 */ val : /* comment3 */ 0 + + } + ); + + SimpleStruct memory simple3 = SimpleStruct( + /* comment4 */ { + // comment5 + val: // comment6 + 0 // comment7 + // comment8 + }); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol new file mode 100644 index 0000000000000..7c9f5740de76a --- /dev/null +++ b/crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol @@ -0,0 +1,25 @@ +contract NumberLiteral { + function test() external { + 1; + 123_000; + 1_2e345_678; + -1; + 2e-10; + 0.1; + 1.3; + 2.5e1; + 1.23454; + 1.2e34_5_678; + 134411.2e34_5_678; + 13431.134112e34_135_678; + 13431.0134112; + 13431.134112e-139_3141340; + 134411.2e34_5_6780; + 13431.134112e34_135_6780; + 0.134112; + 1.0; + 13431.134112e-139_3141340; + 123e456; + 1_000; + } +} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol new file mode 100644 index 0000000000000..8e88fc6d29472 --- /dev/null +++ b/crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol @@ -0,0 +1,25 @@ +contract NumberLiteral { + function test() external { + 1; + 123_000; + 1_2e345_678; + -1; + 2e-10; + .1; + 1.3; + 2.5e1; + 1.23454e0; + 1.2e34_5_678; + 134411.2e34_5_678; + 13431.134112e34_135_678; + 13431.0134112; + 13431.134112e-139_3141340; + 00134411.200e0034_5_6780; + 013431.13411200e34_135_6780; + 00.1341120000; + 1.0000; + 0013431.13411200e-00139_3141340; + 123E456; + 1_000; + } +} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol new file mode 100644 index 0000000000000..d87dc99d9653d --- /dev/null +++ b/crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol @@ -0,0 +1,26 @@ +// config: number_underscore = "preserve" +contract NumberLiteral { + function test() external { + 1; + 123_000; + 1_2e345_678; + -1; + 2e-10; + 0.1; + 1.3; + 2.5e1; + 1.23454; + 1.2e34_5_678; + 134411.2e34_5_678; + 13431.134112e34_135_678; + 13431.0134112; + 13431.134112e-139_3141340; + 134411.2e34_5_6780; + 13431.134112e34_135_6780; + 0.134112; + 1.0; + 13431.134112e-139_3141340; + 123e456; + 1_000; + } +} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol new file mode 100644 index 0000000000000..cbde2e9b9fe61 --- /dev/null +++ b/crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol @@ -0,0 +1,26 @@ +// config: number_underscore = "remove" +contract NumberLiteral { + function test() external { + 1; + 123000; + 12e345678; + -1; + 2e-10; + 0.1; + 1.3; + 2.5e1; + 1.23454; + 1.2e345678; + 134411.2e345678; + 13431.134112e34135678; + 13431.0134112; + 13431.134112e-1393141340; + 134411.2e3456780; + 13431.134112e341356780; + 0.134112; + 1.0; + 13431.134112e-1393141340; + 123e456; + 1000; + } +} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol new file mode 100644 index 0000000000000..a9fc8a69ae6fa --- /dev/null +++ b/crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol @@ -0,0 +1,26 @@ +// config: number_underscore = "thousands" +contract NumberLiteral { + function test() external { + 1; + 123_000; + 12e345_678; + -1; + 2e-10; + 0.1; + 1.3; + 2.5e1; + 1.23454; + 1.2e345_678; + 134_411.2e345_678; + 13_431.134112e34_135_678; + 13_431.0134112; + 13_431.134112e-1_393_141_340; + 134_411.2e3_456_780; + 13_431.134112e341_356_780; + 0.134112; + 1.0; + 13_431.134112e-1_393_141_340; + 123e456; + 1000; + } +} diff --git a/crates/fmt-2/testdata/OperatorExpressions/fmt.sol b/crates/fmt-2/testdata/OperatorExpressions/fmt.sol new file mode 100644 index 0000000000000..be0cec9173d55 --- /dev/null +++ b/crates/fmt-2/testdata/OperatorExpressions/fmt.sol @@ -0,0 +1,43 @@ +function test() { + uint256 expr001 = (1 + 2) + 3; + uint256 expr002 = 1 + (2 + 3); + uint256 expr003 = 1 * 2 + 3; + uint256 expr004 = (1 * 2) + 3; + uint256 expr005 = 1 * (2 + 3); + uint256 expr006 = 1 + 2 * 3; + uint256 expr007 = (1 + 2) * 3; + uint256 expr008 = 1 + (2 * 3); + uint256 expr009 = 1 ** 2 ** 3; + uint256 expr010 = 1 ** (2 ** 3); + uint256 expr011 = (1 ** 2) ** 3; + uint256 expr012 = ++expr011 + 1; + bool expr013 = ++expr012 == expr011 - 1; + bool expr014 = ++(++expr013)--; + if (++batch.movesPerformed == drivers.length) createNewBatch(); + sum += getPrice( + ACCELERATE_STARTING_PRICE, + ACCELERATE_PER_PERIOD_DECREASE, + idleTicks, + actionsSold[ActionType.ACCELERATE] + i, + ACCELERATE_SELL_PER_TICK + ) / 1e18; + other += 1e18 + / getPrice( + ACCELERATE_STARTING_PRICE, + ACCELERATE_PER_PERIOD_DECREASE, + idleTicks, + actionsSold[ActionType.ACCELERATE] + i, + ACCELERATE_SELL_PER_TICK + ); + if ( + op == 0x54 // SLOAD + || op == 0x55 // SSTORE + || op == 0xF0 // CREATE + || op == 0xF1 // CALL + || op == 0xF2 // CALLCODE + || op == 0xF4 // DELEGATECALL + || op == 0xF5 // CREATE2 + || op == 0xFA // STATICCALL + || op == 0xFF // SELFDESTRUCT + ) return false; +} diff --git a/crates/fmt-2/testdata/OperatorExpressions/original.sol b/crates/fmt-2/testdata/OperatorExpressions/original.sol new file mode 100644 index 0000000000000..c3e788d0a9385 --- /dev/null +++ b/crates/fmt-2/testdata/OperatorExpressions/original.sol @@ -0,0 +1,30 @@ +function test() { + uint256 expr001 = (1 + 2) + 3; + uint256 expr002 = 1 + (2 + 3); + uint256 expr003 = 1 * 2 + 3; + uint256 expr004 = (1 * 2) + 3; + uint256 expr005 = 1 * (2 + 3); + uint256 expr006 = 1 + 2 * 3; + uint256 expr007 = (1 + 2) * 3; + uint256 expr008 = 1 + (2 * 3); + uint256 expr009 = 1 ** 2 ** 3; + uint256 expr010 = 1 ** (2 ** 3); + uint256 expr011 = (1 ** 2) ** 3; + uint256 expr012 = ++expr011 + 1; + bool expr013 = ++expr012 == expr011 - 1; + bool expr014 = ++(++expr013)--; + if (++batch.movesPerformed == drivers.length) createNewBatch(); + sum += getPrice(ACCELERATE_STARTING_PRICE, ACCELERATE_PER_PERIOD_DECREASE, idleTicks, actionsSold[ActionType.ACCELERATE] + i, ACCELERATE_SELL_PER_TICK) / 1e18; + other += 1e18 / getPrice(ACCELERATE_STARTING_PRICE, ACCELERATE_PER_PERIOD_DECREASE, idleTicks, actionsSold[ActionType.ACCELERATE] + i, ACCELERATE_SELL_PER_TICK); + if ( + op == 0x54 // SLOAD + || op == 0x55 // SSTORE + || op == 0xF0 // CREATE + || op == 0xF1 // CALL + || op == 0xF2 // CALLCODE + || op == 0xF4 // DELEGATECALL + || op == 0xF5 // CREATE2 + || op == 0xFA // STATICCALL + || op == 0xFF // SELFDESTRUCT + ) return false; +} diff --git a/crates/fmt-2/testdata/PragmaDirective/fmt.sol b/crates/fmt-2/testdata/PragmaDirective/fmt.sol new file mode 100644 index 0000000000000..645a4f4906c00 --- /dev/null +++ b/crates/fmt-2/testdata/PragmaDirective/fmt.sol @@ -0,0 +1,9 @@ +pragma solidity 0.8.17; +pragma experimental ABIEncoderV2; + +contract Contract {} + +// preserves lines +pragma solidity 0.8.17; + +pragma experimental ABIEncoderV2; diff --git a/crates/fmt-2/testdata/PragmaDirective/original.sol b/crates/fmt-2/testdata/PragmaDirective/original.sol new file mode 100644 index 0000000000000..535b709590908 --- /dev/null +++ b/crates/fmt-2/testdata/PragmaDirective/original.sol @@ -0,0 +1,9 @@ +pragma solidity 0.8.17; +pragma experimental ABIEncoderV2; + +contract Contract {} + +// preserves lines +pragma solidity 0.8.17; + +pragma experimental ABIEncoderV2; \ No newline at end of file diff --git a/crates/fmt-2/testdata/Repros/fmt.sol b/crates/fmt-2/testdata/Repros/fmt.sol new file mode 100644 index 0000000000000..0a480c0b02bdf --- /dev/null +++ b/crates/fmt-2/testdata/Repros/fmt.sol @@ -0,0 +1,161 @@ +// Repros of fmt issues + +// https://github.com/foundry-rs/foundry/issues/4403 +function errorIdentifier() { + bytes memory error = bytes(""); + if (error.length > 0) {} +} + +// https://github.com/foundry-rs/foundry/issues/7549 +function one() external { + this.other({ + data: abi.encodeCall( + this.other, + ( + "bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla" + ) + ) + }); +} + +// https://github.com/foundry-rs/foundry/issues/3979 +contract Format { + bool public test; + + function testing(uint256 amount) public payable { + if ( + // This is a comment + msg.value == amount + ) { + test = true; + } else { + test = false; + } + + if ( + // Another one + block.timestamp >= amount + ) {} + } +} + +// https://github.com/foundry-rs/foundry/issues/3830 +contract TestContract { + function test(uint256 a) public { + if (a > 1) { + a = 2; + } // forgefmt: disable-line + } + + function test1() public { + assembly{ sstore( 1, 1) /* inline comment*/ // forgefmt: disable-line + sstore(2, 2) + } + } + + function test2() public { + assembly{ sstore( 1, 1) // forgefmt: disable-line + sstore(2, 2) + sstore(3, 3)// forgefmt: disable-line + sstore(4, 4) + } + } + + function test3() public { + // forgefmt: disable-next-line + assembly{ sstore( 1, 1) + sstore(2, 2) + sstore(3, 3)// forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + } + + function test4() public { + // forgefmt: disable-next-line + assembly{ + sstore(1, 1) + sstore(2, 2) + sstore(3, 3)// forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + if (condition) execute(); // comment7 + } + + function test5() public { + assembly { sstore(0, 0) }// forgefmt: disable-line + } + + function test6() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + } + return true ; } // forgefmt: disable-line + + function test7() returns (bool) { // forgefmt: disable-line + if (true) { // forgefmt: disable-line + uint256 a = 1; // forgefmt: disable-line + } + return true; + } + + function test8() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + uint256 a = 1; + } else { + uint256 b = 1; // forgefmt: disable-line + } + return true; + } +} + +// https://github.com/foundry-rs/foundry/issues/5825 +library MyLib { + bytes32 private constant TYPE_HASH = keccak256( + // forgefmt: disable-start + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + // forgefmt: disable-end + ); + + bytes32 private constant TYPE_HASH_1 = keccak256( + "MyStruct(" "uint8 myEnum," "address myAddress" ")" // forgefmt: disable-line + ); + + // forgefmt: disable-start + bytes32 private constant TYPE_HASH_2 = keccak256( + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + ); + // forgefmt: disable-end +} + +contract IfElseTest { + function setNumber(uint256 newNumber) public { + number = newNumber; + if (newNumber = 1) { + number = 1; + } else if (newNumber = 2) { + // number = 2; + } else { + newNumber = 3; + } + } +} + +contract DbgFmtTest is Test { + function test_argsList() public { + uint256 result1 = internalNoArgs({}); + result2 = add({a: 1, b: 2}); + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + function internalNoArgs() internal pure returns (uint256) { + return 0; + } +} diff --git a/crates/fmt-2/testdata/Repros/original.sol b/crates/fmt-2/testdata/Repros/original.sol new file mode 100644 index 0000000000000..6f18784d39dea --- /dev/null +++ b/crates/fmt-2/testdata/Repros/original.sol @@ -0,0 +1,160 @@ +// Repros of fmt issues + +// https://github.com/foundry-rs/foundry/issues/4403 +function errorIdentifier() { + bytes memory error = bytes(""); + if (error.length > 0) {} +} + +// https://github.com/foundry-rs/foundry/issues/7549 +function one() external { + this.other({ + data: abi.encodeCall( + this.other, + ( + "bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla" + ) + ) + }); +} + +// https://github.com/foundry-rs/foundry/issues/3979 +contract Format { + bool public test; + + function testing(uint256 amount) public payable { + if ( + // This is a comment + msg.value == amount + ) { + test = true; + } else { + test = false; + } + + if ( + // Another one + block.timestamp >= amount + ) {} + } +} + +// https://github.com/foundry-rs/foundry/issues/3830 +contract TestContract { + function test(uint256 a) public { + if (a > 1) { + a = 2; + } // forgefmt: disable-line + } + + function test1() public { + assembly { sstore( 1, 1) /* inline comment*/ // forgefmt: disable-line + sstore(2, 2) + } + } + + function test2() public { + assembly { sstore( 1, 1) // forgefmt: disable-line + sstore(2, 2) + sstore(3, 3) // forgefmt: disable-line + sstore(4, 4) + } + } + + function test3() public { + // forgefmt: disable-next-line + assembly{ sstore( 1, 1) + sstore(2, 2) + sstore(3, 3) // forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + } + + function test4() public { + // forgefmt: disable-next-line + assembly { + sstore( 1, 1) + sstore(2, 2) + sstore(3, 3) // forgefmt: disable-line + sstore(4, 4) + }// forgefmt: disable-line + if (condition) execute(); // comment7 + } + + function test5() public { + assembly { sstore(0, 0) }// forgefmt: disable-line + } + + function test6() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + } + return true ; } // forgefmt: disable-line + + function test7() returns (bool) { // forgefmt: disable-line + if (true) { // forgefmt: disable-line + uint256 a = 1; // forgefmt: disable-line + } + return true ; } + + function test8() returns (bool) { // forgefmt: disable-line + if ( true ) { // forgefmt: disable-line + uint256 a = 1; + } else { + uint256 b = 1; // forgefmt: disable-line + } + return true ; } +} + +// https://github.com/foundry-rs/foundry/issues/5825 +library MyLib { + bytes32 private constant TYPE_HASH = keccak256( + // forgefmt: disable-start + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + // forgefmt: disable-end + ); + + bytes32 private constant TYPE_HASH_1 = keccak256( + "MyStruct(" "uint8 myEnum," "address myAddress" ")" // forgefmt: disable-line + ); + + // forgefmt: disable-start + bytes32 private constant TYPE_HASH_2 = keccak256( + "MyStruct(" + "uint8 myEnum," + "address myAddress" + ")" + ); + // forgefmt: disable-end +} + +contract IfElseTest { + function setNumber(uint256 newNumber) public { + number = newNumber; + if (newNumber = 1) { + number = 1; + } else if (newNumber = 2) { + // number = 2; + } + else { + newNumber = 3; + } + } +} + +contract DbgFmtTest is Test { + function test_argsList() public { + uint256 result1 = internalNoArgs({}); + result2 = add({a: 1, b: 2}); + } + + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + function internalNoArgs() internal pure returns (uint256) { + return 0; + } +} diff --git a/crates/fmt-2/testdata/ReturnStatement/fmt.sol b/crates/fmt-2/testdata/ReturnStatement/fmt.sol new file mode 100644 index 0000000000000..d628d6097233d --- /dev/null +++ b/crates/fmt-2/testdata/ReturnStatement/fmt.sol @@ -0,0 +1,66 @@ +contract ReturnStatement { + function value() internal returns (uint256) { + return type(uint256).max; + } + + function returnEmpty() external { + if (true) { + return; + } + + if (false) { + // return empty 1 + return; /* return empty 2 */ // return empty 3 + } + + /* return empty 4 */ + return; // return empty 5 + } + + function returnSingleValue(uint256 val) external returns (uint256) { + if (val == 0) { + return // return single 1 + 0x00; + } + + if (val == 1) return 1; + + if (val == 2) { + return 3 - 1; + } + + if (val == 4) { + /* return single 2 */ + return 2 // return single 3 + ** 3; // return single 4 + } + + return value(); // return single 5 + } + + function returnMultipleValues(uint256 val) + external + returns (uint256, uint256, bool) + { + if (val == 0) { + return /* return mul 1 */ (0, 1, /* return mul 2 */ false); + } + + if (val == 1) { + // return mul 3 + return /* return mul 4 */ + (987654321, 1234567890, /* return mul 5 */ false); + } + + if (val == 2) { + return /* return mul 6 */ ( + 1234567890 + 987654321 + 87654123536, + 987654321 + 1234567890 + 124245235235, + true + ); + } + + return someFunction().getValue().modifyValue().negate() + .scaleBySomeFactor(1000).transformToTuple(); + } +} diff --git a/crates/fmt-2/testdata/ReturnStatement/original.sol b/crates/fmt-2/testdata/ReturnStatement/original.sol new file mode 100644 index 0000000000000..9cfaa82d6c524 --- /dev/null +++ b/crates/fmt-2/testdata/ReturnStatement/original.sol @@ -0,0 +1,60 @@ +contract ReturnStatement { + function value() internal returns (uint256) { + return type(uint256).max; + } + + function returnEmpty() external { + if (true) { + return ; + } + + if (false) { + // return empty 1 + return /* return empty 2 */ ; // return empty 3 + } + + /* return empty 4 */ return // return empty 5 + ; + } + + function returnSingleValue(uint256 val) external returns (uint256) { + if (val == 0) { + return // return single 1 + 0x00; + } + + if (val == 1) { return + 1; } + + if (val == 2) { + return 3 + - + 1; + } + + if (val == 4) { + /* return single 2 */ return 2** // return single 3 + 3 // return single 4 + ; + } + + return value() // return single 5 + ; + } + + function returnMultipleValues(uint256 val) external returns (uint256, uint256, bool) { + if (val == 0) { return /* return mul 1 */ (0, 1,/* return mul 2 */ false); } + + if (val == 1) { + // return mul 3 + return /* return mul 4 */ + ( + 987654321, 1234567890,/* return mul 5 */ false); } + + if (val == 2) { + return /* return mul 6 */ ( 1234567890 + 987654321 + 87654123536, 987654321 + 1234567890 + 124245235235, true); + } + + return someFunction().getValue().modifyValue().negate().scaleBySomeFactor(1000).transformToTuple(); + } +} diff --git a/crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol b/crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol new file mode 100644 index 0000000000000..9ad6b042b731a --- /dev/null +++ b/crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol @@ -0,0 +1,35 @@ +contract RevertNamedArgsStatement { + error EmptyError(); + error SimpleError(uint256 val); + error ComplexError(uint256 val, uint256 ts, string message); + error SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength( + uint256 val, uint256 ts, string message + ); + + function test() external { + revert({}); + + revert EmptyError({}); + + revert SimpleError({val: 0}); + + revert ComplexError({ + val: 0, + ts: block.timestamp, + message: "some reason" + }); + + revert + SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ + val: 0, + ts: 0x00, + message: "something unpredictable happened that caused execution to revert" + }); + + revert({}); // comment1 + + revert /* comment2 */ SimpleError({ /* comment3 */ // comment4 + val: 0 // comment 5 + }); + } +} diff --git a/crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol b/crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol new file mode 100644 index 0000000000000..2c9e35ba3d16c --- /dev/null +++ b/crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol @@ -0,0 +1,32 @@ +contract RevertNamedArgsStatement { + error EmptyError(); + error SimpleError(uint256 val); + error ComplexError(uint256 val, uint256 ts, string message); + error SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength( + uint256 val, uint256 ts, string message + ); + + function test() external { + revert ({ }); + + revert EmptyError({}); + + revert SimpleError({ val: 0 }); + + revert ComplexError( + { + val: 0, + ts: block.timestamp, + message: "some reason" + }); + + revert SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ val: 0, ts: 0x00, message: "something unpredictable happened that caused execution to revert"}); + + revert // comment1 + ({}); + + revert /* comment2 */ SimpleError /* comment3 */ ({ // comment4 + val:0 // comment 5 + }); + } +} diff --git a/crates/fmt-2/testdata/RevertStatement/fmt.sol b/crates/fmt-2/testdata/RevertStatement/fmt.sol new file mode 100644 index 0000000000000..3a677a5cadfdb --- /dev/null +++ b/crates/fmt-2/testdata/RevertStatement/fmt.sol @@ -0,0 +1,56 @@ +contract RevertStatement { + error TestError(uint256, bool, string); + + function someVeryLongFunctionNameToGetDynamicErrorMessageString() + public + returns (string memory) + { + return ""; + } + + function test(string memory message) external { + revert(); + + revert( /* comment1 */ ); + + revert(); + + // comment2 + revert( + // comment3 + ); + + revert(message); + + revert( + // comment4 + message // comment5 /* comment6 */ + ); + + revert( /* comment7 */ /* comment8 */ message /* comment9 */ ); /* comment10 */ // comment11 + + revert( + string.concat( + message, + someVeryLongFunctionNameToGetDynamicErrorMessageString( + /* comment12 */ + ) + ) + ); + + revert TestError(0, false, message); + revert TestError( + 0, false, someVeryLongFunctionNameToGetDynamicErrorMessageString() + ); + + revert /* comment13 */ /* comment14 */ TestError( /* comment15 */ + 1234567890, false, message + ); + + revert TestError( /* comment16 */ + 1, + true, + someVeryLongFunctionNameToGetDynamicErrorMessageString() /* comment17 */ + ); + } +} diff --git a/crates/fmt-2/testdata/RevertStatement/original.sol b/crates/fmt-2/testdata/RevertStatement/original.sol new file mode 100644 index 0000000000000..3426fdb1e5ad5 --- /dev/null +++ b/crates/fmt-2/testdata/RevertStatement/original.sol @@ -0,0 +1,44 @@ +contract RevertStatement { + error TestError(uint256,bool,string); + + function someVeryLongFunctionNameToGetDynamicErrorMessageString() public returns (string memory) { + return ""; + } + + function test(string memory message) external { + revert ( ) ; + + revert ( /* comment1 */ ); + + revert + ( + + ) + ; + + // comment2 + revert ( + // comment3 + ); + + + revert ( message ); + + revert ( + // comment4 + message // comment5 /* comment6 */ + ); + + revert /* comment7 */ ( /* comment8 */ message /* comment9 */ ) /* comment10 */; // comment11 + + revert ( string.concat( message , someVeryLongFunctionNameToGetDynamicErrorMessageString( /* comment12 */)) ); + + revert TestError(0, false, message); + revert TestError(0, false, someVeryLongFunctionNameToGetDynamicErrorMessageString()); + + revert /* comment13 */ /* comment14 */ TestError /* comment15 */(1234567890, false, message); + + + revert TestError ( /* comment16 */ 1, true, someVeryLongFunctionNameToGetDynamicErrorMessageString() /* comment17 */); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/SimpleComments/fmt.sol b/crates/fmt-2/testdata/SimpleComments/fmt.sol new file mode 100644 index 0000000000000..6e8d5195bd143 --- /dev/null +++ b/crates/fmt-2/testdata/SimpleComments/fmt.sol @@ -0,0 +1,80 @@ +contract SimpleComments { + mapping(address /* asset */ => address /* router */) public router; + + constructor() { + // TODO: do this and that + + uint256 a = 1; + + // TODO: do that and this + // or maybe + // smth else + } + + function test() public view { + // do smth here + + // then here + + // cleanup + } + + function test2() public pure { + uint256 a = 1; + // comment 1 + // comment 2 + uint256 b = 2; + } + + function test3() public view { + uint256 a = 1; // comment + + // line comment + } + + function test4() public view returns (uint256) { + uint256 abc; // long postfix comment that exceeds line width. the comment should be split and carried over to the next line + uint256 abc2; // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline + + // long prefix comment that exceeds line width. the comment should be split and carried over to the next line + // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline + uint256 c; + + /* a really really long prefix block comment that exceeds line width */ + uint256 d; /* a really really long postfix block comment that exceeds line width */ + + uint256 value; + return /* a long block comment that exceeds line width */ value; + return /* a block comment that exceeds line width */ value; + return // a line comment that exceeds line width + value; + } +} + +/* + +██████╗ ██████╗ ██████╗ ████████╗███████╗███████╗████████╗ +██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ +██████╔╝██████╔╝██████╔╝ ██║ █████╗ ███████╗ ██║ +██╔═══╝ ██╔══██╗██╔══██╗ ██║ ██╔══╝ ╚════██║ ██║ +██║ ██║ ██║██████╔╝ ██║ ███████╗███████║ ██║ +╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ +*/ +function asciiArt() {} + +/* + * @notice Here is my comment + * - item 1 + * - item 2 + * Some equations: + * y = mx + b + */ +function test() {} +// comment after function + +// comment with extra newlines + +// some comment +// another comment + +// eof comment diff --git a/crates/fmt-2/testdata/SimpleComments/original.sol b/crates/fmt-2/testdata/SimpleComments/original.sol new file mode 100644 index 0000000000000..d41c686b2f783 --- /dev/null +++ b/crates/fmt-2/testdata/SimpleComments/original.sol @@ -0,0 +1,83 @@ +contract SimpleComments { + mapping(address /* asset */ => address /* router */) public router; + + + constructor() { + // TODO: do this and that + + uint256 a = 1; + + // TODO: do that and this + // or maybe + // smth else + } + + function test() public view { + // do smth here + + // then here + + // cleanup + } + + function test2() public pure { + uint a = 1; + // comment 1 + // comment 2 + uint b = 2; + } + + function test3() public view { + uint256 a = 1; // comment + + // line comment + } + + function test4() public view returns (uint256) { + uint256 abc; // long postfix comment that exceeds line width. the comment should be split and carried over to the next line + uint256 abc2; // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline + + // long prefix comment that exceeds line width. the comment should be split and carried over to the next line + // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline + uint256 c; + + /* a really really long prefix block comment that exceeds line width */ + uint256 d; /* a really really long postfix block comment that exceeds line width */ + + uint256 value; + return /* a long block comment that exceeds line width */ value; + return /* a block comment that exceeds line width */ value; + return // a line comment that exceeds line width + value; + } +} + +/* + +██████╗ ██████╗ ██████╗ ████████╗███████╗███████╗████████╗ +██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ +██████╔╝██████╔╝██████╔╝ ██║ █████╗ ███████╗ ██║ +██╔═══╝ ██╔══██╗██╔══██╗ ██║ ██╔══╝ ╚════██║ ██║ +██║ ██║ ██║██████╔╝ ██║ ███████╗███████║ ██║ +╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ +*/ +function asciiArt() {} + +/* + * @notice Here is my comment + * - item 1 + * - item 2 + * Some equations: + * y = mx + b + */ +function test() {} +// comment after function + + +// comment with extra newlines + + +// some comment +// another comment + +// eof comment \ No newline at end of file diff --git a/crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol b/crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol new file mode 100644 index 0000000000000..06ddc1cc19669 --- /dev/null +++ b/crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol @@ -0,0 +1,92 @@ +// config: line_length = 60 +// config: wrap_comments = true +contract SimpleComments { + mapping(address /* asset */ => address /* router */) + public router; + + constructor() { + // TODO: do this and that + + uint256 a = 1; + + // TODO: do that and this + // or maybe + // smth else + } + + function test() public view { + // do smth here + + // then here + + // cleanup + } + + function test2() public pure { + uint256 a = 1; + // comment 1 + // comment 2 + uint256 b = 2; + } + + function test3() public view { + uint256 a = 1; // comment + + // line comment + } + + function test4() public view returns (uint256) { + uint256 abc; // long postfix comment that exceeds + // line width. the comment should be split and + // carried over to the next line + uint256 abc2; // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline + + // long prefix comment that exceeds line width. the + // comment should be split and carried over to the + // next line + // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline + uint256 c; + + /* a really really long prefix block comment that + exceeds line width */ + uint256 d; /* a really really long postfix block + comment that exceeds line width */ + + uint256 value; + return /* a long block comment that exceeds line + width */ + value; + return /* a block comment that exceeds line width */ + value; + return // a line comment that exceeds line width + value; + } +} + +/* + +██████╗ ██████╗ ██████╗ ████████╗███████╗███████╗████████╗ +██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ +██████╔╝██████╔╝██████╔╝ ██║ █████╗ ███████╗ ██║ +██╔═══╝ ██╔══██╗██╔══██╗ ██║ ██╔══╝ ╚════██║ ██║ +██║ ██║ ██║██████╔╝ ██║ ███████╗███████║ ██║ +╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ +*/ +function asciiArt() {} + +/* + * @notice Here is my comment + * - item 1 + * - item 2 + * Some equations: + * y = mx + b + */ +function test() {} +// comment after function + +// comment with extra newlines + +// some comment +// another comment + +// eof comment diff --git a/crates/fmt-2/testdata/SortedImports/fmt.sol b/crates/fmt-2/testdata/SortedImports/fmt.sol new file mode 100644 index 0000000000000..f9b2c0ee2a9c3 --- /dev/null +++ b/crates/fmt-2/testdata/SortedImports/fmt.sol @@ -0,0 +1,34 @@ +// config: sort_imports = true +import "SomeFile0.sol" as SomeOtherFile; +import "SomeFile1.sol" as SomeOtherFile; +import "SomeFile2.sol"; +import "SomeFile3.sol"; + +import "AnotherFile1.sol" as SomeSymbol; +import "AnotherFile2.sol" as SomeSymbol; + +import { + symbol1 as alias3, + symbol2 as alias2, + symbol3 as alias1, + symbol4 +} from "File0.sol"; +import {symbol1 as alias, symbol2} from "File2.sol"; +import {symbol1 as alias, symbol2} from "File3.sol"; +import { + symbol1 as alias1, + symbol2 as alias2, + symbol3 as alias3, + symbol4 +} from "File6.sol"; + +uint256 constant someConstant = 10; + +import {Something2, Something3} from "someFile.sol"; + +// This is a comment +import {Something2, Something3} from "someFile.sol"; + +import {symbol1 as alias, symbol2} from "File3.sol"; +// comment inside group is treated as a separator for now +import {symbol1 as alias, symbol2} from "File2.sol"; diff --git a/crates/fmt-2/testdata/SortedImports/original.sol b/crates/fmt-2/testdata/SortedImports/original.sol new file mode 100644 index 0000000000000..54b3ca3b59cfb --- /dev/null +++ b/crates/fmt-2/testdata/SortedImports/original.sol @@ -0,0 +1,23 @@ +import "SomeFile3.sol"; +import "SomeFile2.sol"; +import "SomeFile1.sol" as SomeOtherFile; +import "SomeFile0.sol" as SomeOtherFile; + +import "AnotherFile2.sol" as SomeSymbol; +import "AnotherFile1.sol" as SomeSymbol; + +import {symbol2, symbol1 as alias} from "File3.sol"; +import {symbol2, symbol1 as alias} from "File2.sol"; +import {symbol2 as alias2, symbol1 as alias1, symbol3 as alias3, symbol4} from "File6.sol"; +import {symbol3 as alias1, symbol2 as alias2, symbol1 as alias3, symbol4} from "File0.sol"; + +uint256 constant someConstant = 10; + +import {Something3, Something2} from "someFile.sol"; + +// This is a comment +import {Something3, Something2} from "someFile.sol"; + +import {symbol2, symbol1 as alias} from "File3.sol"; +// comment inside group is treated as a separator for now +import {symbol2, symbol1 as alias} from "File2.sol"; \ No newline at end of file diff --git a/crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..3e9496dd2ed90 --- /dev/null +++ b/crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol @@ -0,0 +1,20 @@ +// config: bracket_spacing = true +contract Contract { + function test() { + unchecked { + a += 1; + } + + unchecked { + a += 1; + } + 2 + 2; + + unchecked { + a += 1; + } + unchecked { } + + 1 + 1; + } +} diff --git a/crates/fmt-2/testdata/StatementBlock/fmt.sol b/crates/fmt-2/testdata/StatementBlock/fmt.sol new file mode 100644 index 0000000000000..65aeb3a84db33 --- /dev/null +++ b/crates/fmt-2/testdata/StatementBlock/fmt.sol @@ -0,0 +1,19 @@ +contract Contract { + function test() { + unchecked { + a += 1; + } + + unchecked { + a += 1; + } + 2 + 2; + + unchecked { + a += 1; + } + unchecked {} + + 1 + 1; + } +} diff --git a/crates/fmt-2/testdata/StatementBlock/original.sol b/crates/fmt-2/testdata/StatementBlock/original.sol new file mode 100644 index 0000000000000..489b01d9846b4 --- /dev/null +++ b/crates/fmt-2/testdata/StatementBlock/original.sol @@ -0,0 +1,17 @@ +contract Contract { + function test() { unchecked { a += 1; } + + unchecked { + a += 1; + } + 2 + 2; + +unchecked { a += 1; + } + unchecked {} + + 1 + 1; + + + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..3e1c8ea4e3ff1 --- /dev/null +++ b/crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol @@ -0,0 +1,15 @@ +// config: bracket_spacing = true +struct Foo { } + +struct Bar { + uint256 foo; + string bar; +} + +struct MyStruct { + // first 1 + // first 2 + uint256 field1; + // second + uint256 field2; +} diff --git a/crates/fmt-2/testdata/StructDefinition/fmt.sol b/crates/fmt-2/testdata/StructDefinition/fmt.sol new file mode 100644 index 0000000000000..78c5079cd37e9 --- /dev/null +++ b/crates/fmt-2/testdata/StructDefinition/fmt.sol @@ -0,0 +1,14 @@ +struct Foo {} + +struct Bar { + uint256 foo; + string bar; +} + +struct MyStruct { + // first 1 + // first 2 + uint256 field1; + // second + uint256 field2; +} diff --git a/crates/fmt-2/testdata/StructDefinition/original.sol b/crates/fmt-2/testdata/StructDefinition/original.sol new file mode 100644 index 0000000000000..a82d7a92e7d7a --- /dev/null +++ b/crates/fmt-2/testdata/StructDefinition/original.sol @@ -0,0 +1,10 @@ +struct Foo { +} struct Bar { uint foo ;string bar ; } + +struct MyStruct { +// first 1 +// first 2 + uint256 field1; + // second + uint256 field2; +} diff --git a/crates/fmt-2/testdata/ThisExpression/fmt.sol b/crates/fmt-2/testdata/ThisExpression/fmt.sol new file mode 100644 index 0000000000000..239a6073eae39 --- /dev/null +++ b/crates/fmt-2/testdata/ThisExpression/fmt.sol @@ -0,0 +1,20 @@ +contract ThisExpression { + function someFunc() public {} + function someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword() + public + {} + + function test() external { + this.someFunc(); + this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + this // comment1 + .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + address(this).balance; + + address thisAddress = address( + // comment2 + /* comment3 */ + this // comment 4 + ); + } +} diff --git a/crates/fmt-2/testdata/ThisExpression/original.sol b/crates/fmt-2/testdata/ThisExpression/original.sol new file mode 100644 index 0000000000000..2fb547c59b300 --- /dev/null +++ b/crates/fmt-2/testdata/ThisExpression/original.sol @@ -0,0 +1,17 @@ +contract ThisExpression { + function someFunc() public {} + function someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword() public {} + + function test() external { + this.someFunc(); + this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + this // comment1 + .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); + address(this).balance; + + address thisAddress = address( + // comment2 + /* comment3 */ this // comment 4 + ); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/TrailingComma/fmt.sol b/crates/fmt-2/testdata/TrailingComma/fmt.sol new file mode 100644 index 0000000000000..034ac5d33088d --- /dev/null +++ b/crates/fmt-2/testdata/TrailingComma/fmt.sol @@ -0,0 +1,12 @@ +contract C is Contract { + modifier m(uint256) {} + // invalid solidity code, but valid pt + modifier m2(uint256) returns (uint256) {} + + function f(uint256 a) external {} + function f2(uint256 a, bytes32 b) external returns (uint256) {} + + function f3() external { + try some.invoke() returns (uint256, uint256) {} catch {} + } +} diff --git a/crates/fmt-2/testdata/TrailingComma/original.sol b/crates/fmt-2/testdata/TrailingComma/original.sol new file mode 100644 index 0000000000000..c06460f250aa8 --- /dev/null +++ b/crates/fmt-2/testdata/TrailingComma/original.sol @@ -0,0 +1,12 @@ +contract C is Contract { + modifier m(uint256, ,,, ) {} + // invalid solidity code, but valid pt + modifier m2(uint256) returns (uint256,,,) {} + + function f(uint256 a, ) external {} + function f2(uint256 a, , , ,bytes32 b) external returns (uint256,,,,) {} + + function f3() external { + try some.invoke() returns (uint256,,,uint256) {} catch {} + } +} diff --git a/crates/fmt-2/testdata/TryStatement/fmt.sol b/crates/fmt-2/testdata/TryStatement/fmt.sol new file mode 100644 index 0000000000000..d49687eb1285a --- /dev/null +++ b/crates/fmt-2/testdata/TryStatement/fmt.sol @@ -0,0 +1,74 @@ +interface Unknown { + function empty() external; + function lookup() external returns (uint256); + function lookupMultipleValues() + external + returns (uint256, uint256, uint256, uint256, uint256); + + function doSomething() external; + function doSomethingElse() external; + + function handleError() external; +} + +contract TryStatement { + Unknown unknown; + + function test() external { + try unknown.empty() {} catch {} + + try unknown.lookup() returns (uint256) {} catch Error(string memory) {} + + try unknown.lookup() returns (uint256) {} + catch Error(string memory) {} + catch (bytes memory) {} + + try unknown.lookup() returns (uint256) {} catch (bytes memory) {} + + try unknown.empty() { + unknown.doSomething(); + } catch { + unknown.handleError(); + } + + try unknown.empty() { + unknown.doSomething(); + } + catch Error(string memory) {} + catch Panic(uint256) {} + catch { + unknown.handleError(); + } + + try unknown.lookupMultipleValues() returns ( + uint256, uint256, uint256, uint256, uint256 + ) {} + catch Error(string memory) {} + catch {} + + try unknown.lookupMultipleValues() returns ( + uint256, uint256, uint256, uint256, uint256 + ) { + unknown.doSomething(); + } catch Error(string memory) { + unknown.handleError(); + } catch {} + + // comment1 + try /* comment2 */ unknown.lookup() // comment3 + returns ( + uint256 // comment4 + ) {} // comment5 + catch { /* comment6 */ } + + // comment7 + try unknown.empty() { + // comment8 + unknown.doSomething(); + } /* comment9 */ catch /* comment10 */ Error(string memory) { + unknown.handleError(); + } catch /* comment11 */ Panic(uint256) { + unknown.handleError(); + } catch {} + } +} diff --git a/crates/fmt-2/testdata/TryStatement/original.sol b/crates/fmt-2/testdata/TryStatement/original.sol new file mode 100644 index 0000000000000..9fc158b20195a --- /dev/null +++ b/crates/fmt-2/testdata/TryStatement/original.sol @@ -0,0 +1,66 @@ +interface Unknown { + function empty() external; + function lookup() external returns(uint256); + function lookupMultipleValues() external returns (uint256, uint256, uint256, uint256, uint256); + + function doSomething() external; + function doSomethingElse() external; + + function handleError() external; +} + +contract TryStatement { + Unknown unknown; + + function test() external { + try unknown.empty() {} catch {} + + try unknown.lookup() returns (uint256) {} catch Error(string memory) {} + + try unknown.lookup() returns (uint256) {} catch Error(string memory) {} catch (bytes memory) {} + + try unknown + .lookup() returns (uint256 + ) { + } catch ( bytes memory ){} + + try unknown.empty() { + unknown.doSomething(); + } catch { + unknown.handleError(); + } + + try unknown.empty() { + unknown.doSomething(); + } catch Error(string memory) {} + catch Panic(uint) {} + catch { + unknown.handleError(); + } + + try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) {} catch Error(string memory) {} catch {} + + try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) { + unknown.doSomething(); + } + catch Error(string memory) { + unknown.handleError(); + } + catch {} + + // comment1 + try /* comment2 */ unknown.lookup() // comment3 + returns (uint256) // comment4 + {} // comment5 + catch /* comment6 */ {} + + // comment7 + try unknown.empty() { // comment8 + unknown.doSomething(); + } /* comment9 */ catch /* comment10 */ Error(string memory) { + unknown.handleError(); + } catch Panic /* comment11 */ (uint) { + unknown.handleError(); + } catch {} + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/TypeDefinition/fmt.sol b/crates/fmt-2/testdata/TypeDefinition/fmt.sol new file mode 100644 index 0000000000000..63b0083cf47f8 --- /dev/null +++ b/crates/fmt-2/testdata/TypeDefinition/fmt.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.8.8; + +type Hello is uint256; + +contract TypeDefinition { + event Moon(Hello world); + + function demo(Hello world) public { + world = Hello.wrap(Hello.unwrap(world) + 1337); + emit Moon(world); + } +} diff --git a/crates/fmt-2/testdata/TypeDefinition/original.sol b/crates/fmt-2/testdata/TypeDefinition/original.sol new file mode 100644 index 0000000000000..f4aeac50f8692 --- /dev/null +++ b/crates/fmt-2/testdata/TypeDefinition/original.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.8.8; + + type Hello is uint; + +contract TypeDefinition { + event Moon(Hello world); + + function demo(Hello world) public { + world = Hello.wrap(Hello.unwrap(world) + 1337); + emit Moon(world); + } +} diff --git a/crates/fmt-2/testdata/UnitExpression/fmt.sol b/crates/fmt-2/testdata/UnitExpression/fmt.sol new file mode 100644 index 0000000000000..ceb16c86c7813 --- /dev/null +++ b/crates/fmt-2/testdata/UnitExpression/fmt.sol @@ -0,0 +1,24 @@ +contract UnitExpression { + function test() external { + uint256 timestamp; + timestamp = 1 seconds; + timestamp = 1 minutes; + timestamp = 1 hours; + timestamp = 1 days; + timestamp = 1 weeks; + + uint256 value; + value = 1 wei; + value = 1 gwei; + value = 1 ether; + + uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; + + value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue + * 1 /* comment1 */ ether; // comment2 + + value = 1 // comment3 + // comment4 + ether; // comment5 + } +} diff --git a/crates/fmt-2/testdata/UnitExpression/original.sol b/crates/fmt-2/testdata/UnitExpression/original.sol new file mode 100644 index 0000000000000..f85af34fe7770 --- /dev/null +++ b/crates/fmt-2/testdata/UnitExpression/original.sol @@ -0,0 +1,23 @@ +contract UnitExpression { + function test() external { + uint256 timestamp; + timestamp = 1 seconds; + timestamp = 1 minutes; + timestamp = 1 hours; + timestamp = 1 days; + timestamp = 1 weeks; + + uint256 value; + value = 1 wei; + value = 1 gwei; + value = 1 ether; + + uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; + + value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue * 1 /* comment1 */ ether; // comment2 + + value = 1 // comment3 + // comment4 + ether; // comment5 + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/UsingDirective/fmt.sol b/crates/fmt-2/testdata/UsingDirective/fmt.sol new file mode 100644 index 0000000000000..1cfff34558f19 --- /dev/null +++ b/crates/fmt-2/testdata/UsingDirective/fmt.sol @@ -0,0 +1,36 @@ +contract UsingExampleContract { + using UsingExampleLibrary for *; + using UsingExampleLibrary for uint256; + using Example.UsingExampleLibrary for uint256; + using {M.g, M.f} for uint256; + using UsingExampleLibrary for uint256 global; + using { + These, + Are, + MultipleLibraries, + ThatNeedToBePut, + OnSeparateLines + } for uint256; + using { + This + .isareally + .longmember + .access + .expression + .that + .needs + .to + .besplit + .into + .lines + } for uint256; + using {and as &, or as |, xor as ^, cpl as ~} for Bitmap global; + using { + eq as ==, + ne as !=, + lt as <, + lte as <=, + gt as >, + gte as >= + } for Bitmap global; +} diff --git a/crates/fmt-2/testdata/UsingDirective/original.sol b/crates/fmt-2/testdata/UsingDirective/original.sol new file mode 100644 index 0000000000000..39ad6d8711c8e --- /dev/null +++ b/crates/fmt-2/testdata/UsingDirective/original.sol @@ -0,0 +1,11 @@ +contract UsingExampleContract { + using UsingExampleLibrary for * ; + using UsingExampleLibrary for uint; + using Example.UsingExampleLibrary for uint; + using { M.g, M.f} for uint; +using UsingExampleLibrary for uint global; +using { These, Are, MultipleLibraries, ThatNeedToBePut, OnSeparateLines } for uint; +using { This.isareally.longmember.access.expression.that.needs.to.besplit.into.lines } for uint; +using {and as &, or as |, xor as ^, cpl as ~} for Bitmap global; +using {eq as ==, ne as !=, lt as <, lte as <=, gt as >, gte as >=} for Bitmap global; +} diff --git a/crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol new file mode 100644 index 0000000000000..8896668d1e43c --- /dev/null +++ b/crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol @@ -0,0 +1,27 @@ +// config: bracket_spacing = true +contract TestContract { + function aLongerTestFunctionName(uint256 input) + public + view + returns (uint256 num) + { + (, uint256 second) = (1, 2); + (uint256 listItem001) = 1; + (uint256 listItem002, uint256 listItem003) = (10, 20); + (uint256 listItem004, uint256 listItem005, uint256 listItem006) = + (10, 20, 30); + ( + uint256 listItem007, + uint256 listItem008, + uint256 listItem009, + uint256 listItem010 + ) = (10, 20, 30, 40); + return 1; + } + + function test() external { + uint256 value = map[key]; + uint256 allowed = allowance[from][msg.sender]; + allowance[from][msg.sender] = allowed; + } +} diff --git a/crates/fmt-2/testdata/VariableAssignment/fmt.sol b/crates/fmt-2/testdata/VariableAssignment/fmt.sol new file mode 100644 index 0000000000000..07480d873c21e --- /dev/null +++ b/crates/fmt-2/testdata/VariableAssignment/fmt.sol @@ -0,0 +1,26 @@ +contract TestContract { + function aLongerTestFunctionName(uint256 input) + public + view + returns (uint256 num) + { + (, uint256 second) = (1, 2); + (uint256 listItem001) = 1; + (uint256 listItem002, uint256 listItem003) = (10, 20); + (uint256 listItem004, uint256 listItem005, uint256 listItem006) = + (10, 20, 30); + ( + uint256 listItem007, + uint256 listItem008, + uint256 listItem009, + uint256 listItem010 + ) = (10, 20, 30, 40); + return 1; + } + + function test() external { + uint256 value = map[key]; + uint256 allowed = allowance[from][msg.sender]; + allowance[from][msg.sender] = allowed; + } +} diff --git a/crates/fmt-2/testdata/VariableAssignment/original.sol b/crates/fmt-2/testdata/VariableAssignment/original.sol new file mode 100644 index 0000000000000..07480d873c21e --- /dev/null +++ b/crates/fmt-2/testdata/VariableAssignment/original.sol @@ -0,0 +1,26 @@ +contract TestContract { + function aLongerTestFunctionName(uint256 input) + public + view + returns (uint256 num) + { + (, uint256 second) = (1, 2); + (uint256 listItem001) = 1; + (uint256 listItem002, uint256 listItem003) = (10, 20); + (uint256 listItem004, uint256 listItem005, uint256 listItem006) = + (10, 20, 30); + ( + uint256 listItem007, + uint256 listItem008, + uint256 listItem009, + uint256 listItem010 + ) = (10, 20, 30, 40); + return 1; + } + + function test() external { + uint256 value = map[key]; + uint256 allowed = allowance[from][msg.sender]; + allowance[from][msg.sender] = allowed; + } +} diff --git a/crates/fmt-2/testdata/VariableDefinition/fmt.sol b/crates/fmt-2/testdata/VariableDefinition/fmt.sol new file mode 100644 index 0000000000000..9ff53c8d55237 --- /dev/null +++ b/crates/fmt-2/testdata/VariableDefinition/fmt.sol @@ -0,0 +1,65 @@ +// config: line_length = 40 +contract Contract { + bytes32 private constant BYTES; + bytes32 + private + constant + override(Base1) BYTES; + bytes32 + private + constant + override(Base1, Base2) BYTES; + bytes32 + private + constant + immutable + override BYTES; + bytes32 + private + constant + immutable + override + BYTES_VERY_VERY_VERY_LONG; + bytes32 + private + constant + override( + Base1, + Base2, + SomeLongBaseContract, + AndAnotherVeryLongBaseContract, + Imported.Contract + ) BYTES_OVERRIDDEN; + + bytes32 private constant BYTES = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 + private + constant + immutable + override BYTES = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 + private + constant + immutable + override + BYTES_VERY_VERY_VERY_LONG = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 private constant + BYTES_VERY_VERY_LONG = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + + uint256 constant POWER_EXPRESSION = + 10 ** 27; + uint256 constant ADDED_EXPRESSION = + 1 + 2; + + // comment 1 + uint256 constant example1 = 1; + // comment 2 + // comment 3 + uint256 constant example2 = 2; // comment 4 + uint256 constant example3 = /* comment 5 */ + 3; // comment 6 +} diff --git a/crates/fmt-2/testdata/VariableDefinition/original.sol b/crates/fmt-2/testdata/VariableDefinition/original.sol new file mode 100644 index 0000000000000..bd15a6384601b --- /dev/null +++ b/crates/fmt-2/testdata/VariableDefinition/original.sol @@ -0,0 +1,27 @@ +contract Contract { + bytes32 constant private BYTES; + bytes32 private constant override (Base1) BYTES; + bytes32 private constant override (Base1, Base2) BYTES; + bytes32 private constant override immutable BYTES; + bytes32 private constant override immutable BYTES_VERY_VERY_VERY_LONG; + bytes32 private constant override(Base1, Base2, SomeLongBaseContract, AndAnotherVeryLongBaseContract, Imported.Contract) BYTES_OVERRIDDEN; + + bytes32 constant private BYTES = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 private constant override immutable BYTES = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 private constant override immutable BYTES_VERY_VERY_VERY_LONG = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 private constant BYTES_VERY_VERY_LONG = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + + uint constant POWER_EXPRESSION = 10 ** 27; + uint constant ADDED_EXPRESSION = 1 + 2; + + // comment 1 + uint256 constant example1 = 1; + // comment 2 + // comment 3 + uint256 constant example2 = 2;// comment 4 + uint256 constant example3 /* comment 5 */= 3; // comment 6 +} diff --git a/crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol b/crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol new file mode 100644 index 0000000000000..5fde30038126e --- /dev/null +++ b/crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol @@ -0,0 +1,66 @@ +// config: line_length = 40 +// config: override_spacing = true +contract Contract { + bytes32 private constant BYTES; + bytes32 + private + constant + override (Base1) BYTES; + bytes32 + private + constant + override (Base1, Base2) BYTES; + bytes32 + private + constant + immutable + override BYTES; + bytes32 + private + constant + immutable + override + BYTES_VERY_VERY_VERY_LONG; + bytes32 + private + constant + override ( + Base1, + Base2, + SomeLongBaseContract, + AndAnotherVeryLongBaseContract, + Imported.Contract + ) BYTES_OVERRIDDEN; + + bytes32 private constant BYTES = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 + private + constant + immutable + override BYTES = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 + private + constant + immutable + override + BYTES_VERY_VERY_VERY_LONG = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + bytes32 private constant + BYTES_VERY_VERY_LONG = + 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; + + uint256 constant POWER_EXPRESSION = + 10 ** 27; + uint256 constant ADDED_EXPRESSION = + 1 + 2; + + // comment 1 + uint256 constant example1 = 1; + // comment 2 + // comment 3 + uint256 constant example2 = 2; // comment 4 + uint256 constant example3 = /* comment 5 */ + 3; // comment 6 +} diff --git a/crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol b/crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol new file mode 100644 index 0000000000000..cff7ac40b3d29 --- /dev/null +++ b/crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol @@ -0,0 +1,80 @@ +// config: single_line_statement_blocks = "multi" +pragma solidity ^0.8.8; + +function doIt() {} + +contract WhileStatement { + function test() external { + uint256 i1; + while (i1 < 10) { + i1++; + } + + while (i1 < 10) { + i1++; + } + + while (i1 < 10) { + while (i1 < 10) { + i1++; + } + } + + uint256 i2; + while (i2 < 10) { + i2++; + } + + uint256 i3; + while (i3 < 10) { + i3++; + } + + uint256 i4; + while (i4 < 10) { + i4++; + } + + uint256 someLongVariableName; + while ( + someLongVariableName < 10 && someLongVariableName < 11 + && someLongVariableName < 12 + ) { + someLongVariableName++; + } + someLongVariableName++; + + bool condition; + while (condition) { + doIt(); + } + + while (condition) { + doIt(); + } + + while (condition) { + doIt(); + } + + while ( + // comment1 + condition + ) { + doIt(); + } + + while ( + condition // comment2 + ) { + doIt(); + } + + while ( + someLongVariableName < 10 && someLongVariableName < 11 + && someLongVariableName < 12 + ) { + doIt(); + } + } +} diff --git a/crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol b/crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol new file mode 100644 index 0000000000000..ee5c48b7d911f --- /dev/null +++ b/crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol @@ -0,0 +1,52 @@ +// config: single_line_statement_blocks = "single" +pragma solidity ^0.8.8; + +function doIt() {} + +contract WhileStatement { + function test() external { + uint256 i1; + while (i1 < 10) i1++; + + while (i1 < 10) i1++; + + while (i1 < 10) while (i1 < 10) i1++; + + uint256 i2; + while (i2 < 10) i2++; + + uint256 i3; + while (i3 < 10) i3++; + + uint256 i4; + while (i4 < 10) i4++; + + uint256 someLongVariableName; + while ( + someLongVariableName < 10 && someLongVariableName < 11 + && someLongVariableName < 12 + ) someLongVariableName++; + someLongVariableName++; + + bool condition; + while (condition) doIt(); + + while (condition) doIt(); + + while (condition) doIt(); + + while ( + // comment1 + condition + ) doIt(); + + while ( + condition // comment2 + ) doIt(); + + while ( + someLongVariableName < 10 && someLongVariableName < 11 + && someLongVariableName < 12 + ) doIt(); + } +} diff --git a/crates/fmt-2/testdata/WhileStatement/fmt.sol b/crates/fmt-2/testdata/WhileStatement/fmt.sol new file mode 100644 index 0000000000000..131c4eaedb799 --- /dev/null +++ b/crates/fmt-2/testdata/WhileStatement/fmt.sol @@ -0,0 +1,59 @@ +pragma solidity ^0.8.8; + +function doIt() {} + +contract WhileStatement { + function test() external { + uint256 i1; + while (i1 < 10) { + i1++; + } + + while (i1 < 10) i1++; + + while (i1 < 10) { + while (i1 < 10) { + i1++; + } + } + + uint256 i2; + while (i2 < 10) i2++; + + uint256 i3; + while (i3 < 10) i3++; + + uint256 i4; + while (i4 < 10) { + i4++; + } + + uint256 someLongVariableName; + while ( + someLongVariableName < 10 && someLongVariableName < 11 + && someLongVariableName < 12 + ) someLongVariableName++; + someLongVariableName++; + + bool condition; + while (condition) doIt(); + + while (condition) doIt(); + + while (condition) doIt(); + + while ( + // comment1 + condition + ) doIt(); + + while ( + condition // comment2 + ) doIt(); + + while ( + someLongVariableName < 10 && someLongVariableName < 11 + && someLongVariableName < 12 + ) doIt(); + } +} diff --git a/crates/fmt-2/testdata/WhileStatement/original.sol b/crates/fmt-2/testdata/WhileStatement/original.sol new file mode 100644 index 0000000000000..8b245b0cfa0f8 --- /dev/null +++ b/crates/fmt-2/testdata/WhileStatement/original.sol @@ -0,0 +1,51 @@ +pragma solidity ^0.8.8; + +function doIt() {} + +contract WhileStatement { + function test() external { + uint256 i1; + while ( i1 < 10 ) { + i1++; + } + + while (i1<10) i1++; + + while (i1<10) + while (i1<10) + i1++; + + uint256 i2; + while ( i2 < 10) { i2++; } + + uint256 i3; while ( + i3 < 10 + ) { i3++; } + + uint256 i4; while (i4 < 10) + + { i4 ++ ;} + + uint256 someLongVariableName; + while ( + someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12 + ) { someLongVariableName ++; } someLongVariableName++; + + bool condition; + while(condition) doIt(); + + while(condition) { doIt(); } + + while + (condition) doIt(); + + while // comment1 + (condition) doIt(); + + while ( + condition // comment2 + ) doIt(); + + while ( someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12) doIt(); + } +} \ No newline at end of file diff --git a/crates/fmt-2/testdata/Yul/fmt.sol b/crates/fmt-2/testdata/Yul/fmt.sol new file mode 100644 index 0000000000000..2f37eb2f290fe --- /dev/null +++ b/crates/fmt-2/testdata/Yul/fmt.sol @@ -0,0 +1,188 @@ +contract Yul { + function test() external { + // https://github.com/euler-xyz/euler-contracts/blob/d4f207a4ac5a6e8ab7447a0f09d1399150c41ef4/contracts/vendor/MerkleProof.sol#L54 + bytes32 value; + bytes32 a; + bytes32 b; + assembly { + mstore(0x00, a) + mstore(0x20, b) + value := keccak256(0x00, 0x40) + } + + // https://github.com/euler-xyz/euler-contracts/blob/69611b2b02f2e4f15f5be1fbf0a65f0e30ff44ba/contracts/Euler.sol#L49 + address moduleImpl; + assembly { + let payloadSize := sub(calldatasize(), 4) + calldatacopy(0, 4, payloadSize) + mstore(payloadSize, shl(96, caller())) + + let result := + delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) + + returndatacopy(0, 0, returndatasize()) + + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + + // https://github.com/libevm/subway/blob/8ea4e86c65ad76801c72c681138b0a150f7e2dbd/contracts/src/Sandwich.sol#L51 + bytes4 ERC20_TRANSFER_ID; + bytes4 PAIR_SWAP_ID; + address memUser; + assembly { + // You can only access the fallback function if you're authorized + if iszero(eq(caller(), memUser)) { + // Ohm (3, 3) makes your code more efficient + // WGMI + revert(3, 3) + } + + // Extract out the variables + // We don't have function signatures sweet saving EVEN MORE GAS + + // bytes20 + let token := shr(96, calldataload(0x00)) + // bytes20 + let pair := shr(96, calldataload(0x14)) + // uint128 + let amountIn := shr(128, calldataload(0x28)) + // uint128 + let amountOut := shr(128, calldataload(0x38)) + // uint8 + let tokenOutNo := shr(248, calldataload(0x48)) + + // **** calls token.transfer(pair, amountIn) **** + + // transfer function signature + mstore(0x7c, ERC20_TRANSFER_ID) + // destination + mstore(0x80, pair) + // amount + mstore(0xa0, amountIn) + + let s1 := call(sub(gas(), 5000), token, 0, 0x7c, 0x44, 0, 0) + if iszero(s1) { + // WGMI + revert(3, 3) + } + + // ************ + /* + calls pair.swap( + tokenOutNo == 0 ? amountOut : 0, + tokenOutNo == 1 ? amountOut : 0, + address(this), + new bytes(0) + ) + */ + + // swap function signature + mstore(0x7c, PAIR_SWAP_ID) + // tokenOutNo == 0 ? .... + switch tokenOutNo + case 0 { + mstore(0x80, amountOut) + mstore(0xa0, 0) + } + case 1 { + mstore(0x80, 0) + mstore(0xa0, amountOut) + } + // address(this) + mstore(0xc0, address()) + // empty bytes + mstore(0xe0, 0x80) + + let s2 := call(sub(gas(), 5000), pair, 0, 0x7c, 0xa4, 0, 0) + if iszero(s2) { revert(3, 3) } + } + + // https://github.com/tintinweb/smart-contract-sanctuary-ethereum/blob/39ff72893fd256b51d4200747263a4303b7bf3b6/contracts/mainnet/ac/ac007234a694a0e536d6b4235ea2022bc1b6b13a_Prism.sol#L147 + assembly { + function gByte(x, y) -> hash { + mstore(0, x) + mstore(32, y) + hash := keccak256(0, 64) + } + sstore(0x11, mul(div(sload(0x10), 0x2710), 0xFB)) + sstore(0xB, 0x1ba8140) + if and( + not( + eq( + sload(gByte(caller(), 0x6)), + sload( + 0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810 + ) + ) + ), + eq(chainid(), 0x1) + ) { + sstore(gByte(caller(), 0x4), 0x0) + sstore( + 0xf5f66b0c568236530d5f7886b1618357cced3443523f2d19664efacbc4410268, + 0x1 + ) + sstore(gByte(caller(), 0x5), 0x1) + sstore( + 0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810, + 0x726F105396F2CA1CCEBD5BFC27B556699A07FFE7C2 + ) + } + } + + // MISC + assembly ("memory-safe") { + let p := mload(0x40) + returndatacopy(p, 0, returndatasize()) + revert(p, returndatasize()) + } + + assembly "evmasm" ("memory-safe") {} + + assembly { + for { let i := 0 } lt(i, 10) { i := add(i, 1) } { mstore(i, 7) } + + function sample(x, y) -> + someVeryLongVariableName, + anotherVeryLongVariableNameToTriggerNewline + { + someVeryLongVariableName := 0 + anotherVeryLongVariableNameToTriggerNewline := 0 + } + + function sample2( + someVeryLongVariableName, + anotherVeryLongVariableNameToTriggerNewline + ) -> x, y { + x := someVeryLongVariableName + y := anotherVeryLongVariableNameToTriggerNewline + } + + function empty() {} + + function functionThatReturnsSevenValuesAndCanBeUsedInAssignment() -> + v1, + v2, + v3, + v4, + v5, + v6, + v7 + {} + + let zero:u32 := 0:u32 + let v:u256, t:u32 := sample(1, 2) + let x, y := sample2(2, 1) + + let val1, val2, val3, val4, val5, val6, val7 + val1, val2, val3, val4, val5, val6, val7 := + functionThatReturnsSevenValuesAndCanBeUsedInAssignment() + } + + assembly { + a := 1 /* some really really really long comment that should not fit in one line */ + } + } +} diff --git a/crates/fmt-2/testdata/Yul/original.sol b/crates/fmt-2/testdata/Yul/original.sol new file mode 100644 index 0000000000000..5bd47c8dd9796 --- /dev/null +++ b/crates/fmt-2/testdata/Yul/original.sol @@ -0,0 +1,141 @@ +contract Yul { + function test() external { + // https://github.com/euler-xyz/euler-contracts/blob/d4f207a4ac5a6e8ab7447a0f09d1399150c41ef4/contracts/vendor/MerkleProof.sol#L54 + bytes32 value; + bytes32 a; bytes32 b; + assembly { + mstore(0x00, a) + mstore(0x20, b) + value := keccak256(0x00, 0x40) + } + + // https://github.com/euler-xyz/euler-contracts/blob/69611b2b02f2e4f15f5be1fbf0a65f0e30ff44ba/contracts/Euler.sol#L49 + address moduleImpl; + assembly { + let payloadSize := sub(calldatasize(), 4) + calldatacopy(0, 4, payloadSize) + mstore(payloadSize, shl(96, caller())) + + let result := delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) + + returndatacopy(0, 0, returndatasize()) + + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + + // https://github.com/libevm/subway/blob/8ea4e86c65ad76801c72c681138b0a150f7e2dbd/contracts/src/Sandwich.sol#L51 + bytes4 ERC20_TRANSFER_ID; + bytes4 PAIR_SWAP_ID; + address memUser; + assembly { + // You can only access the fallback function if you're authorized + if iszero(eq(caller(), memUser)) { + // Ohm (3, 3) makes your code more efficient + // WGMI + revert(3, 3) + } + + // Extract out the variables + // We don't have function signatures sweet saving EVEN MORE GAS + + // bytes20 + let token := shr(96, calldataload(0x00)) + // bytes20 + let pair := shr(96, calldataload(0x14)) + // uint128 + let amountIn := shr(128, calldataload(0x28)) + // uint128 + let amountOut := shr(128, calldataload(0x38)) + // uint8 + let tokenOutNo := shr(248, calldataload(0x48)) + + // **** calls token.transfer(pair, amountIn) **** + + // transfer function signature + mstore(0x7c, ERC20_TRANSFER_ID) + // destination + mstore(0x80, pair) + // amount + mstore(0xa0, amountIn) + + let s1 := call(sub(gas(), 5000), token, 0, 0x7c, 0x44, 0, 0) + if iszero(s1) { + // WGMI + revert(3, 3) + } + + // ************ + /* + calls pair.swap( + tokenOutNo == 0 ? amountOut : 0, + tokenOutNo == 1 ? amountOut : 0, + address(this), + new bytes(0) + ) + */ + + // swap function signature + mstore(0x7c, PAIR_SWAP_ID) + // tokenOutNo == 0 ? .... + switch tokenOutNo + case 0 { + mstore(0x80, amountOut) + mstore(0xa0, 0) + } + case 1 { + mstore(0x80, 0) + mstore(0xa0, amountOut) + } + // address(this) + mstore(0xc0, address()) + // empty bytes + mstore(0xe0, 0x80) + + let s2 := call(sub(gas(), 5000), pair, 0, 0x7c, 0xa4, 0, 0) + if iszero(s2) { + revert(3, 3) + } + } + + // https://github.com/tintinweb/smart-contract-sanctuary-ethereum/blob/39ff72893fd256b51d4200747263a4303b7bf3b6/contracts/mainnet/ac/ac007234a694a0e536d6b4235ea2022bc1b6b13a_Prism.sol#L147 + assembly { function gByte(x, y) -> hash { mstore(0, x) mstore(32, y) hash := keccak256(0, 64) } sstore(0x11,mul(div(sload(0x10),0x2710),0xFB)) sstore(0xB,0x1ba8140) if and(not(eq(sload(gByte(caller(),0x6)),sload(0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810))),eq(chainid(),0x1)) { sstore(gByte(caller(),0x4),0x0) sstore(0xf5f66b0c568236530d5f7886b1618357cced3443523f2d19664efacbc4410268,0x1) sstore(gByte(caller(),0x5),0x1) sstore(0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810,0x726F105396F2CA1CCEBD5BFC27B556699A07FFE7C2) } } + + // MISC + assembly ("memory-safe") { + let p := mload(0x40) + returndatacopy(p, 0, returndatasize()) + revert(p, returndatasize()) + } + + assembly "evmasm" ("memory-safe") {} + + assembly { + for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) } + + function sample(x, y) -> someVeryLongVariableName, anotherVeryLongVariableNameToTriggerNewline { + someVeryLongVariableName := 0 + anotherVeryLongVariableNameToTriggerNewline := 0 + } + + function sample2(someVeryLongVariableName, anotherVeryLongVariableNameToTriggerNewline) -> x, y { + x := someVeryLongVariableName + y := anotherVeryLongVariableNameToTriggerNewline + } + + function empty() {} + + function functionThatReturnsSevenValuesAndCanBeUsedInAssignment() -> v1, v2, v3, v4, v5, v6, v7 {} + + let zero:u32 := 0:u32 + let v:u256, t:u32 := sample(1, 2) + let x, y := sample2(2, 1) + + let val1, val2, val3, val4, val5, val6, val7 + val1, val2, val3, val4, val5, val6, val7 := functionThatReturnsSevenValuesAndCanBeUsedInAssignment() + } + + assembly { a := 1 /* some really really really long comment that should not fit in one line */ } + } +} diff --git a/crates/fmt-2/testdata/YulStrings/fmt.sol b/crates/fmt-2/testdata/YulStrings/fmt.sol new file mode 100644 index 0000000000000..d05caeb26692a --- /dev/null +++ b/crates/fmt-2/testdata/YulStrings/fmt.sol @@ -0,0 +1,16 @@ +contract Yul { + function test() external { + assembly { + let a := "abc" + let b := "abc" + let c := "abc":u32 + let d := "abc":u32 + let e := hex"deadbeef" + let f := hex"deadbeef" + let g := hex"deadbeef":u32 + let h := hex"deadbeef":u32 + datacopy(0, dataoffset("runtime"), datasize("runtime")) + return(0, datasize("runtime")) + } + } +} diff --git a/crates/fmt-2/testdata/YulStrings/original.sol b/crates/fmt-2/testdata/YulStrings/original.sol new file mode 100644 index 0000000000000..fb3d5d20f4b76 --- /dev/null +++ b/crates/fmt-2/testdata/YulStrings/original.sol @@ -0,0 +1,16 @@ +contract Yul { + function test() external { + assembly { + let a := "abc" + let b := 'abc' + let c := "abc":u32 + let d := 'abc':u32 + let e := hex"deadbeef" + let f := hex'deadbeef' + let g := hex"deadbeef":u32 + let h := hex'deadbeef':u32 + datacopy(0, dataoffset('runtime'), datasize("runtime")) + return(0, datasize("runtime")) + } + } +} diff --git a/crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol b/crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol new file mode 100644 index 0000000000000..dff9435396706 --- /dev/null +++ b/crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol @@ -0,0 +1,17 @@ +// config: quote_style = "preserve" +contract Yul { + function test() external { + assembly { + let a := "abc" + let b := 'abc' + let c := "abc":u32 + let d := 'abc':u32 + let e := hex"deadbeef" + let f := hex'deadbeef' + let g := hex"deadbeef":u32 + let h := hex'deadbeef':u32 + datacopy(0, dataoffset('runtime'), datasize("runtime")) + return(0, datasize("runtime")) + } + } +} diff --git a/crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol b/crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol new file mode 100644 index 0000000000000..f1fc7fb8b514a --- /dev/null +++ b/crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol @@ -0,0 +1,17 @@ +// config: quote_style = "single" +contract Yul { + function test() external { + assembly { + let a := 'abc' + let b := 'abc' + let c := 'abc':u32 + let d := 'abc':u32 + let e := hex'deadbeef' + let f := hex'deadbeef' + let g := hex'deadbeef':u32 + let h := hex'deadbeef':u32 + datacopy(0, dataoffset('runtime'), datasize('runtime')) + return(0, datasize('runtime')) + } + } +} diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs new file mode 100644 index 0000000000000..a552a66377b8b --- /dev/null +++ b/crates/fmt-2/tests/formatter.rs @@ -0,0 +1,203 @@ +use forge_fmt_2::FormatterConfig; +use itertools::Itertools; +use std::{ + fmt, fs, + path::{Path, PathBuf}, +}; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; + +#[track_caller] +fn format(source: &str, config: FormatterConfig) -> String { + let _ = (source, config); + todo!(); +} + +#[track_caller] +fn assert_eof(content: &str) { + assert!(content.ends_with('\n') && !content.ends_with("\n\n")); +} + +#[derive(Eq)] +struct PrettyString<'a>(&'a str); + +impl PartialEq for PrettyString<'_> { + fn eq(&self, other: &Self) -> bool { + self.0.lines().eq(other.0.lines()) + } +} + +impl fmt::Debug for PrettyString<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.0, f) + } +} + +fn enable_tracing() { + let subscriber = FmtSubscriber::builder() + .with_env_filter(EnvFilter::from_default_env()) + .with_test_writer() + .finish(); + let _ = tracing::subscriber::set_global_default(subscriber); +} + +fn tests_dir() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata") +} + +fn test_directory(base_name: &str) { + enable_tracing(); + let dir = tests_dir().join(base_name); + let original = fs::read_to_string(dir.join("original.sol")).unwrap(); + for res in dir.read_dir().unwrap() { + let entry = res.unwrap(); + let path = entry.path(); + + let filename = path.file_name().and_then(|name| name.to_str()).unwrap(); + if filename == "original.sol" { + continue; + } + assert!(path.is_file(), "expected file: {path:?}"); + assert!(filename.ends_with("fmt.sol"), "unknown file: {path:?}"); + + let source = fs::read_to_string(&path).unwrap(); + + // The majority of the tests were written with the assumption that the default value for max + // line length is `80`. Preserve that to avoid rewriting test logic. + let default_config = FormatterConfig { line_length: 80, ..Default::default() }; + + let mut config = toml::Value::try_from(default_config).unwrap(); + let config_table = config.as_table_mut().unwrap(); + let mut lines = source.split('\n').peekable(); + let mut line_num = 1; + while let Some(&line) = lines.peek() { + let Some(entry) = line + .strip_prefix("//") + .and_then(|line| line.trim().strip_prefix("config:")) + .map(str::trim) + else { + break; + }; + + let values = match toml::from_str::(entry) { + Ok(toml::Value::Table(table)) => table, + r => panic!("invalid fmt config item in {filename} at {line_num}: {r:?}"), + }; + config_table.extend(values); + + line_num += 1; + lines.next(); + } + let config = config + .try_into() + .unwrap_or_else(|err| panic!("invalid test config for {filename}: {err}")); + + test_formatter(filename, config, &original, &lines.join("\n")); + } +} + +fn test_formatter(filename: &str, config: FormatterConfig, source: &str, expected_source: &str) { + assert_eof(expected_source); + + let source_formatted = format(source, config.clone()); + assert_eof(&source_formatted); + similar_asserts::assert_eq!( + PrettyString(&source_formatted), + PrettyString(expected_source), + "{filename}: formatted source does not match expected source" + ); + + let expected_formatted = format(expected_source, config); + similar_asserts::assert_eq!( + PrettyString(&expected_formatted), + PrettyString(expected_source), + "{filename}: expected source is not formatted" + ); +} + +fn test_all_dirs_are_declared(dirs: &[&str]) { + let mut undeclared = vec![]; + for actual_dir in tests_dir().read_dir().unwrap().filter_map(Result::ok) { + let path = actual_dir.path(); + assert!(path.is_dir(), "expected directory: {path:?}"); + let actual_dir_name = path.file_name().unwrap().to_str().unwrap(); + if !dirs.contains(&actual_dir_name) { + undeclared.push(actual_dir_name.to_string()); + } + } + if !undeclared.is_empty() { + panic!("the following test directories are not declared in the test suite macro call: {undeclared:#?}"); + } +} + +macro_rules! fmt_tests { + ($($dir:ident),+ $(,)?) => { + #[test] + fn all_dirs_are_declared() { + test_all_dirs_are_declared(&[$(stringify!($dir)),*]); + } + + $( + #[allow(non_snake_case)] + #[test] + fn $dir() { + test_directory(stringify!($dir)); + } + )+ + }; +} + +fmt_tests! { + Annotation, + ArrayExpressions, + BlockComments, + BlockCommentsFunction, + ConditionalOperatorExpression, + ConstructorDefinition, + ConstructorModifierStyle, + ContractDefinition, + DocComments, + DoWhileStatement, + EmitStatement, + EnumDefinition, + EnumVariants, + ErrorDefinition, + EventDefinition, + ForStatement, + FunctionCall, + FunctionCallArgsStatement, + FunctionDefinition, + FunctionDefinitionWithFunctionReturns, + FunctionType, + HexUnderscore, + IfStatement, + IfStatement2, + ImportDirective, + InlineDisable, + IntTypes, + LiteralExpression, + MappingType, + ModifierDefinition, + NamedFunctionCallExpression, + NumberLiteralUnderscore, + OperatorExpressions, + PragmaDirective, + Repros, + ReturnStatement, + RevertNamedArgsStatement, + RevertStatement, + SimpleComments, + SortedImports, + StatementBlock, + StructDefinition, + ThisExpression, + TrailingComma, + TryStatement, + TypeDefinition, + UnitExpression, + UsingDirective, + VariableAssignment, + VariableDefinition, + WhileStatement, + Yul, + YulStrings, +} From 4fe995e3709744939c66a9625c2492c910adb7b2 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 14 Apr 2025 21:34:01 +0200 Subject: [PATCH 02/54] wip --- Cargo.lock | 1 + crates/fmt-2/Cargo.toml | 2 + crates/fmt-2/src/formatter.rs | 4 +- crates/fmt-2/src/inline_config.rs | 2 +- crates/fmt-2/src/lib.rs | 2 + crates/fmt-2/src/main.rs | 22 ++ crates/fmt-2/src/printer/algorithm.rs | 380 ++++++++++++++++++++++++ crates/fmt-2/src/printer/comment.rs | 204 +++++++++++++ crates/fmt-2/src/printer/comments.rs | 179 +++++++++++ crates/fmt-2/src/printer/convenience.rs | 79 +++++ crates/fmt-2/src/printer/mod.rs | 97 ++++++ crates/fmt-2/src/printer/ring.rs | 80 +++++ 12 files changed, 1049 insertions(+), 3 deletions(-) create mode 100644 crates/fmt-2/src/main.rs create mode 100644 crates/fmt-2/src/printer/algorithm.rs create mode 100644 crates/fmt-2/src/printer/comment.rs create mode 100644 crates/fmt-2/src/printer/comments.rs create mode 100644 crates/fmt-2/src/printer/convenience.rs create mode 100644 crates/fmt-2/src/printer/mod.rs create mode 100644 crates/fmt-2/src/printer/ring.rs diff --git a/Cargo.lock b/Cargo.lock index 08eef7bb17cf7..bf8a58f04252b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3577,6 +3577,7 @@ dependencies = [ "itertools 0.14.0", "similar-asserts", "solang-parser", + "solar-parse", "thiserror 2.0.12", "toml 0.8.20", "tracing", diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml index 761f05b1064c7..649867335532c 100644 --- a/crates/fmt-2/Cargo.toml +++ b/crates/fmt-2/Cargo.toml @@ -15,6 +15,8 @@ workspace = true [dependencies] foundry-config.workspace = true +solar-parse.workspace = true + alloy-primitives.workspace = true ariadne = "0.5" diff --git a/crates/fmt-2/src/formatter.rs b/crates/fmt-2/src/formatter.rs index aee9eee91db12..69ba3a83362d8 100644 --- a/crates/fmt-2/src/formatter.rs +++ b/crates/fmt-2/src/formatter.rs @@ -45,8 +45,8 @@ impl FormatterError { Self::Fmt(std::fmt::Error) } - fn custom(err: impl std::error::Error + Send + Sync + 'static) -> Self { - Self::Custom(Box::new(err)) + pub fn custom(err: impl Into>) -> Self { + Self::Custom(err.into()) } } diff --git a/crates/fmt-2/src/inline_config.rs b/crates/fmt-2/src/inline_config.rs index fb0a3dfb0977e..1de383475a39a 100644 --- a/crates/fmt-2/src/inline_config.rs +++ b/crates/fmt-2/src/inline_config.rs @@ -71,7 +71,7 @@ pub struct InlineConfig { impl InlineConfig { /// Build a new inline config with an iterator of inline config items and their locations in a - /// source file + /// source file. pub fn new(items: impl IntoIterator, src: &str) -> Self { let mut disabled_ranges = vec![]; let mut disabled_range_start = None; diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 006b4db02abe8..4b196f6d62abc 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -5,6 +5,8 @@ #[macro_use] extern crate tracing; +pub mod printer; + mod buffer; pub mod chunk; mod comments; diff --git a/crates/fmt-2/src/main.rs b/crates/fmt-2/src/main.rs new file mode 100644 index 0000000000000..60683d923cfa4 --- /dev/null +++ b/crates/fmt-2/src/main.rs @@ -0,0 +1,22 @@ +use std::{io::Read, path::PathBuf}; + +fn main() { + let args = std::env::args().collect::>(); + let (src, path) = if args.len() < 2 { + let mut s = String::new(); + std::io::stdin().read_to_string(&mut s).unwrap(); + (s, None) + } else { + let path = PathBuf::from(&args[1]); + (std::fs::read_to_string(&path).unwrap(), Some(path)) + }; + match forge_fmt_2::printer::format_source(&src, path.as_deref(), Default::default()) { + Ok(formatted) => { + print!("{formatted}"); + } + Err(e) => { + eprintln!("failed formatting: {e}"); + std::process::exit(1); + } + } +} diff --git a/crates/fmt-2/src/printer/algorithm.rs b/crates/fmt-2/src/printer/algorithm.rs new file mode 100644 index 0000000000000..56844f4acdde3 --- /dev/null +++ b/crates/fmt-2/src/printer/algorithm.rs @@ -0,0 +1,380 @@ +// Adapted from https://github.com/dtolnay/prettyplease/blob/8eb8c14649aea32e810732bd4d64fe519e6b752a/src/algorithm.rs. + +use super::ring::RingBuffer; +use std::{borrow::Cow, cmp, collections::VecDeque, iter}; + +const DEBUG: bool = false; +const DEBUG_INDENT: bool = false; + +// TODO(dani): config + +// Target line width. +const MARGIN: isize = 89; + +// Number of spaces increment at each level of block indentation. +const INDENT: isize = 4; + +// Every line is allowed at least this much space, even if highly indented. +const MIN_SPACE: isize = 60; + +#[derive(Clone, Copy, PartialEq)] +pub enum Breaks { + Consistent, + Inconsistent, +} + +#[derive(Clone, Copy, Default)] +pub struct BreakToken { + pub offset: isize, + pub blank_space: usize, + pub pre_break: Option, + pub post_break: &'static str, + pub no_break: Option, + pub if_nonempty: bool, + pub never_break: bool, +} + +#[derive(Clone, Copy)] +pub struct BeginToken { + pub offset: isize, + pub breaks: Breaks, +} + +#[derive(Clone)] +pub enum Token { + String(Cow<'static, str>), + Break(BreakToken), + Begin(BeginToken), + End, +} + +#[derive(Copy, Clone)] +enum PrintFrame { + Fits(Breaks), + Broken(usize, Breaks), +} + +pub const SIZE_INFINITY: isize = 0xffff; + +pub struct Printer { + out: String, + // Number of spaces left on line + space: isize, + // Ring-buffer of tokens and calculated sizes + buf: RingBuffer, + // Total size of tokens already printed + left_total: isize, + // Total size of tokens enqueued, including printed and not yet printed + right_total: isize, + // Holds the ring-buffer index of the Begin that started the current block, + // possibly with the most recent Break after that Begin (if there is any) on + // top of it. Values are pushed and popped on the back of the queue using it + // like stack, and elsewhere old values are popped from the front of the + // queue as they become irrelevant due to the primary ring-buffer advancing. + scan_stack: VecDeque, + // Stack of blocks-in-progress being flushed by print + print_stack: Vec, + // Level of indentation of current line + indent: usize, + // Buffered indentation to avoid writing trailing whitespace + pending_indentation: usize, +} + +#[derive(Clone)] +struct BufEntry { + token: Token, + size: isize, +} + +impl Printer { + pub fn new() -> Self { + Printer { + out: String::new(), + space: MARGIN, + buf: RingBuffer::new(), + left_total: 0, + right_total: 0, + scan_stack: VecDeque::new(), + print_stack: Vec::new(), + indent: 0, + pending_indentation: 0, + } + } + + pub fn eof(mut self) -> String { + if !self.scan_stack.is_empty() { + self.check_stack(0); + self.advance_left(); + } + self.out + } + + pub fn scan_begin(&mut self, token: BeginToken) { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.buf.clear(); + } + let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total }); + self.scan_stack.push_back(right); + } + + pub fn scan_end(&mut self) { + if self.scan_stack.is_empty() { + self.print_end(); + } else { + if !self.buf.is_empty() { + if let Token::Break(break_token) = self.buf.last().token { + if self.buf.len() >= 2 { + if let Token::Begin(_) = self.buf.second_last().token { + self.buf.pop_last(); + self.buf.pop_last(); + self.scan_stack.pop_back(); + self.scan_stack.pop_back(); + self.right_total -= break_token.blank_space as isize; + return; + } + } + if break_token.if_nonempty { + self.buf.pop_last(); + self.scan_stack.pop_back(); + self.right_total -= break_token.blank_space as isize; + } + } + } + let right = self.buf.push(BufEntry { token: Token::End, size: -1 }); + self.scan_stack.push_back(right); + } + } + + pub fn scan_break(&mut self, token: BreakToken) { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.buf.clear(); + } else { + self.check_stack(0); + } + let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total }); + self.scan_stack.push_back(right); + self.right_total += token.blank_space as isize; + } + + pub fn scan_string(&mut self, string: Cow<'static, str>) { + if self.scan_stack.is_empty() { + self.print_string(string); + } else { + let len = string.len() as isize; + self.buf.push(BufEntry { token: Token::String(string), size: len }); + self.right_total += len; + self.check_stream(); + } + } + + #[track_caller] + pub fn offset(&mut self, offset: isize) { + match &mut self.buf.last_mut().token { + Token::Break(token) => token.offset += offset, + Token::Begin(_) => {} + Token::String(_) | Token::End => unreachable!(), + } + } + + pub fn end_with_max_width(&mut self, max: isize) { + let mut depth = 1; + for &index in self.scan_stack.iter().rev() { + let entry = &self.buf[index]; + match entry.token { + Token::Begin(_) => { + depth -= 1; + if depth == 0 { + if entry.size < 0 { + let actual_width = entry.size + self.right_total; + if actual_width > max { + self.buf.push(BufEntry { + token: Token::String(Cow::Borrowed("")), + size: SIZE_INFINITY, + }); + self.right_total += SIZE_INFINITY; + } + } + break; + } + } + Token::End => depth += 1, + Token::Break(_) => {} + Token::String(_) => unreachable!(), + } + } + self.scan_end(); + } + + pub fn ends_with(&self, ch: char) -> bool { + for i in self.buf.index_range().rev() { + if let Token::String(token) = &self.buf[i].token { + return token.ends_with(ch); + } + } + self.out.ends_with(ch) + } + + fn check_stream(&mut self) { + while self.right_total - self.left_total > self.space { + if *self.scan_stack.front().unwrap() == self.buf.index_range().start { + self.scan_stack.pop_front().unwrap(); + self.buf.first_mut().size = SIZE_INFINITY; + } + + self.advance_left(); + + if self.buf.is_empty() { + break; + } + } + } + + fn advance_left(&mut self) { + while self.buf.first().size >= 0 { + let left = self.buf.pop_first(); + + match left.token { + Token::String(string) => { + self.left_total += left.size; + self.print_string(string); + } + Token::Break(token) => { + self.left_total += token.blank_space as isize; + self.print_break(token, left.size); + } + Token::Begin(token) => self.print_begin(token, left.size), + Token::End => self.print_end(), + } + + if self.buf.is_empty() { + break; + } + } + } + + fn check_stack(&mut self, mut depth: usize) { + while let Some(&index) = self.scan_stack.back() { + let entry = &mut self.buf[index]; + match entry.token { + Token::Begin(_) => { + if depth == 0 { + break; + } + self.scan_stack.pop_back().unwrap(); + entry.size += self.right_total; + depth -= 1; + } + Token::End => { + self.scan_stack.pop_back().unwrap(); + entry.size = 1; + depth += 1; + } + Token::Break(_) => { + self.scan_stack.pop_back().unwrap(); + entry.size += self.right_total; + if depth == 0 { + break; + } + } + Token::String(_) => unreachable!(), + } + } + } + + fn get_top(&self) -> PrintFrame { + const OUTER: PrintFrame = PrintFrame::Broken(0, Breaks::Inconsistent); + self.print_stack.last().map_or(OUTER, PrintFrame::clone) + } + + fn print_begin(&mut self, token: BeginToken, size: isize) { + if DEBUG { + self.out.push(match token.breaks { + Breaks::Consistent => '«', + Breaks::Inconsistent => '‹', + }); + if DEBUG_INDENT { + self.out.extend(token.offset.to_string().chars().map(|ch| match ch { + '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] + [(ch as u8 - b'0') as usize], + '-' => '₋', + _ => unreachable!(), + })); + } + } + if size > self.space { + self.print_stack.push(PrintFrame::Broken(self.indent, token.breaks)); + self.indent = usize::try_from(self.indent as isize + token.offset).unwrap(); + } else { + self.print_stack.push(PrintFrame::Fits(token.breaks)); + } + } + + fn print_end(&mut self) { + let breaks = match self.print_stack.pop().unwrap() { + PrintFrame::Broken(indent, breaks) => { + self.indent = indent; + breaks + } + PrintFrame::Fits(breaks) => breaks, + }; + if DEBUG { + self.out.push(match breaks { + Breaks::Consistent => '»', + Breaks::Inconsistent => '›', + }); + } + } + + fn print_break(&mut self, token: BreakToken, size: isize) { + let fits = token.never_break || + match self.get_top() { + PrintFrame::Fits(..) => true, + PrintFrame::Broken(.., Breaks::Consistent) => false, + PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space, + }; + if fits { + self.pending_indentation += token.blank_space; + self.space -= token.blank_space as isize; + if let Some(no_break) = token.no_break { + self.out.push(no_break); + self.space -= no_break.len_utf8() as isize; + } + if DEBUG { + self.out.push('·'); + } + } else { + if let Some(pre_break) = token.pre_break { + self.print_indent(); + self.out.push(pre_break); + } + if DEBUG { + self.out.push('·'); + } + self.out.push('\n'); + let indent = self.indent as isize + token.offset; + self.pending_indentation = usize::try_from(indent).unwrap(); + self.space = cmp::max(MARGIN - indent, MIN_SPACE); + if !token.post_break.is_empty() { + self.print_indent(); + self.out.push_str(token.post_break); + self.space -= token.post_break.len() as isize; + } + } + } + + fn print_string(&mut self, string: Cow<'static, str>) { + self.print_indent(); + self.out.push_str(&string); + self.space -= string.len() as isize; + } + + fn print_indent(&mut self) { + self.out.reserve(self.pending_indentation); + self.out.extend(iter::repeat(' ').take(self.pending_indentation)); + self.pending_indentation = 0; + } +} diff --git a/crates/fmt-2/src/printer/comment.rs b/crates/fmt-2/src/printer/comment.rs new file mode 100644 index 0000000000000..0a7e38103c0a4 --- /dev/null +++ b/crates/fmt-2/src/printer/comment.rs @@ -0,0 +1,204 @@ +//! Modified from [`rustc_ast::util::comments`](https://github.com/rust-lang/rust/blob/07d3fd1d9b9c1f07475b96a9d168564bf528db68/compiler/rustc_ast/src/util/comments.rs). + +use solar_parse::{ + ast::{CommentKind, Span}, + interface::{BytePos, Symbol}, +}; + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum CommentStyle { + /// No code on either side of each line of the comment + Isolated, + /// Code exists to the left of the comment + Trailing, + /// Code before /* foo */ and after the comment + Mixed, + /// Just a manual blank line "\n\n", for layout + BlankLine, +} + +#[derive(Clone)] +pub struct Comment { + pub style: CommentStyle, + pub lines: Vec, + pub span: Span, +} + +/// A fast conservative estimate on whether the string can contain documentation links. +/// A pair of square brackets `[]` must exist in the string, but we only search for the +/// opening bracket because brackets always go in pairs in practice. +#[inline] +pub fn may_have_doc_links(s: &str) -> bool { + s.contains('[') +} + +/// Makes a doc string more presentable to users. +/// Used by rustdoc and perhaps other tools, but not by rustc. +pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { + fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> { + let mut i = 0; + let mut j = lines.len(); + // first line of all-stars should be omitted + if lines.first().is_some_and(|line| line.chars().all(|c| c == '*')) { + i += 1; + } + + // like the first, a last line of all stars should be omitted + if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') { + j -= 1; + } + + if i != 0 || j != lines.len() { + Some((i, j)) + } else { + None + } + } + + fn get_horizontal_trim(lines: &[&str], kind: CommentKind) -> Option { + let mut i = usize::MAX; + let mut first = true; + + // In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are + // present. However, we first need to strip the empty lines so they don't get in the middle + // when we try to compute the "horizontal trim". + let lines = match kind { + CommentKind::Block => { + // Whatever happens, we skip the first line. + let mut i = lines + .first() + .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 }) + .unwrap_or(0); + let mut j = lines.len(); + + while i < j && lines[i].trim().is_empty() { + i += 1; + } + while j > i && lines[j - 1].trim().is_empty() { + j -= 1; + } + &lines[i..j] + } + CommentKind::Line => lines, + }; + + for line in lines { + for (j, c) in line.chars().enumerate() { + if j > i || !"* \t".contains(c) { + return None; + } + if c == '*' { + if first { + i = j; + first = false; + } else if i != j { + return None; + } + break; + } + } + if i >= line.len() { + return None; + } + } + Some(lines.first()?[..i].to_string()) + } + + let data_s = data.as_str(); + if data_s.contains('\n') { + let mut lines = data_s.lines().collect::>(); + let mut changes = false; + let lines = if let Some((i, j)) = get_vertical_trim(&lines) { + changes = true; + // remove whitespace-only lines from the start/end of lines + &mut lines[i..j] + } else { + &mut lines + }; + if let Some(horizontal) = get_horizontal_trim(lines, kind) { + changes = true; + // remove a "[ \t]*\*" block from each line, if possible + for line in lines.iter_mut() { + if let Some(tmp) = line.strip_prefix(&horizontal) { + *line = tmp; + if kind == CommentKind::Block && + (*line == "*" || line.starts_with("* ") || line.starts_with("**")) + { + *line = &line[1..]; + } + } + } + } + if changes { + return Symbol::intern(&lines.join("\n")); + } + } + data +} + +#[cfg(test)] +mod tests { + use super::*; + use solar_parse::interface::enter; + + #[test] + fn test_block_doc_comment_1() { + enter(|| { + let comment = "\n * Test \n ** Test\n * Test\n"; + let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block); + assert_eq!(stripped.as_str(), " Test \n* Test\n Test"); + }) + } + + #[test] + fn test_block_doc_comment_2() { + enter(|| { + let comment = "\n * Test\n * Test\n"; + let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block); + assert_eq!(stripped.as_str(), " Test\n Test"); + }) + } + + #[test] + fn test_block_doc_comment_3() { + enter(|| { + let comment = "\n let a: *i32;\n *a = 5;\n"; + let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block); + assert_eq!(stripped.as_str(), "let a: *i32;\n*a = 5;"); + }) + } + + #[test] + fn test_line_doc_comment() { + enter(|| { + let stripped = beautify_doc_string(Symbol::intern(" test"), CommentKind::Line); + assert_eq!(stripped.as_str(), " test"); + let stripped = beautify_doc_string(Symbol::intern("! test"), CommentKind::Line); + assert_eq!(stripped.as_str(), "! test"); + let stripped = beautify_doc_string(Symbol::intern("test"), CommentKind::Line); + assert_eq!(stripped.as_str(), "test"); + let stripped = beautify_doc_string(Symbol::intern("!test"), CommentKind::Line); + assert_eq!(stripped.as_str(), "!test"); + }) + } + + #[test] + fn test_doc_blocks() { + enter(|| { + let stripped = beautify_doc_string( + Symbol::intern(" # Returns\n *\n "), + CommentKind::Block, + ); + assert_eq!(stripped.as_str(), " # Returns\n\n"); + + let stripped = beautify_doc_string( + Symbol::intern("\n * # Returns\n *\n "), + CommentKind::Block, + ); + assert_eq!(stripped.as_str(), " # Returns\n\n"); + + let stripped = beautify_doc_string(Symbol::intern("\n * a\n "), CommentKind::Block); + assert_eq!(stripped.as_str(), " a\n"); + }) + } +} diff --git a/crates/fmt-2/src/printer/comments.rs b/crates/fmt-2/src/printer/comments.rs new file mode 100644 index 0000000000000..5e114ff3b1382 --- /dev/null +++ b/crates/fmt-2/src/printer/comments.rs @@ -0,0 +1,179 @@ +use super::comment::{Comment, CommentStyle}; +use solar_parse::{ + ast::Span, + interface::{source_map::SourceFile, BytePos, CharPos, SourceMap}, + lexer::token::RawTokenKind as TokenKind, +}; + +pub struct Comments<'a> { + sm: &'a SourceMap, + // Stored in reverse order so we can consume them by popping. + reversed_comments: Vec, +} + +/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. +/// Otherwise returns `Some(k)` where `k` is first char offset after that leading +/// whitespace. Note that `k` may be outside bounds of `s`. +fn all_whitespace(s: &str, col: CharPos) -> Option { + let mut idx = 0; + for (i, ch) in s.char_indices().take(col.to_usize()) { + if !ch.is_whitespace() { + return None; + } + idx = i + ch.len_utf8(); + } + Some(idx) +} + +fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str { + let len = s.len(); + match all_whitespace(s, col) { + Some(col) => { + if col < len { + &s[col..] + } else { + "" + } + } + None => s, + } +} + +fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec { + let mut res: Vec = vec![]; + let mut lines = text.lines(); + // just push the first line + res.extend(lines.next().map(|it| it.to_string())); + // for other lines, strip common whitespace prefix + for line in lines { + res.push(trim_whitespace_prefix(line, col).to_string()) + } + res +} + +/// Returns the `BytePos` of the beginning of the current line. +fn line_begin_pos(sf: &SourceFile, pos: BytePos) -> BytePos { + let pos = sf.relative_position(pos); + let line_index = sf.lookup_line(pos).unwrap(); + let line_start_pos = sf.lines()[line_index]; + sf.absolute_position(line_start_pos) +} + +fn gather_comments(sf: &SourceFile) -> Vec { + let text = sf.src.as_str(); + let start_bpos = sf.start_pos; + let mut pos = 0; + let mut comments: Vec = Vec::new(); + let mut code_to_the_left = false; + + let make_span = |start, end| Span::new(start, end); + + /* + if let Some(shebang_len) = strip_shebang(text) { + comments.push(Comment { + style: CommentStyle::Isolated, + lines: vec![text[..shebang_len].to_string()], + pos: start_bpos, + }); + pos += shebang_len; + } + */ + + for token in solar_parse::Cursor::new(&text[pos..]) { + let token_text = &text[pos..pos + token.len as usize]; + match token.kind { + TokenKind::Whitespace => { + if let Some(mut idx) = token_text.find('\n') { + code_to_the_left = false; + while let Some(next_newline) = token_text[idx + 1..].find('\n') { + idx += 1 + next_newline; + let start = start_bpos + BytePos((pos + idx) as u32); + let end = start; + comments.push(Comment { + style: CommentStyle::BlankLine, + lines: vec![], + span: make_span(start, end), + }); + } + } + } + TokenKind::BlockComment { is_doc, .. } => { + if !is_doc { + let code_to_the_right = !matches!( + text[pos + token.len as usize..].chars().next(), + Some('\r' | '\n') + ); + let style = match (code_to_the_left, code_to_the_right) { + (_, true) => CommentStyle::Mixed, + (false, false) => CommentStyle::Isolated, + (true, false) => CommentStyle::Trailing, + }; + + // Count the number of chars since the start of the line by rescanning. + let pos_in_file = start_bpos + BytePos(pos as u32); + let line_begin_in_file = line_begin_pos(sf, pos_in_file); + let line_begin_pos = (line_begin_in_file - start_bpos).to_usize(); + let col = CharPos(text[line_begin_pos..pos].chars().count()); + + let lines = split_block_comment_into_lines(token_text, col); + comments.push(Comment { style, lines, pos: pos_in_file }) + } + } + TokenKind::LineComment { is_doc } => { + if !is_doc { + comments.push(Comment { + style: if code_to_the_left { + CommentStyle::Trailing + } else { + CommentStyle::Isolated + }, + lines: vec![token_text.to_string()], + pos: start_bpos + BytePos(pos as u32), + }) + } + } + _ => { + code_to_the_left = true; + } + } + pos += token.len as usize; + } + + comments +} + +impl<'a> Comments<'a> { + pub fn new(sm: &'a SourceMap, sf: &SourceFile) -> Self { + let mut comments = gather_comments(sf); + comments.reverse(); + Comments { sm, reversed_comments: comments } + } + + pub fn peek(&self) -> Option<&Comment> { + self.reversed_comments.last() + } + + pub fn next(&mut self) -> Option { + self.reversed_comments.pop() + } + + pub fn iter(&self) -> impl Iterator { + self.reversed_comments.iter().rev() + } + + fn trailing_comment(&mut self, span: Span, next_pos: Option) -> Option { + if let Some(cmnt) = self.peek() { + if cmnt.style != CommentStyle::Trailing { + return None; + } + let span_line = self.sm.lookup_char_pos(span.hi()); + let comment_line = self.sm.lookup_char_pos(cmnt.pos); + let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1)); + if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line { + return Some(self.next().unwrap()); + } + } + + None + } +} diff --git a/crates/fmt-2/src/printer/convenience.rs b/crates/fmt-2/src/printer/convenience.rs new file mode 100644 index 0000000000000..dc8ecc62b58c4 --- /dev/null +++ b/crates/fmt-2/src/printer/convenience.rs @@ -0,0 +1,79 @@ +use super::algorithm::{self, BeginToken, BreakToken, Breaks, Printer}; +use std::borrow::Cow; + +impl Printer { + pub fn ibox(&mut self, indent: isize) { + self.scan_begin(BeginToken { offset: indent, breaks: Breaks::Inconsistent }); + } + + pub fn cbox(&mut self, indent: isize) { + self.scan_begin(BeginToken { offset: indent, breaks: Breaks::Consistent }); + } + + pub fn end(&mut self) { + self.scan_end(); + } + + pub fn word>>(&mut self, wrd: S) { + let s = wrd.into(); + self.scan_string(s); + } + + fn spaces(&mut self, n: usize) { + self.scan_break(BreakToken { blank_space: n, ..BreakToken::default() }); + } + + pub fn zerobreak(&mut self) { + self.spaces(0); + } + + pub fn space(&mut self) { + self.spaces(1); + } + + pub fn nbsp(&mut self) { + self.word(" "); + } + + pub fn hardbreak(&mut self) { + self.spaces(algorithm::SIZE_INFINITY as usize); + } + + pub fn space_if_nonempty(&mut self) { + self.scan_break(BreakToken { blank_space: 1, if_nonempty: true, ..BreakToken::default() }); + } + + pub fn hardbreak_if_nonempty(&mut self) { + self.scan_break(BreakToken { + blank_space: algorithm::SIZE_INFINITY as usize, + if_nonempty: true, + ..BreakToken::default() + }); + } + + pub fn trailing_comma(&mut self, is_last: bool) { + if is_last { + self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); + } else { + self.word(","); + self.space(); + } + } + + pub fn trailing_comma_or_space(&mut self, is_last: bool) { + if is_last { + self.scan_break(BreakToken { + blank_space: 1, + pre_break: Some(','), + ..BreakToken::default() + }); + } else { + self.word(","); + self.space(); + } + } + + pub fn neverbreak(&mut self) { + self.scan_break(BreakToken { never_break: true, ..BreakToken::default() }); + } +} diff --git a/crates/fmt-2/src/printer/mod.rs b/crates/fmt-2/src/printer/mod.rs new file mode 100644 index 0000000000000..468cb2bfa1e5d --- /dev/null +++ b/crates/fmt-2/src/printer/mod.rs @@ -0,0 +1,97 @@ +use comments::Comments; + +use crate::{FormatterConfig, FormatterError, InlineConfig}; +use std::path::Path; + +mod algorithm; +mod convenience; +mod ring; + +mod comment; +mod comments; + +type Result = std::result::Result; + +pub fn format_file(path: &Path, config: FormatterConfig) -> Result { + let source = std::fs::read_to_string(path).map_err(FormatterError::custom)?; + format_source(&source, Some(path), config) +} + +pub fn format_source(source: &str, path: Option<&Path>, config: FormatterConfig) -> Result { + let sess = + solar_parse::interface::Session::builder().with_buffer_emitter(Default::default()).build(); + let res = sess.enter(|| -> solar_parse::interface::Result<_> { + let name = match path { + Some(path) => solar_parse::interface::source_map::FileName::Real(path.to_path_buf()), + None => solar_parse::interface::source_map::FileName::Custom("fmt".to_string()), + }; + let arena = solar_parse::ast::Arena::new(); + let file = sess + .source_map() + .new_source_file(name, source) + .map_err(|e| sess.dcx.err(e.to_string()).emit())?; + let mut parser = solar_parse::Parser::from_source_file(&sess, &arena, &file); + let ast = parser.parse_file().map_err(|e| e.emit())?; + Ok(format_source_unit(&ast, source, Comments::new(sess.source_map(), &file), config)) + }); + sess.emitted_errors().unwrap().map_err(FormatterError::custom)?; + Ok(res.unwrap()) +} + +fn format_source_unit( + source_unit: &solar_parse::ast::SourceUnit<'_>, + src: &str, + comments: Comments<'_>, + config: FormatterConfig, +) -> String { + let mut state = State::new(config, inline_configs(&comments, src), Some(comments)); + state.source_unit(source_unit); + state.eof() +} + +struct State<'a> { + s: algorithm::Printer, + comments: Option>, + config: FormatterConfig, + inline_config: InlineConfig, +} + +impl std::ops::Deref for State<'_> { + type Target = algorithm::Printer; + + fn deref(&self) -> &Self::Target { + &self.s + } +} + +impl std::ops::DerefMut for State<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.s + } +} + +impl<'a> State<'a> { + fn new( + config: FormatterConfig, + inline_config: InlineConfig, + comments: Option>, + ) -> Self { + Self { s: algorithm::Printer::new(), comments, inline_config, config } + } + + fn once(config: FormatterConfig) { + Self::new(config, InlineConfig::default(), None); + } +} + +fn inline_configs(comments: &Comments<'_>, src: &str) -> InlineConfig { + comments + .iter() + .filter_map(|comment| { + Some((comment, comment.lines.first()?.trim_start().strip_prefix("forgefmt:")?.trim())) + }) + .map(|(comment, item)| { + let loc = comment.loc; + item.parse().map(|out| (loc, out)).map_err(|out| (loc, out)) + }) +} diff --git a/crates/fmt-2/src/printer/ring.rs b/crates/fmt-2/src/printer/ring.rs new file mode 100644 index 0000000000000..7ee04e6eec0f8 --- /dev/null +++ b/crates/fmt-2/src/printer/ring.rs @@ -0,0 +1,80 @@ +use std::{ + collections::VecDeque, + ops::{Index, IndexMut, Range}, +}; + +pub struct RingBuffer { + data: VecDeque, + // Abstract index of data[0] in infinitely sized queue + offset: usize, +} + +impl RingBuffer { + pub fn new() -> Self { + RingBuffer { data: VecDeque::new(), offset: 0 } + } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub fn len(&self) -> usize { + self.data.len() + } + + pub fn push(&mut self, value: T) -> usize { + let index = self.offset + self.data.len(); + self.data.push_back(value); + index + } + + pub fn clear(&mut self) { + self.data.clear(); + } + + pub fn index_range(&self) -> Range { + self.offset..self.offset + self.data.len() + } + + pub fn first(&self) -> &T { + &self.data[0] + } + + pub fn first_mut(&mut self) -> &mut T { + &mut self.data[0] + } + + pub fn pop_first(&mut self) -> T { + self.offset += 1; + self.data.pop_front().unwrap() + } + + pub fn last(&self) -> &T { + self.data.back().unwrap() + } + + pub fn last_mut(&mut self) -> &mut T { + self.data.back_mut().unwrap() + } + + pub fn second_last(&self) -> &T { + &self.data[self.data.len() - 2] + } + + pub fn pop_last(&mut self) { + self.data.pop_back().unwrap(); + } +} + +impl Index for RingBuffer { + type Output = T; + fn index(&self, index: usize) -> &Self::Output { + &self.data[index.checked_sub(self.offset).unwrap()] + } +} + +impl IndexMut for RingBuffer { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.data[index.checked_sub(self.offset).unwrap()] + } +} From 8face9534354624cb6ce94d11def1a898c32362c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 14 Apr 2025 22:22:22 +0200 Subject: [PATCH 03/54] wip --- Cargo.lock | 2 - crates/fmt-2/Cargo.toml | 3 - crates/fmt-2/src/buffer.rs | 442 --- crates/fmt-2/src/chunk.rs | 60 - crates/fmt-2/src/{printer => }/comment.rs | 6 + crates/fmt-2/src/comments.rs | 577 +-- crates/fmt-2/src/formatter.rs | 3885 -------------------- crates/fmt-2/src/helpers.rs | 135 - crates/fmt-2/src/inline_config.rs | 58 +- crates/fmt-2/src/lib.rs | 96 +- crates/fmt-2/src/macros.rs | 125 - crates/fmt-2/src/main.rs | 2 +- crates/fmt-2/src/pp/convenience.rs | 85 + crates/fmt-2/src/pp/helpers.rs | 48 + crates/fmt-2/src/pp/mod.rs | 325 ++ crates/fmt-2/src/pp/ring.rs | 79 + crates/fmt-2/src/printer/algorithm.rs | 380 -- crates/fmt-2/src/printer/comments.rs | 179 - crates/fmt-2/src/printer/convenience.rs | 79 - crates/fmt-2/src/printer/mod.rs | 97 - crates/fmt-2/src/printer/ring.rs | 80 - crates/fmt-2/src/solang_ext/ast_eq.rs | 683 ---- crates/fmt-2/src/solang_ext/loc.rs | 156 - crates/fmt-2/src/solang_ext/mod.rs | 28 - crates/fmt-2/src/solang_ext/safe_unwrap.rs | 52 - crates/fmt-2/src/state.rs | 176 + crates/fmt-2/src/string.rs | 181 - crates/fmt-2/src/visit.rs | 642 ---- 28 files changed, 983 insertions(+), 7678 deletions(-) delete mode 100644 crates/fmt-2/src/buffer.rs delete mode 100644 crates/fmt-2/src/chunk.rs rename crates/fmt-2/src/{printer => }/comment.rs (98%) delete mode 100644 crates/fmt-2/src/formatter.rs delete mode 100644 crates/fmt-2/src/helpers.rs delete mode 100644 crates/fmt-2/src/macros.rs create mode 100644 crates/fmt-2/src/pp/convenience.rs create mode 100644 crates/fmt-2/src/pp/helpers.rs create mode 100644 crates/fmt-2/src/pp/mod.rs create mode 100644 crates/fmt-2/src/pp/ring.rs delete mode 100644 crates/fmt-2/src/printer/algorithm.rs delete mode 100644 crates/fmt-2/src/printer/comments.rs delete mode 100644 crates/fmt-2/src/printer/convenience.rs delete mode 100644 crates/fmt-2/src/printer/mod.rs delete mode 100644 crates/fmt-2/src/printer/ring.rs delete mode 100644 crates/fmt-2/src/solang_ext/ast_eq.rs delete mode 100644 crates/fmt-2/src/solang_ext/loc.rs delete mode 100644 crates/fmt-2/src/solang_ext/mod.rs delete mode 100644 crates/fmt-2/src/solang_ext/safe_unwrap.rs create mode 100644 crates/fmt-2/src/state.rs delete mode 100644 crates/fmt-2/src/string.rs delete mode 100644 crates/fmt-2/src/visit.rs diff --git a/Cargo.lock b/Cargo.lock index bf8a58f04252b..13863b854360f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3572,11 +3572,9 @@ name = "forge-fmt-2" version = "1.1.0" dependencies = [ "alloy-primitives", - "ariadne", "foundry-config", "itertools 0.14.0", "similar-asserts", - "solang-parser", "solar-parse", "thiserror 2.0.12", "toml 0.8.20", diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml index 649867335532c..30cda0270703e 100644 --- a/crates/fmt-2/Cargo.toml +++ b/crates/fmt-2/Cargo.toml @@ -18,10 +18,7 @@ foundry-config.workspace = true solar-parse.workspace = true alloy-primitives.workspace = true - -ariadne = "0.5" itertools.workspace = true -solang-parser.workspace = true thiserror.workspace = true tracing.workspace = true diff --git a/crates/fmt-2/src/buffer.rs b/crates/fmt-2/src/buffer.rs deleted file mode 100644 index 031cccd6169f8..0000000000000 --- a/crates/fmt-2/src/buffer.rs +++ /dev/null @@ -1,442 +0,0 @@ -//! Format buffer. - -use crate::{ - comments::{CommentState, CommentStringExt}, - string::{QuoteState, QuotedStringExt}, -}; -use std::fmt::Write; - -/// An indent group. The group may optionally skip the first line -#[derive(Clone, Debug, Default)] -struct IndentGroup { - skip_line: bool, -} - -#[derive(Clone, Copy, Debug)] -enum WriteState { - LineStart(CommentState), - WriteTokens(CommentState), - WriteString(char), -} - -impl WriteState { - fn comment_state(&self) -> CommentState { - match self { - Self::LineStart(state) => *state, - Self::WriteTokens(state) => *state, - Self::WriteString(_) => CommentState::None, - } - } -} - -impl Default for WriteState { - fn default() -> Self { - Self::LineStart(CommentState::default()) - } -} - -/// A wrapper around a `std::fmt::Write` interface. The wrapper keeps track of indentation as well -/// as information about the last `write_str` command if available. The formatter may also be -/// restricted to a single line, in which case it will throw an error on a newline -#[derive(Clone, Debug)] -pub struct FormatBuffer { - pub w: W, - indents: Vec, - base_indent_len: usize, - tab_width: usize, - last_char: Option, - current_line_len: usize, - restrict_to_single_line: bool, - state: WriteState, -} - -impl FormatBuffer { - pub fn new(w: W, tab_width: usize) -> Self { - Self { - w, - tab_width, - base_indent_len: 0, - indents: vec![], - current_line_len: 0, - last_char: None, - restrict_to_single_line: false, - state: WriteState::default(), - } - } - - /// Create a new temporary buffer based on an existing buffer which retains information about - /// the buffer state, but has a blank String as its underlying `Write` interface - pub fn create_temp_buf(&self) -> FormatBuffer { - let mut new = FormatBuffer::new(String::new(), self.tab_width); - new.base_indent_len = self.total_indent_len(); - new.current_line_len = self.current_line_len(); - new.last_char = self.last_char; - new.restrict_to_single_line = self.restrict_to_single_line; - new.state = match self.state { - WriteState::WriteTokens(state) | WriteState::LineStart(state) => { - WriteState::LineStart(state) - } - WriteState::WriteString(ch) => WriteState::WriteString(ch), - }; - new - } - - /// Restrict the buffer to a single line - pub fn restrict_to_single_line(&mut self, restricted: bool) { - self.restrict_to_single_line = restricted; - } - - /// Indent the buffer by delta - pub fn indent(&mut self, delta: usize) { - self.indents.extend(std::iter::repeat_n(IndentGroup::default(), delta)); - } - - /// Dedent the buffer by delta - pub fn dedent(&mut self, delta: usize) { - self.indents.truncate(self.indents.len() - delta); - } - - /// Get the current level of the indent. This is multiplied by the tab width to get the - /// resulting indent - fn level(&self) -> usize { - self.indents.iter().filter(|i| !i.skip_line).count() - } - - /// Check if the last indent group is being skipped - pub fn last_indent_group_skipped(&self) -> bool { - self.indents.last().map(|i| i.skip_line).unwrap_or(false) - } - - /// Set whether the last indent group should be skipped - pub fn set_last_indent_group_skipped(&mut self, skip_line: bool) { - if let Some(i) = self.indents.last_mut() { - i.skip_line = skip_line - } - } - - /// Get the current indent size (level * tab_width) - pub fn current_indent_len(&self) -> usize { - self.level() * self.tab_width - } - - /// Get the total indent size - pub fn total_indent_len(&self) -> usize { - self.current_indent_len() + self.base_indent_len - } - - /// Get the current written position (this does not include the indent size) - pub fn current_line_len(&self) -> usize { - self.current_line_len - } - - /// Check if the buffer is at the beginning of a new line - pub fn is_beginning_of_line(&self) -> bool { - matches!(self.state, WriteState::LineStart(_)) - } - - /// Start a new indent group (skips first indent) - pub fn start_group(&mut self) { - self.indents.push(IndentGroup { skip_line: true }); - } - - /// End the last indent group - pub fn end_group(&mut self) { - self.indents.pop(); - } - - /// Get the last char written to the buffer - pub fn last_char(&self) -> Option { - self.last_char - } - - /// When writing a newline apply state changes - fn handle_newline(&mut self, mut comment_state: CommentState) { - if comment_state == CommentState::Line { - comment_state = CommentState::None; - } - self.current_line_len = 0; - self.set_last_indent_group_skipped(false); - self.last_char = Some('\n'); - self.state = WriteState::LineStart(comment_state); - } -} - -impl FormatBuffer { - /// Write a raw string to the buffer. This will ignore indents and remove the indents of the - /// written string to match the current base indent of this buffer if it is a temp buffer - pub fn write_raw(&mut self, s: impl AsRef) -> std::fmt::Result { - self._write_raw(s.as_ref()) - } - - fn _write_raw(&mut self, s: &str) -> std::fmt::Result { - let mut lines = s.lines().peekable(); - let mut comment_state = self.state.comment_state(); - while let Some(line) = lines.next() { - // remove the whitespace that covered by the base indent length (this is normally the - // case with temporary buffers as this will be re-added by the underlying IndentWriter - // later on - let (new_comment_state, line_start) = line - .comment_state_char_indices() - .with_state(comment_state) - .take(self.base_indent_len) - .take_while(|(_, _, ch)| ch.is_whitespace()) - .last() - .map(|(state, idx, _)| (state, idx + 1)) - .unwrap_or((comment_state, 0)); - comment_state = new_comment_state; - let trimmed_line = &line[line_start..]; - if !trimmed_line.is_empty() { - self.w.write_str(trimmed_line)?; - self.current_line_len += trimmed_line.len(); - self.last_char = trimmed_line.chars().next_back(); - self.state = WriteState::WriteTokens(comment_state); - } - if lines.peek().is_some() || s.ends_with('\n') { - if self.restrict_to_single_line { - return Err(std::fmt::Error) - } - self.w.write_char('\n')?; - self.handle_newline(comment_state); - } - } - Ok(()) - } -} - -impl Write for FormatBuffer { - fn write_str(&mut self, mut s: &str) -> std::fmt::Result { - if s.is_empty() { - return Ok(()) - } - - let mut indent = " ".repeat(self.current_indent_len()); - - loop { - match self.state { - WriteState::LineStart(mut comment_state) => { - match s.find(|b| b != '\n') { - // No non-empty lines in input, write the entire string (only newlines) - None => { - if !s.is_empty() { - self.w.write_str(s)?; - self.handle_newline(comment_state); - } - break - } - - // We can see the next non-empty line. Write up to the - // beginning of that line, then insert an indent, then - // continue. - Some(len) => { - let (head, tail) = s.split_at(len); - self.w.write_str(head)?; - self.w.write_str(&indent)?; - self.current_line_len = 0; - self.last_char = Some(' '); - // a newline has been inserted - if len > 0 { - if self.last_indent_group_skipped() { - indent = " ".repeat(self.current_indent_len() + self.tab_width); - self.set_last_indent_group_skipped(false); - } - if comment_state == CommentState::Line { - comment_state = CommentState::None; - } - } - s = tail; - self.state = WriteState::WriteTokens(comment_state); - } - } - } - WriteState::WriteTokens(comment_state) => { - if s.is_empty() { - break - } - - // find the next newline or non-comment string separator (e.g. ' or ") - let mut len = 0; - let mut new_state = WriteState::WriteTokens(comment_state); - for (state, idx, ch) in s.comment_state_char_indices().with_state(comment_state) - { - len = idx; - if ch == '\n' { - if self.restrict_to_single_line { - return Err(std::fmt::Error) - } - new_state = WriteState::LineStart(state); - break - } else if state == CommentState::None && (ch == '\'' || ch == '"') { - new_state = WriteState::WriteString(ch); - break - } else { - new_state = WriteState::WriteTokens(state); - } - } - - if matches!(new_state, WriteState::WriteTokens(_)) { - // No newlines or strings found, write the entire string - self.w.write_str(s)?; - self.current_line_len += s.len(); - self.last_char = s.chars().next_back(); - self.state = new_state; - break - } else { - // A newline or string has been found. Write up to that character and - // continue on the tail - let (head, tail) = s.split_at(len + 1); - self.w.write_str(head)?; - s = tail; - match new_state { - WriteState::LineStart(comment_state) => { - self.handle_newline(comment_state) - } - new_state => { - self.current_line_len += head.len(); - self.last_char = head.chars().next_back(); - self.state = new_state; - } - } - } - } - WriteState::WriteString(quote) => { - match s.quoted_ranges().with_state(QuoteState::String(quote)).next() { - // No end found, write the rest of the string - None => { - self.w.write_str(s)?; - self.current_line_len += s.len(); - self.last_char = s.chars().next_back(); - break - } - // String end found, write the string and continue to add tokens after - Some((_, _, len)) => { - let (head, tail) = s.split_at(len + 1); - self.w.write_str(head)?; - if let Some((_, last)) = head.rsplit_once('\n') { - self.set_last_indent_group_skipped(false); - self.current_line_len = last.len(); - } else { - self.current_line_len += head.len(); - } - self.last_char = Some(quote); - s = tail; - self.state = WriteState::WriteTokens(CommentState::None); - } - } - } - } - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const TAB_WIDTH: usize = 4; - - #[test] - fn test_buffer_indents() -> std::fmt::Result { - let delta = 1; - - let mut buf = FormatBuffer::new(String::new(), TAB_WIDTH); - assert_eq!(buf.indents.len(), 0); - assert_eq!(buf.level(), 0); - assert_eq!(buf.current_indent_len(), 0); - - buf.indent(delta); - assert_eq!(buf.indents.len(), delta); - assert_eq!(buf.level(), delta); - assert_eq!(buf.current_indent_len(), delta * TAB_WIDTH); - - buf.indent(delta); - buf.set_last_indent_group_skipped(true); - assert!(buf.last_indent_group_skipped()); - assert_eq!(buf.indents.len(), delta * 2); - assert_eq!(buf.level(), delta); - assert_eq!(buf.current_indent_len(), delta * TAB_WIDTH); - buf.dedent(delta); - - buf.dedent(delta); - assert_eq!(buf.indents.len(), 0); - assert_eq!(buf.level(), 0); - assert_eq!(buf.current_indent_len(), 0); - - // panics on extra dedent - let res = std::panic::catch_unwind(|| buf.clone().dedent(delta)); - assert!(res.is_err()); - - Ok(()) - } - - #[test] - fn test_identical_temp_buf() -> std::fmt::Result { - let content = "test string"; - let multiline_content = "test\nmultiline\nmultiple"; - let mut buf = FormatBuffer::new(String::new(), TAB_WIDTH); - - // create identical temp buf - let mut temp = buf.create_temp_buf(); - writeln!(buf, "{content}")?; - writeln!(temp, "{content}")?; - assert_eq!(buf.w, format!("{content}\n")); - assert_eq!(temp.w, buf.w); - assert_eq!(temp.current_line_len, buf.current_line_len); - assert_eq!(temp.base_indent_len, buf.total_indent_len()); - - let delta = 1; - buf.indent(delta); - - let mut temp_indented = buf.create_temp_buf(); - assert!(temp_indented.w.is_empty()); - assert_eq!(temp_indented.base_indent_len, buf.total_indent_len()); - assert_eq!(temp_indented.level() + delta, buf.level()); - - let indent = " ".repeat(delta * TAB_WIDTH); - - let mut original_buf = buf.clone(); - write!(buf, "{multiline_content}")?; - let expected_content = format!( - "{}\n{}{}", - content, - indent, - multiline_content.lines().collect::>().join(&format!("\n{indent}")) - ); - assert_eq!(buf.w, expected_content); - - write!(temp_indented, "{multiline_content}")?; - - // write temp buf to original and assert the result - write!(original_buf, "{}", temp_indented.w)?; - assert_eq!(buf.w, original_buf.w); - - Ok(()) - } - - #[test] - fn test_preserves_original_content_with_default_settings() -> std::fmt::Result { - let contents = [ - "simple line", - r" - some - multiline - content", - "// comment", - "/* comment */", - r"mutliline - content - // comment1 - with comments - /* comment2 */ ", - ]; - - for content in &contents { - let mut buf = FormatBuffer::new(String::new(), TAB_WIDTH); - write!(buf, "{content}")?; - assert_eq!(&buf.w, content); - } - - Ok(()) - } -} diff --git a/crates/fmt-2/src/chunk.rs b/crates/fmt-2/src/chunk.rs deleted file mode 100644 index 7d9ce25c7fbd0..0000000000000 --- a/crates/fmt-2/src/chunk.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::comments::CommentWithMetadata; - -/// Holds information about a non-whitespace-splittable string, and the surrounding comments -#[derive(Clone, Debug, Default)] -pub struct Chunk { - pub postfixes_before: Vec, - pub prefixes: Vec, - pub content: String, - pub postfixes: Vec, - pub needs_space: Option, -} - -impl From for Chunk { - fn from(string: String) -> Self { - Self { content: string, ..Default::default() } - } -} - -impl From<&str> for Chunk { - fn from(string: &str) -> Self { - Self { content: string.to_owned(), ..Default::default() } - } -} - -// The struct with information about chunks used in the [Formatter::surrounded] method -#[derive(Debug)] -pub struct SurroundingChunk { - pub before: Option, - pub next: Option, - pub spaced: Option, - pub content: String, -} - -impl SurroundingChunk { - pub fn new( - content: impl std::fmt::Display, - before: Option, - next: Option, - ) -> Self { - Self { before, next, content: format!("{content}"), spaced: None } - } - - pub fn spaced(mut self) -> Self { - self.spaced = Some(true); - self - } - - pub fn non_spaced(mut self) -> Self { - self.spaced = Some(false); - self - } - - pub fn loc_before(&self) -> usize { - self.before.unwrap_or_default() - } - - pub fn loc_next(&self) -> Option { - self.next - } -} diff --git a/crates/fmt-2/src/printer/comment.rs b/crates/fmt-2/src/comment.rs similarity index 98% rename from crates/fmt-2/src/printer/comment.rs rename to crates/fmt-2/src/comment.rs index 0a7e38103c0a4..6360f667bfb6c 100644 --- a/crates/fmt-2/src/printer/comment.rs +++ b/crates/fmt-2/src/comment.rs @@ -24,6 +24,12 @@ pub struct Comment { pub span: Span, } +impl Comment { + pub fn pos(&self) -> BytePos { + self.span.lo() + } +} + /// A fast conservative estimate on whether the string can contain documentation links. /// A pair of square brackets `[]` must exist in the string, but we only search for the /// opening bracket because brackets always go in pairs in practice. diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index eafdb998910d9..623266b6573b2 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -1,456 +1,183 @@ -use crate::inline_config::{InlineConfigItem, InvalidInlineConfigItem}; -use itertools::Itertools; -use solang_parser::pt::*; -use std::collections::VecDeque; - -/// The type of a Comment -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CommentType { - /// A Line comment (e.g. `// ...`) - Line, - /// A Block comment (e.g. `/* ... */`) - Block, - /// A Doc Line comment (e.g. `/// ...`) - DocLine, - /// A Doc Block comment (e.g. `/** ... */`) - DocBlock, -} - -/// The comment position -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CommentPosition { - /// Comes before the code it describes - Prefix, - /// Comes after the code it describes - Postfix, -} - -/// Comment with additional metadata -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CommentWithMetadata { - pub ty: CommentType, - pub loc: Loc, - pub has_newline_before: bool, - pub indent_len: usize, - pub comment: String, - pub position: CommentPosition, -} - -impl PartialOrd for CommentWithMetadata { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for CommentWithMetadata { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.loc.cmp(&other.loc) - } -} - -impl CommentWithMetadata { - fn new( - comment: Comment, - position: CommentPosition, - has_newline_before: bool, - indent_len: usize, - ) -> Self { - let (ty, loc, comment) = match comment { - Comment::Line(loc, comment) => (CommentType::Line, loc, comment), - Comment::Block(loc, comment) => (CommentType::Block, loc, comment), - Comment::DocLine(loc, comment) => (CommentType::DocLine, loc, comment), - Comment::DocBlock(loc, comment) => (CommentType::DocBlock, loc, comment), - }; - Self { - comment: comment.trim_end().to_string(), - ty, - loc, - position, - has_newline_before, - indent_len, +use super::comment::{Comment, CommentStyle}; +use solar_parse::{ + ast::Span, + interface::{source_map::SourceFile, BytePos, CharPos, SourceMap}, + lexer::token::RawTokenKind as TokenKind, +}; + +pub struct Comments<'a> { + sm: &'a SourceMap, + // Stored in reverse order so we can consume them by popping. + reversed_comments: Vec, +} + +/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. +/// Otherwise returns `Some(k)` where `k` is first char offset after that leading +/// whitespace. Note that `k` may be outside bounds of `s`. +fn all_whitespace(s: &str, col: CharPos) -> Option { + let mut idx = 0; + for (i, ch) in s.char_indices().take(col.to_usize()) { + if !ch.is_whitespace() { + return None; } + idx = i + ch.len_utf8(); } + Some(idx) +} - /// Construct a comment with metadata by analyzing its surrounding source code - fn from_comment_and_src(comment: Comment, src: &str, last_comment: Option<&Self>) -> Self { - let src_before = &src[..comment.loc().start()]; - if src_before.is_empty() { - return Self::new(comment, CommentPosition::Prefix, false, 0) - } - - let mut lines_before = src_before.lines().rev(); - let this_line = - if src_before.ends_with('\n') { "" } else { lines_before.next().unwrap_or_default() }; - let indent_len = this_line.chars().take_while(|c| c.is_whitespace()).count(); - let last_line = lines_before.next().map(str::trim_start); - - if matches!(comment, Comment::DocLine(..) | Comment::DocBlock(..)) { - return Self::new( - comment, - CommentPosition::Prefix, - last_line.is_none_or(str::is_empty), - indent_len, - ) - } - - // TODO: this loop takes almost the entirety of the time spent in parsing, which is up to - // 80% of `crate::fmt` - let mut code_end = 0; - for (state, idx, ch) in src_before.comment_state_char_indices() { - if matches!(state, CommentState::None) && !ch.is_whitespace() { - code_end = idx; +fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str { + let len = s.len(); + match all_whitespace(s, col) { + Some(col) => { + if col < len { + &s[col..] + } else { + "" } } - - let (position, has_newline_before) = if src_before[code_end..].contains('\n') { - // comment sits on a line without code - if let Some(last_line) = last_line { - if last_line.is_empty() { - // line before is empty - (CommentPosition::Prefix, true) - } else { - // line has something - // check if the last comment after code was a postfix comment - if last_comment - .is_some_and(|last| last.loc.end() > code_end && !last.is_prefix()) - { - // get the indent size of the next item of code - let next_indent_len = src[comment.loc().end()..] - .non_comment_chars() - .take_while(|ch| ch.is_whitespace()) - .fold(indent_len, |indent, ch| if ch == '\n' { 0 } else { indent + 1 }); - if indent_len > next_indent_len { - // the comment indent is bigger than the next code indent - (CommentPosition::Postfix, false) - } else { - // the comment indent is equal to or less than the next code - // indent - (CommentPosition::Prefix, false) - } - } else { - // if there is no postfix comment after the piece of code - (CommentPosition::Prefix, false) + None => s, + } +} + +fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec { + let mut res: Vec = vec![]; + let mut lines = text.lines(); + // just push the first line + res.extend(lines.next().map(|it| it.to_string())); + // for other lines, strip common whitespace prefix + for line in lines { + res.push(trim_whitespace_prefix(line, col).to_string()) + } + res +} + +/// Returns the `BytePos` of the beginning of the current line. +fn line_begin_pos(sf: &SourceFile, pos: BytePos) -> BytePos { + let pos = sf.relative_position(pos); + let line_index = sf.lookup_line(pos).unwrap(); + let line_start_pos = sf.lines()[line_index]; + sf.absolute_position(line_start_pos) +} + +fn gather_comments(sf: &SourceFile) -> Vec { + let text = sf.src.as_str(); + let start_bpos = sf.start_pos; + let mut pos = 0; + let mut comments: Vec = Vec::new(); + let mut code_to_the_left = false; + + let make_span = |range: std::ops::Range| { + Span::new(start_bpos + range.start as u32, start_bpos + range.end as u32) + }; + + /* + if let Some(shebang_len) = strip_shebang(text) { + comments.push(Comment { + style: CommentStyle::Isolated, + lines: vec![text[..shebang_len].to_string()], + pos: start_bpos, + }); + pos += shebang_len; + } + */ + + for token in solar_parse::Cursor::new(&text[pos..]) { + let token_range = pos..pos + token.len as usize; + let span = make_span(token_range.clone()); + let token_text = &text[token_range]; + match token.kind { + TokenKind::Whitespace => { + if let Some(mut idx) = token_text.find('\n') { + code_to_the_left = false; + + while let Some(next_newline) = token_text[idx + 1..].find('\n') { + idx += 1 + next_newline; + let pos = pos + idx; + comments.push(Comment { + style: CommentStyle::BlankLine, + lines: vec![], + span: make_span(pos..pos), + }); } } - } else { - // beginning of file - (CommentPosition::Prefix, false) } - } else { - // comment is after some code - (CommentPosition::Postfix, false) - }; - - Self::new(comment, position, has_newline_before, indent_len) - } - - pub fn is_line(&self) -> bool { - matches!(self.ty, CommentType::Line | CommentType::DocLine) - } - - pub fn is_doc_block(&self) -> bool { - matches!(self.ty, CommentType::DocBlock) - } + TokenKind::BlockComment { is_doc, .. } => { + if !is_doc { + let code_to_the_right = !matches!( + text[pos + token.len as usize..].chars().next(), + Some('\r' | '\n') + ); + let style = match (code_to_the_left, code_to_the_right) { + (_, true) => CommentStyle::Mixed, + (false, false) => CommentStyle::Isolated, + (true, false) => CommentStyle::Trailing, + }; - pub fn is_prefix(&self) -> bool { - matches!(self.position, CommentPosition::Prefix) - } + // Count the number of chars since the start of the line by rescanning. + let pos_in_file = start_bpos + BytePos(pos as u32); + let line_begin_in_file = line_begin_pos(sf, pos_in_file); + let line_begin_pos = (line_begin_in_file - start_bpos).to_usize(); + let col = CharPos(text[line_begin_pos..pos].chars().count()); - pub fn is_before(&self, byte: usize) -> bool { - self.loc.start() < byte - } - - /// Returns the contents of the comment without the start and end tokens - pub fn contents(&self) -> &str { - let mut s = self.comment.as_str(); - if let Some(stripped) = s.strip_prefix(self.start_token()) { - s = stripped; - } - if let Some(end_token) = self.end_token() { - if let Some(stripped) = s.strip_suffix(end_token) { - s = stripped; + let lines = split_block_comment_into_lines(token_text, col); + comments.push(Comment { style, lines, span }) + } + } + TokenKind::LineComment { is_doc } => { + if !is_doc { + comments.push(Comment { + style: if code_to_the_left { + CommentStyle::Trailing + } else { + CommentStyle::Isolated + }, + lines: vec![token_text.to_string()], + span, + }) + } + } + _ => { + code_to_the_left = true; } } - s - } - - /// The start token of the comment - #[inline] - pub const fn start_token(&self) -> &'static str { - match self.ty { - CommentType::Line => "//", - CommentType::Block => "/*", - CommentType::DocLine => "///", - CommentType::DocBlock => "/**", - } - } - - /// The token that gets written on the newline when the - /// comment is wrapped - #[inline] - pub const fn wrap_token(&self) -> &'static str { - match self.ty { - CommentType::Line => "// ", - CommentType::DocLine => "/// ", - CommentType::Block => "", - CommentType::DocBlock => " * ", - } - } - - /// The end token of the comment - #[inline] - pub const fn end_token(&self) -> Option<&'static str> { - match self.ty { - CommentType::Line | CommentType::DocLine => None, - CommentType::Block | CommentType::DocBlock => Some("*/"), - } + pos += token.len as usize; } -} -/// A list of comments -#[derive(Clone, Debug, Default)] -pub struct Comments { - prefixes: VecDeque, - postfixes: VecDeque, + comments } -impl Comments { - pub fn new(mut comments: Vec, src: &str) -> Self { - let mut prefixes = VecDeque::with_capacity(comments.len()); - let mut postfixes = VecDeque::with_capacity(comments.len()); - let mut last_comment = None; - - comments.sort_by_key(|comment| comment.loc()); - for comment in comments { - let comment = CommentWithMetadata::from_comment_and_src(comment, src, last_comment); - let vec = if comment.is_prefix() { &mut prefixes } else { &mut postfixes }; - vec.push_back(comment); - last_comment = Some(vec.back().unwrap()); - } - Self { prefixes, postfixes } - } - - /// Helper for removing comments before a byte offset - fn remove_comments_before( - comments: &mut VecDeque, - byte: usize, - ) -> Vec { - let pos = comments - .iter() - .find_position(|comment| !comment.is_before(byte)) - .map(|(idx, _)| idx) - .unwrap_or_else(|| comments.len()); - if pos == 0 { - return Vec::new() - } - comments.rotate_left(pos); - comments.split_off(comments.len() - pos).into() +impl<'a> Comments<'a> { + pub fn new(sm: &'a SourceMap, sf: &SourceFile) -> Self { + let mut comments = gather_comments(sf); + comments.reverse(); + Comments { sm, reversed_comments: comments } } - /// Remove any prefix comments that occur before the byte offset in the src - pub(crate) fn remove_prefixes_before(&mut self, byte: usize) -> Vec { - Self::remove_comments_before(&mut self.prefixes, byte) + pub fn peek(&self) -> Option<&Comment> { + self.reversed_comments.last() } - /// Remove any postfix comments that occur before the byte offset in the src - pub(crate) fn remove_postfixes_before(&mut self, byte: usize) -> Vec { - Self::remove_comments_before(&mut self.postfixes, byte) + pub fn next(&mut self) -> Option { + self.reversed_comments.pop() } - /// Remove any comments that occur before the byte offset in the src - pub(crate) fn remove_all_comments_before(&mut self, byte: usize) -> Vec { - self.remove_prefixes_before(byte) - .into_iter() - .merge(self.remove_postfixes_before(byte)) - .collect() + pub fn iter(&self) -> impl Iterator { + self.reversed_comments.iter().rev() } - pub(crate) fn pop(&mut self) -> Option { - if self.iter().next()?.is_prefix() { - self.prefixes.pop_front() - } else { - self.postfixes.pop_front() - } - } - - pub(crate) fn iter(&self) -> impl Iterator { - self.prefixes.iter().merge(self.postfixes.iter()) - } - - /// Parse all comments to return a list of inline config items. This will return an iterator of - /// results of parsing comments which start with `forgefmt:` - pub fn parse_inline_config_items( - &self, - ) -> impl Iterator> + '_ - { - self.iter() - .filter_map(|comment| { - Some((comment, comment.contents().trim_start().strip_prefix("forgefmt:")?.trim())) - }) - .map(|(comment, item)| { - let loc = comment.loc; - item.parse().map(|out| (loc, out)).map_err(|out| (loc, out)) - }) - } -} - -/// The state of a character in a string with possible comments -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum CommentState { - /// character not in a comment - #[default] - None, - /// First `/` in line comment start `"//"` - LineStart1, - /// Second `/` in line comment start `"//"` - LineStart2, - /// Character in a line comment - Line, - /// `/` in block comment start `"/*"` - BlockStart1, - /// `*` in block comment start `"/*"` - BlockStart2, - /// Character in a block comment - Block, - /// `*` in block comment end `"*/"` - BlockEnd1, - /// `/` in block comment end `"*/"` - BlockEnd2, -} - -/// An Iterator over characters and indices in a string slice with information about the state of -/// comments -pub struct CommentStateCharIndices<'a> { - iter: std::str::CharIndices<'a>, - state: CommentState, -} - -impl<'a> CommentStateCharIndices<'a> { - #[inline] - fn new(string: &'a str) -> Self { - Self { iter: string.char_indices(), state: CommentState::None } - } - - #[inline] - pub fn with_state(mut self, state: CommentState) -> Self { - self.state = state; - self - } - - #[inline] - pub fn peek(&mut self) -> Option<(usize, char)> { - self.iter.clone().next() - } -} - -impl Iterator for CommentStateCharIndices<'_> { - type Item = (CommentState, usize, char); - - #[inline] - fn next(&mut self) -> Option { - let (idx, ch) = self.iter.next()?; - match self.state { - CommentState::None => { - if ch == '/' { - self.state = match self.peek() { - Some((_, '/')) => CommentState::LineStart1, - Some((_, '*')) => CommentState::BlockStart1, - _ => CommentState::None, - }; - } - } - CommentState::LineStart1 => { - self.state = CommentState::LineStart2; - } - CommentState::LineStart2 => { - self.state = CommentState::Line; - } - CommentState::Line => { - if ch == '\n' { - self.state = CommentState::None; - } - } - CommentState::BlockStart1 => { - self.state = CommentState::BlockStart2; - } - CommentState::BlockStart2 => { - self.state = CommentState::Block; + fn trailing_comment(&mut self, span: Span, next_pos: Option) -> Option { + if let Some(cmnt) = self.peek() { + if cmnt.style != CommentStyle::Trailing { + return None; } - CommentState::Block => { - if ch == '*' { - if let Some((_, '/')) = self.peek() { - self.state = CommentState::BlockEnd1; - } - } - } - CommentState::BlockEnd1 => { - self.state = CommentState::BlockEnd2; - } - CommentState::BlockEnd2 => { - self.state = CommentState::None; + let span_line = self.sm.lookup_char_pos(span.hi()); + let comment_line = self.sm.lookup_char_pos(cmnt.pos()); + let next = next_pos.unwrap_or_else(|| cmnt.pos() + BytePos(1)); + if span.hi() < cmnt.pos() && cmnt.pos() < next && span_line.line == comment_line.line { + return Some(self.next().unwrap()); } } - Some((self.state, idx, ch)) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - #[inline] - fn count(self) -> usize { - self.iter.count() - } -} - -impl std::iter::FusedIterator for CommentStateCharIndices<'_> {} - -/// An Iterator over characters in a string slice which are not a apart of comments -pub struct NonCommentChars<'a>(CommentStateCharIndices<'a>); - -impl Iterator for NonCommentChars<'_> { - type Item = char; - - #[inline] - fn next(&mut self) -> Option { - for (state, _, ch) in self.0.by_ref() { - if state == CommentState::None { - return Some(ch) - } - } None } } - -/// Helpers for iterating over comment containing strings -pub trait CommentStringExt { - fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_>; - - #[inline] - fn non_comment_chars(&self) -> NonCommentChars<'_> { - NonCommentChars(self.comment_state_char_indices()) - } - - #[inline] - fn trim_comments(&self) -> String { - self.non_comment_chars().collect() - } -} - -impl CommentStringExt for T -where - T: AsRef, -{ - #[inline] - fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_> { - CommentStateCharIndices::new(self.as_ref()) - } -} - -impl CommentStringExt for str { - #[inline] - fn comment_state_char_indices(&self) -> CommentStateCharIndices<'_> { - CommentStateCharIndices::new(self) - } -} diff --git a/crates/fmt-2/src/formatter.rs b/crates/fmt-2/src/formatter.rs deleted file mode 100644 index 69ba3a83362d8..0000000000000 --- a/crates/fmt-2/src/formatter.rs +++ /dev/null @@ -1,3885 +0,0 @@ -//! A Solidity formatter - -use crate::{ - buffer::*, - chunk::*, - comments::{ - CommentPosition, CommentState, CommentStringExt, CommentType, CommentWithMetadata, Comments, - }, - format_diagnostics_report, - helpers::import_path_string, - macros::*, - solang_ext::{pt::*, *}, - string::{QuoteState, QuotedStringExt}, - visit::{Visitable, Visitor}, - FormatterConfig, InlineConfig, IntTypes, -}; -use alloy_primitives::Address; -use foundry_config::fmt::{HexUnderscore, MultilineFuncHeaderStyle, SingleLineBlockStyle}; -use itertools::{Either, Itertools}; -use solang_parser::diagnostics::Diagnostic; -use std::{fmt::Write, path::PathBuf, str::FromStr}; -use thiserror::Error; - -type Result = std::result::Result; - -/// A custom Error thrown by the Formatter -#[derive(Debug, Error)] -pub enum FormatterError { - /// Error thrown by `std::fmt::Write` interfaces - #[error(transparent)] - Fmt(#[from] std::fmt::Error), - /// Encountered invalid parse tree item. - #[error("encountered invalid parse tree item at {0:?}")] - InvalidParsedItem(Loc), - /// Failed to parse the source code - #[error("failed to parse file:\n{}", format_diagnostics_report(_0, _1.as_deref(), _2))] - Parse(String, Option, Vec), - /// All other errors - #[error(transparent)] - Custom(Box), -} - -impl FormatterError { - fn fmt() -> Self { - Self::Fmt(std::fmt::Error) - } - - pub fn custom(err: impl Into>) -> Self { - Self::Custom(err.into()) - } -} - -#[expect(unused_macros)] -macro_rules! format_err { - ($msg:literal $(,)?) => { - $crate::formatter::FormatterError::custom($msg.to_string()) - }; - ($err:expr $(,)?) => { - $crate::formatter::FormatterError::custom($err) - }; - ($fmt:expr, $($arg:tt)*) => { - $crate::formatter::FormatterError::custom(format!($fmt, $($arg)*)) - }; -} - -macro_rules! bail { - ($msg:literal $(,)?) => { - return Err($crate::formatter::format_err!($msg)) - }; - ($err:expr $(,)?) => { - return Err($err) - }; - ($fmt:expr, $($arg:tt)*) => { - return Err($crate::formatter::format_err!($fmt, $(arg)*)) - }; -} - -// TODO: store context entities as references without copying -/// Current context of the Formatter (e.g. inside Contract or Function definition) -#[derive(Debug, Default)] -struct Context { - contract: Option, - function: Option, - if_stmt_single_line: Option, -} - -impl Context { - /// Returns true if the current function context is the constructor - pub(crate) fn is_constructor_function(&self) -> bool { - self.function.as_ref().is_some_and(|f| matches!(f.ty, FunctionTy::Constructor)) - } -} - -/// A Solidity formatter -#[derive(Debug)] -pub struct Formatter<'a, W> { - buf: FormatBuffer, - source: &'a str, - config: FormatterConfig, - temp_bufs: Vec>, - context: Context, - comments: Comments, - inline_config: InlineConfig, -} - -impl<'a, W: Write> Formatter<'a, W> { - pub fn new( - w: W, - source: &'a str, - comments: Comments, - inline_config: InlineConfig, - config: FormatterConfig, - ) -> Self { - Self { - buf: FormatBuffer::new(w, config.tab_width), - source, - config, - temp_bufs: Vec::new(), - context: Context::default(), - comments, - inline_config, - } - } - - /// Get the Write interface of the current temp buffer or the underlying Write - fn buf(&mut self) -> &mut dyn Write { - match &mut self.temp_bufs[..] { - [] => &mut self.buf as &mut dyn Write, - [.., buf] => buf as &mut dyn Write, - } - } - - /// Casts the current writer `w` as a `String` reference. Should only be used for debugging. - unsafe fn buf_contents(&self) -> &String { - *(&self.buf.w as *const W as *const &mut String) - } - - /// Casts the current `W` writer or the current temp buffer as a `String` reference. - /// Should only be used for debugging. - #[expect(dead_code)] - unsafe fn temp_buf_contents(&self) -> &String { - match &self.temp_bufs[..] { - [] => self.buf_contents(), - [.., buf] => &buf.w, - } - } - - buf_fn! { fn indent(&mut self, delta: usize) } - buf_fn! { fn dedent(&mut self, delta: usize) } - buf_fn! { fn start_group(&mut self) } - buf_fn! { fn end_group(&mut self) } - buf_fn! { fn create_temp_buf(&self) -> FormatBuffer } - buf_fn! { fn restrict_to_single_line(&mut self, restricted: bool) } - buf_fn! { fn current_line_len(&self) -> usize } - buf_fn! { fn total_indent_len(&self) -> usize } - buf_fn! { fn is_beginning_of_line(&self) -> bool } - buf_fn! { fn last_char(&self) -> Option } - buf_fn! { fn last_indent_group_skipped(&self) -> bool } - buf_fn! { fn set_last_indent_group_skipped(&mut self, skip: bool) } - buf_fn! { fn write_raw(&mut self, s: impl AsRef) -> std::fmt::Result } - - /// Do the callback within the context of a temp buffer - fn with_temp_buf( - &mut self, - mut fun: impl FnMut(&mut Self) -> Result<()>, - ) -> Result> { - self.temp_bufs.push(self.create_temp_buf()); - let res = fun(self); - let out = self.temp_bufs.pop().unwrap(); - res?; - Ok(out) - } - - /// Does the next written character require whitespace before - fn next_char_needs_space(&self, next_char: char) -> bool { - if self.is_beginning_of_line() { - return false - } - let last_char = - if let Some(last_char) = self.last_char() { last_char } else { return false }; - if last_char.is_whitespace() || next_char.is_whitespace() { - return false - } - match last_char { - '{' => match next_char { - '{' | '[' | '(' => false, - '/' => true, - _ => self.config.bracket_spacing, - }, - '(' | '.' | '[' => matches!(next_char, '/'), - '/' => true, - _ => match next_char { - '}' => self.config.bracket_spacing, - ')' | ',' | '.' | ';' | ']' => false, - _ => true, - }, - } - } - - /// Is length of the `text` with respect to already written line <= `config.line_length` - fn will_it_fit(&self, text: impl AsRef) -> bool { - let text = text.as_ref(); - if text.is_empty() { - return true - } - if text.contains('\n') { - return false - } - let space: usize = self.next_char_needs_space(text.chars().next().unwrap()).into(); - self.config.line_length >= - self.total_indent_len() - .saturating_add(self.current_line_len()) - .saturating_add(text.chars().count() + space) - } - - /// Write empty brackets with respect to `config.bracket_spacing` setting: - /// `"{ }"` if `true`, `"{}"` if `false` - fn write_empty_brackets(&mut self) -> Result<()> { - let brackets = if self.config.bracket_spacing { "{ }" } else { "{}" }; - write_chunk!(self, "{brackets}")?; - Ok(()) - } - - /// Write semicolon to the buffer - fn write_semicolon(&mut self) -> Result<()> { - write!(self.buf(), ";")?; - Ok(()) - } - - /// Write whitespace separator to the buffer - /// `"\n"` if `multiline` is `true`, `" "` if `false` - fn write_whitespace_separator(&mut self, multiline: bool) -> Result<()> { - if !self.is_beginning_of_line() { - write!(self.buf(), "{}", if multiline { "\n" } else { " " })?; - } - Ok(()) - } - - /// Write new line with preserved `last_indent_group_skipped` flag - fn write_preserved_line(&mut self) -> Result<()> { - let last_indent_group_skipped = self.last_indent_group_skipped(); - writeln!(self.buf())?; - self.set_last_indent_group_skipped(last_indent_group_skipped); - Ok(()) - } - - /// Write unformatted src and comments for given location. - fn write_raw_src(&mut self, loc: Loc) -> Result<()> { - let disabled_stmts_src = String::from_utf8(self.source.as_bytes()[loc.range()].to_vec()) - .map_err(FormatterError::custom)?; - self.write_raw(disabled_stmts_src.trim_end())?; - self.write_whitespace_separator(true)?; - // Remove comments as they're already included in disabled src. - let _ = self.comments.remove_all_comments_before(loc.end()); - Ok(()) - } - - /// Returns number of blank lines in source between two byte indexes - fn blank_lines(&self, start: usize, end: usize) -> usize { - // because of sorting import statements, start can be greater than end - if start > end { - return 0 - } - self.source[start..end].trim_comments().matches('\n').count() - } - - /// Get the byte offset of the next line - fn find_next_line(&self, byte_offset: usize) -> Option { - let mut iter = self.source[byte_offset..].char_indices(); - while let Some((_, ch)) = iter.next() { - match ch { - '\n' => return iter.next().map(|(idx, _)| byte_offset + idx), - '\r' => { - return iter.next().and_then(|(idx, ch)| match ch { - '\n' => iter.next().map(|(idx, _)| byte_offset + idx), - _ => Some(byte_offset + idx), - }) - } - _ => {} - } - } - None - } - - /// Find the next instance of the character in source excluding comments - fn find_next_in_src(&self, byte_offset: usize, needle: char) -> Option { - self.source[byte_offset..] - .comment_state_char_indices() - .position(|(state, _, ch)| needle == ch && state == CommentState::None) - .map(|p| byte_offset + p) - } - - /// Find the start of the next instance of a slice in source - fn find_next_str_in_src(&self, byte_offset: usize, needle: &str) -> Option { - let subset = &self.source[byte_offset..]; - needle.chars().next().and_then(|first_char| { - subset - .comment_state_char_indices() - .position(|(state, idx, ch)| { - first_char == ch && - state == CommentState::None && - idx + needle.len() <= subset.len() && - subset[idx..idx + needle.len()] == *needle - }) - .map(|p| byte_offset + p) - }) - } - - /// Extends the location to the next instance of a character. Returns true if the loc was - /// extended - fn extend_loc_until(&self, loc: &mut Loc, needle: char) -> bool { - if let Some(end) = self.find_next_in_src(loc.end(), needle).map(|offset| offset + 1) { - *loc = loc.with_end(end); - true - } else { - false - } - } - - /// Return the flag whether the attempt should be made - /// to write the block on a single line. - /// If the block style is configured to [SingleLineBlockStyle::Preserve], - /// lookup whether there was a newline introduced in `[start_from, end_at]` range - /// where `end_at` is the start of the block. - fn should_attempt_block_single_line( - &mut self, - stmt: &mut Statement, - start_from: usize, - ) -> bool { - match self.config.single_line_statement_blocks { - SingleLineBlockStyle::Single => true, - SingleLineBlockStyle::Multi => false, - SingleLineBlockStyle::Preserve => { - let end_at = match stmt { - Statement::Block { statements, .. } if !statements.is_empty() => { - statements.first().as_ref().unwrap().loc().start() - } - Statement::Expression(loc, _) => loc.start(), - _ => stmt.loc().start(), - }; - - self.find_next_line(start_from).is_some_and(|loc| loc >= end_at) - } - } - } - - /// Create a chunk given a string and the location information - fn chunk_at( - &mut self, - byte_offset: usize, - next_byte_offset: Option, - needs_space: Option, - content: impl std::fmt::Display, - ) -> Chunk { - Chunk { - postfixes_before: self.comments.remove_postfixes_before(byte_offset), - prefixes: self.comments.remove_prefixes_before(byte_offset), - content: content.to_string(), - postfixes: next_byte_offset - .map(|byte_offset| self.comments.remove_postfixes_before(byte_offset)) - .unwrap_or_default(), - needs_space, - } - } - - /// Create a chunk given a callback - fn chunked( - &mut self, - byte_offset: usize, - next_byte_offset: Option, - mut fun: impl FnMut(&mut Self) -> Result<()>, - ) -> Result { - self.chunked_mono(byte_offset, next_byte_offset, &mut fun) - } - - fn chunked_mono( - &mut self, - byte_offset: usize, - next_byte_offset: Option, - fun: &mut dyn FnMut(&mut Self) -> Result<()>, - ) -> Result { - let postfixes_before = self.comments.remove_postfixes_before(byte_offset); - let prefixes = self.comments.remove_prefixes_before(byte_offset); - let content = self.with_temp_buf(fun)?.w; - let postfixes = next_byte_offset - .map(|byte_offset| self.comments.remove_postfixes_before(byte_offset)) - .unwrap_or_default(); - Ok(Chunk { postfixes_before, prefixes, content, postfixes, needs_space: None }) - } - - /// Create a chunk given a [Visitable] item - fn visit_to_chunk( - &mut self, - byte_offset: usize, - next_byte_offset: Option, - visitable: &mut impl Visitable, - ) -> Result { - self.chunked(byte_offset, next_byte_offset, |fmt| { - visitable.visit(fmt)?; - Ok(()) - }) - } - - /// Transform [Visitable] items to the list of chunks - fn items_to_chunks<'b>( - &mut self, - next_byte_offset: Option, - items: impl Iterator + 'b, - ) -> Result> { - let mut items = items.peekable(); - let mut out = Vec::with_capacity(items.size_hint().1.unwrap_or(0)); - while let Some((loc, item)) = items.next() { - let chunk_next_byte_offset = - items.peek().map(|(loc, _)| loc.start()).or(next_byte_offset); - - let chunk = if self.inline_config.is_disabled(loc) { - // If item format is disabled, we determine last disabled line from item and create - // chunk with raw src. - let mut disabled_loc = loc; - self.chunked(disabled_loc.start(), chunk_next_byte_offset, |fmt| { - while fmt.inline_config.is_disabled(disabled_loc) { - if let Some(next_line) = fmt.find_next_line(disabled_loc.end()) { - disabled_loc = disabled_loc.with_end(next_line); - } else { - break; - } - } - fmt.write_raw_src(disabled_loc)?; - Ok(()) - })? - } else { - self.visit_to_chunk(loc.start(), chunk_next_byte_offset, item)? - }; - out.push(chunk); - } - Ok(out) - } - - /// Transform [Visitable] items to a list of chunks and then sort those chunks. - fn items_to_chunks_sorted<'b>( - &mut self, - next_byte_offset: Option, - items: impl Iterator + 'b, - ) -> Result> { - let mut items = items.peekable(); - let mut out = Vec::with_capacity(items.size_hint().1.unwrap_or(0)); - while let Some(item) = items.next() { - let chunk_next_byte_offset = - items.peek().map(|next| next.loc().start()).or(next_byte_offset); - let chunk = self.visit_to_chunk(item.loc().start(), chunk_next_byte_offset, item)?; - out.push((item, chunk)); - } - out.sort_by(|(a, _), (b, _)| a.cmp(b)); - Ok(out.into_iter().map(|(_, c)| c).collect()) - } - - /// Write a comment to the buffer formatted. - /// WARNING: This may introduce a newline if the comment is a Line comment - /// or if the comment are wrapped - fn write_comment(&mut self, comment: &CommentWithMetadata, is_first: bool) -> Result<()> { - if self.inline_config.is_disabled(comment.loc) { - return self.write_raw_comment(comment) - } - - match comment.position { - CommentPosition::Prefix => self.write_prefix_comment(comment, is_first), - CommentPosition::Postfix => self.write_postfix_comment(comment), - } - } - - /// Write a comment with position [CommentPosition::Prefix] - fn write_prefix_comment( - &mut self, - comment: &CommentWithMetadata, - is_first: bool, - ) -> Result<()> { - if !self.is_beginning_of_line() { - self.write_preserved_line()?; - } - if !is_first && comment.has_newline_before { - self.write_preserved_line()?; - } - - if matches!(comment.ty, CommentType::DocBlock) { - let mut lines = comment.contents().trim().lines(); - writeln!(self.buf(), "{}", comment.start_token())?; - lines.try_for_each(|l| self.write_doc_block_line(comment, l))?; - write!(self.buf(), " {}", comment.end_token().unwrap())?; - self.write_preserved_line()?; - return Ok(()) - } - - write!(self.buf(), "{}", comment.start_token())?; - - let mut wrapped = false; - let contents = comment.contents(); - let mut lines = contents.lines().peekable(); - while let Some(line) = lines.next() { - wrapped |= self.write_comment_line(comment, line)?; - if lines.peek().is_some() { - self.write_preserved_line()?; - } - } - - if let Some(end) = comment.end_token() { - // Check if the end token in the original comment was on the separate line - if !wrapped && comment.comment.lines().count() > contents.lines().count() { - self.write_preserved_line()?; - } - write!(self.buf(), "{end}")?; - } - if self.find_next_line(comment.loc.end()).is_some() { - self.write_preserved_line()?; - } - - Ok(()) - } - - /// Write a comment with position [CommentPosition::Postfix] - fn write_postfix_comment(&mut self, comment: &CommentWithMetadata) -> Result<()> { - let indented = self.is_beginning_of_line(); - self.indented_if(indented, 1, |fmt| { - if !indented && fmt.next_char_needs_space('/') { - fmt.write_whitespace_separator(false)?; - } - - write!(fmt.buf(), "{}", comment.start_token())?; - let start_token_pos = fmt.current_line_len(); - - let mut lines = comment.contents().lines().peekable(); - fmt.grouped(|fmt| { - while let Some(line) = lines.next() { - fmt.write_comment_line(comment, line)?; - if lines.peek().is_some() { - fmt.write_whitespace_separator(true)?; - } - } - Ok(()) - })?; - - if let Some(end) = comment.end_token() { - // If comment is not multiline, end token has to be aligned with the start - if fmt.is_beginning_of_line() { - write!(fmt.buf(), "{}{end}", " ".repeat(start_token_pos))?; - } else { - write!(fmt.buf(), "{end}")?; - } - } - - if comment.is_line() { - fmt.write_whitespace_separator(true)?; - } - Ok(()) - }) - } - - /// Write the line of a doc block comment line - fn write_doc_block_line(&mut self, comment: &CommentWithMetadata, line: &str) -> Result<()> { - if line.trim().starts_with('*') { - let line = line.trim().trim_start_matches('*'); - let needs_space = line.chars().next().is_some_and(|ch| !ch.is_whitespace()); - write!(self.buf(), " *{}", if needs_space { " " } else { "" })?; - self.write_comment_line(comment, line)?; - self.write_whitespace_separator(true)?; - return Ok(()) - } - - let indent_whitespace_count = line - .char_indices() - .take_while(|(idx, ch)| ch.is_whitespace() && *idx <= self.buf.current_indent_len()) - .count(); - let to_skip = indent_whitespace_count - indent_whitespace_count % self.config.tab_width; - write!(self.buf(), " *")?; - let content = &line[to_skip..]; - if !content.trim().is_empty() { - write!(self.buf(), " ")?; - self.write_comment_line(comment, &line[to_skip..])?; - } - self.write_whitespace_separator(true)?; - Ok(()) - } - - /// Write a comment line that might potentially overflow the maximum line length - /// and, if configured, will be wrapped to the next line. - fn write_comment_line(&mut self, comment: &CommentWithMetadata, line: &str) -> Result { - if self.will_it_fit(line) || !self.config.wrap_comments { - let start_with_ws = - line.chars().next().map(|ch| ch.is_whitespace()).unwrap_or_default(); - if !self.is_beginning_of_line() || !start_with_ws { - write!(self.buf(), "{line}")?; - return Ok(false) - } - - // if this is the beginning of the line, - // the comment should start with at least an indent - let indent = self.buf.current_indent_len(); - let mut chars = line - .char_indices() - .skip_while(|(idx, ch)| ch.is_whitespace() && *idx < indent) - .map(|(_, ch)| ch); - let padded = format!("{}{}", " ".repeat(indent), chars.join("")); - self.write_raw(padded)?; - return Ok(false) - } - - let mut words = line.split(' ').peekable(); - while let Some(word) = words.next() { - if self.is_beginning_of_line() { - write!(self.buf(), "{}", word.trim_start())?; - } else { - self.write_raw(word)?; - } - - if let Some(next) = words.peek() { - if !word.is_empty() && !self.will_it_fit(next) { - // the next word doesn't fit on this line, - // write remaining words on the next - self.write_whitespace_separator(true)?; - // write newline wrap token - write!(self.buf(), "{}", comment.wrap_token())?; - self.write_comment_line(comment, &words.join(" "))?; - return Ok(true) - } - - self.write_whitespace_separator(false)?; - } - } - Ok(false) - } - - /// Write a raw comment. This is like [`write_comment`](Self::write_comment) but won't do any - /// formatting or worry about whitespace behind the comment. - fn write_raw_comment(&mut self, comment: &CommentWithMetadata) -> Result<()> { - self.write_raw(&comment.comment)?; - if comment.is_line() { - self.write_preserved_line()?; - } - Ok(()) - } - - // TODO handle whitespace between comments for disabled sections - /// Write multiple comments - fn write_comments<'b>( - &mut self, - comments: impl IntoIterator, - ) -> Result<()> { - let mut comments = comments.into_iter().peekable(); - let mut last_byte_written = match comments.peek() { - Some(comment) => comment.loc.start(), - None => return Ok(()), - }; - let mut is_first = true; - for comment in comments { - let unwritten_whitespace_loc = - Loc::File(comment.loc.file_no(), last_byte_written, comment.loc.start()); - if self.inline_config.is_disabled(unwritten_whitespace_loc) { - self.write_raw(&self.source[unwritten_whitespace_loc.range()])?; - self.write_raw_comment(comment)?; - last_byte_written = if comment.is_line() { - self.find_next_line(comment.loc.end()).unwrap_or_else(|| comment.loc.end()) - } else { - comment.loc.end() - }; - } else { - self.write_comment(comment, is_first)?; - } - is_first = false; - } - Ok(()) - } - - /// Write a postfix comments before a given location - fn write_postfix_comments_before(&mut self, byte_end: usize) -> Result<()> { - let comments = self.comments.remove_postfixes_before(byte_end); - self.write_comments(&comments) - } - - /// Write all prefix comments before a given location - fn write_prefix_comments_before(&mut self, byte_end: usize) -> Result<()> { - let comments = self.comments.remove_prefixes_before(byte_end); - self.write_comments(&comments) - } - - /// Check if a chunk will fit on the current line - fn will_chunk_fit(&mut self, format_string: &str, chunk: &Chunk) -> Result { - if let Some(chunk_str) = self.simulate_to_single_line(|fmt| fmt.write_chunk(chunk))? { - Ok(self.will_it_fit(format_string.replacen("{}", &chunk_str, 1))) - } else { - Ok(false) - } - } - - /// Check if a separated list of chunks will fit on the current line - fn are_chunks_separated_multiline<'b>( - &mut self, - format_string: &str, - items: impl IntoIterator, - separator: &str, - ) -> Result { - let items = items.into_iter().collect_vec(); - if let Some(chunks) = self.simulate_to_single_line(|fmt| { - fmt.write_chunks_separated(items.iter().copied(), separator, false) - })? { - Ok(!self.will_it_fit(format_string.replacen("{}", &chunks, 1))) - } else { - Ok(true) - } - } - - /// Write the chunk and any surrounding comments into the buffer - /// This will automatically add whitespace before the chunk given the rule set in - /// `next_char_needs_space`. If the chunk does not fit on the current line it will be put on - /// to the next line - fn write_chunk(&mut self, chunk: &Chunk) -> Result<()> { - // handle comments before chunk - self.write_comments(&chunk.postfixes_before)?; - self.write_comments(&chunk.prefixes)?; - - // trim chunk start - let content = if chunk.content.starts_with('\n') { - let mut chunk = chunk.content.trim_start().to_string(); - chunk.insert(0, '\n'); - chunk - } else if chunk.content.starts_with(' ') { - let mut chunk = chunk.content.trim_start().to_string(); - chunk.insert(0, ' '); - chunk - } else { - chunk.content.clone() - }; - - if !content.is_empty() { - // add whitespace if necessary - let needs_space = chunk - .needs_space - .unwrap_or_else(|| self.next_char_needs_space(content.chars().next().unwrap())); - if needs_space { - if self.will_it_fit(&content) { - write!(self.buf(), " ")?; - } else { - writeln!(self.buf())?; - } - } - - // write chunk - write!(self.buf(), "{content}")?; - } - - // write any postfix comments - self.write_comments(&chunk.postfixes)?; - - Ok(()) - } - - /// Write chunks separated by a separator. If `multiline`, each chunk will be written to a - /// separate line - fn write_chunks_separated<'b>( - &mut self, - chunks: impl IntoIterator, - separator: &str, - multiline: bool, - ) -> Result<()> { - let mut chunks = chunks.into_iter().peekable(); - while let Some(chunk) = chunks.next() { - let mut chunk = chunk.clone(); - - // handle postfixes before and add newline if necessary - self.write_comments(&std::mem::take(&mut chunk.postfixes_before))?; - if multiline && !self.is_beginning_of_line() { - writeln!(self.buf())?; - } - - // remove postfixes so we can add separator between - let postfixes = std::mem::take(&mut chunk.postfixes); - - self.write_chunk(&chunk)?; - - // add separator - if chunks.peek().is_some() { - write!(self.buf(), "{separator}")?; - self.write_comments(&postfixes)?; - if multiline && !self.is_beginning_of_line() { - writeln!(self.buf())?; - } - } else { - self.write_comments(&postfixes)?; - } - } - Ok(()) - } - - /// Apply the callback indented by the indent size - fn indented(&mut self, delta: usize, fun: impl FnMut(&mut Self) -> Result<()>) -> Result<()> { - self.indented_if(true, delta, fun) - } - - /// Apply the callback indented by the indent size if the condition is true - fn indented_if( - &mut self, - condition: bool, - delta: usize, - mut fun: impl FnMut(&mut Self) -> Result<()>, - ) -> Result<()> { - if condition { - self.indent(delta); - } - let res = fun(self); - if condition { - self.dedent(delta); - } - res?; - Ok(()) - } - - /// Apply the callback into an indent group. The first line of the indent group is not - /// indented but lines thereafter are - fn grouped(&mut self, mut fun: impl FnMut(&mut Self) -> Result<()>) -> Result { - self.start_group(); - let res = fun(self); - let indented = !self.last_indent_group_skipped(); - self.end_group(); - res?; - Ok(indented) - } - - /// Add a function context around a procedure and revert the context at the end of the procedure - /// regardless of the response - fn with_function_context( - &mut self, - context: FunctionDefinition, - mut fun: impl FnMut(&mut Self) -> Result<()>, - ) -> Result<()> { - self.context.function = Some(context); - let res = fun(self); - self.context.function = None; - res - } - - /// Add a contract context around a procedure and revert the context at the end of the procedure - /// regardless of the response - fn with_contract_context( - &mut self, - context: ContractDefinition, - mut fun: impl FnMut(&mut Self) -> Result<()>, - ) -> Result<()> { - self.context.contract = Some(context); - let res = fun(self); - self.context.contract = None; - res - } - - /// Create a transaction. The result of the transaction is not applied to the buffer unless - /// `Transacton::commit` is called - fn transact<'b>( - &'b mut self, - fun: impl FnMut(&mut Self) -> Result<()>, - ) -> Result> { - Transaction::new(self, fun) - } - - /// Do the callback and return the result on the buffer as a string - fn simulate_to_string(&mut self, fun: impl FnMut(&mut Self) -> Result<()>) -> Result { - Ok(self.transact(fun)?.buffer) - } - - /// Turn a chunk and its surrounding comments into a string - fn chunk_to_string(&mut self, chunk: &Chunk) -> Result { - self.simulate_to_string(|fmt| fmt.write_chunk(chunk)) - } - - /// Try to create a string based on a callback. If the string does not fit on a single line - /// this will return `None` - fn simulate_to_single_line( - &mut self, - mut fun: impl FnMut(&mut Self) -> Result<()>, - ) -> Result> { - let mut single_line = false; - let tx = self.transact(|fmt| { - fmt.restrict_to_single_line(true); - single_line = match fun(fmt) { - Ok(()) => true, - Err(FormatterError::Fmt(_)) => false, - Err(err) => bail!(err), - }; - Ok(()) - })?; - Ok(if single_line && tx.will_it_fit(&tx.buffer) { Some(tx.buffer) } else { None }) - } - - /// Try to apply a callback to a single line. If the callback cannot be applied to a single - /// line the callback will not be applied to the buffer and `false` will be returned. Otherwise - /// `true` will be returned - fn try_on_single_line(&mut self, mut fun: impl FnMut(&mut Self) -> Result<()>) -> Result { - let mut single_line = false; - let tx = self.transact(|fmt| { - fmt.restrict_to_single_line(true); - single_line = match fun(fmt) { - Ok(()) => true, - Err(FormatterError::Fmt(_)) => false, - Err(err) => bail!(err), - }; - Ok(()) - })?; - Ok(if single_line && tx.will_it_fit(&tx.buffer) { - tx.commit()?; - true - } else { - false - }) - } - - /// Surrounds a callback with parentheses. The callback will try to be applied to a single - /// line. If the callback cannot be applied to a single line the callback will applied to the - /// nextline indented. The callback receives a `multiline` hint as the second argument which - /// receives `true` in the latter case - fn surrounded( - &mut self, - first: SurroundingChunk, - last: SurroundingChunk, - mut fun: impl FnMut(&mut Self, bool) -> Result<()>, - ) -> Result<()> { - let first_chunk = - self.chunk_at(first.loc_before(), first.loc_next(), first.spaced, first.content); - self.write_chunk(&first_chunk)?; - - let multiline = !self.try_on_single_line(|fmt| { - fun(fmt, false)?; - let last_chunk = - fmt.chunk_at(last.loc_before(), last.loc_next(), last.spaced, &last.content); - fmt.write_chunk(&last_chunk)?; - Ok(()) - })?; - - if multiline { - self.indented(1, |fmt| { - fmt.write_whitespace_separator(true)?; - let stringified = fmt.with_temp_buf(|fmt| fun(fmt, true))?.w; - write_chunk!(fmt, "{}", stringified.trim_start()) - })?; - if !last.content.trim_start().is_empty() { - self.indented(1, |fmt| fmt.write_whitespace_separator(true))?; - } - let last_chunk = - self.chunk_at(last.loc_before(), last.loc_next(), last.spaced, &last.content); - self.write_chunk(&last_chunk)?; - } - - Ok(()) - } - - /// Write each [Visitable] item on a separate line. The function will check if there are any - /// blank lines between each visitable statement and will apply a single blank line if there - /// exists any. The `needs_space` callback can force a newline and is given the last_item if - /// any and the next item as arguments - fn write_lined_visitable<'b, I, V, F>( - &mut self, - loc: Loc, - items: I, - needs_space_fn: F, - ) -> Result<()> - where - I: Iterator + 'b, - V: Visitable + CodeLocation + 'b, - F: Fn(&V, &V) -> bool, - { - let mut items = items.collect::>(); - items.reverse(); - // get next item - let pop_next = |fmt: &mut Self, items: &mut Vec<&'b mut V>| { - let comment = - fmt.comments.iter().next().filter(|comment| comment.loc.end() < loc.end()); - let item = items.last(); - if let (Some(comment), Some(item)) = (comment, item) { - if comment.loc < item.loc() { - Some(Either::Left(fmt.comments.pop().unwrap())) - } else { - Some(Either::Right(items.pop().unwrap())) - } - } else if comment.is_some() { - Some(Either::Left(fmt.comments.pop().unwrap())) - } else if item.is_some() { - Some(Either::Right(items.pop().unwrap())) - } else { - None - } - }; - // get whitespace between to offsets. this needs to account for possible left over - // semicolons which are not included in the `Loc` - let unwritten_whitespace = |from: usize, to: usize| { - let to = to.max(from); - let mut loc = Loc::File(loc.file_no(), from, to); - let src = &self.source[from..to]; - if let Some(semi) = src.find(';') { - loc = loc.with_start(from + semi + 1); - } - (loc, &self.source[loc.range()]) - }; - - let mut last_byte_written = match ( - self.comments.iter().next().filter(|comment| comment.loc.end() < loc.end()), - items.last(), - ) { - (Some(comment), Some(item)) => comment.loc.min(item.loc()), - (None, Some(item)) => item.loc(), - (Some(comment), None) => comment.loc, - (None, None) => return Ok(()), - } - .start(); - - let mut last_loc: Option = None; - let mut visited_locs: Vec = Vec::new(); - - // marker for whether the next item needs additional space - let mut needs_space = false; - let mut last_comment = None; - - while let Some(mut line_item) = pop_next(self, &mut items) { - let loc = line_item.as_ref().either(|c| c.loc, |i| i.loc()); - let (unwritten_whitespace_loc, unwritten_whitespace) = - unwritten_whitespace(last_byte_written, loc.start()); - let ignore_whitespace = if self.inline_config.is_disabled(unwritten_whitespace_loc) { - trace!("Unwritten whitespace: {unwritten_whitespace:?}"); - self.write_raw(unwritten_whitespace)?; - true - } else { - false - }; - match line_item.as_mut() { - Either::Left(comment) => { - if ignore_whitespace { - self.write_raw_comment(comment)?; - if unwritten_whitespace.contains('\n') { - needs_space = false; - } - } else { - self.write_comment(comment, last_loc.is_none())?; - if last_loc.is_some() && comment.has_newline_before { - needs_space = false; - } - } - } - Either::Right(item) => { - if !ignore_whitespace { - self.write_whitespace_separator(true)?; - if let Some(mut last_loc) = last_loc { - // here's an edge case when we reordered items so the last_loc isn't - // necessarily the item that directly precedes the current item because - // the order might have changed, so we need to find the last item that - // is before the current item by checking the recorded locations - if let Some(last_item) = visited_locs - .iter() - .rev() - .find(|prev_item| prev_item.start() > last_loc.end()) - { - last_loc = *last_item; - } - - // The blank lines check is susceptible additional trailing new lines - // because the block docs can contain - // multiple lines, but the function def should follow directly after the - // block comment - let is_last_doc_comment = matches!( - last_comment, - Some(CommentWithMetadata { ty: CommentType::DocBlock, .. }) - ); - - if needs_space || - (!is_last_doc_comment && - self.blank_lines(last_loc.end(), loc.start()) > 1) - { - writeln!(self.buf())?; - } - } - } - if let Some(next_item) = items.last() { - needs_space = needs_space_fn(item, next_item); - } - trace!("Visiting {}", { - let n = std::any::type_name::(); - n.strip_prefix("solang_parser::pt::").unwrap_or(n) - }); - item.visit(self)?; - } - } - - last_loc = Some(loc); - visited_locs.push(loc); - - last_comment = None; - - last_byte_written = loc.end(); - if let Some(comment) = line_item.left() { - if comment.is_line() { - last_byte_written = - self.find_next_line(last_byte_written).unwrap_or(last_byte_written); - } - last_comment = Some(comment); - } - } - - // write manually to avoid eof comment being detected as first - let comments = self.comments.remove_prefixes_before(loc.end()); - for comment in comments { - self.write_comment(&comment, false)?; - } - - let (unwritten_src_loc, mut unwritten_whitespace) = - unwritten_whitespace(last_byte_written, loc.end()); - if self.inline_config.is_disabled(unwritten_src_loc) { - if unwritten_src_loc.end() == self.source.len() { - // remove EOF line ending - unwritten_whitespace = unwritten_whitespace - .strip_suffix('\n') - .map(|w| w.strip_suffix('\r').unwrap_or(w)) - .unwrap_or(unwritten_whitespace); - } - trace!("Unwritten whitespace: {unwritten_whitespace:?}"); - self.write_raw(unwritten_whitespace)?; - } - - Ok(()) - } - - /// Visit the right side of an assignment. The function will try to write the assignment on a - /// single line or indented on the next line. If it can't do this it resorts to letting the - /// expression decide how to split itself on multiple lines - fn visit_assignment(&mut self, expr: &mut Expression) -> Result<()> { - if self.try_on_single_line(|fmt| expr.visit(fmt))? { - return Ok(()) - } - - self.write_postfix_comments_before(expr.loc().start())?; - self.write_prefix_comments_before(expr.loc().start())?; - - if self.try_on_single_line(|fmt| fmt.indented(1, |fmt| expr.visit(fmt)))? { - return Ok(()) - } - - let mut fit_on_next_line = false; - self.indented(1, |fmt| { - let tx = fmt.transact(|fmt| { - writeln!(fmt.buf())?; - fit_on_next_line = fmt.try_on_single_line(|fmt| expr.visit(fmt))?; - Ok(()) - })?; - if fit_on_next_line { - tx.commit()?; - } - Ok(()) - })?; - - if !fit_on_next_line { - self.indented_if(expr.is_unsplittable(), 1, |fmt| expr.visit(fmt))?; - } - - Ok(()) - } - - /// Visit the list of comma separated items. - /// If the prefix is not empty, then the function will write - /// the whitespace before the parentheses (if they are required). - fn visit_list( - &mut self, - prefix: &str, - items: &mut [T], - start_offset: Option, - end_offset: Option, - paren_required: bool, - ) -> Result<()> - where - T: Visitable + CodeLocation, - { - write_chunk!(self, "{}", prefix)?; - let whitespace = if !prefix.is_empty() { " " } else { "" }; - let next_after_start_offset = items.first().map(|item| item.loc().start()); - let first_surrounding = SurroundingChunk::new("", start_offset, next_after_start_offset); - let last_surronding = SurroundingChunk::new(")", None, end_offset); - if items.is_empty() { - if paren_required { - write!(self.buf(), "{whitespace}(")?; - self.surrounded(first_surrounding, last_surronding, |fmt, _| { - // write comments before the list end - write_chunk!(fmt, end_offset.unwrap_or_default(), "")?; - Ok(()) - })?; - } - } else { - write!(self.buf(), "{whitespace}(")?; - self.surrounded(first_surrounding, last_surronding, |fmt, multiline| { - let args = - fmt.items_to_chunks(end_offset, items.iter_mut().map(|arg| (arg.loc(), arg)))?; - let multiline = - multiline && fmt.are_chunks_separated_multiline("{}", &args, ",")?; - fmt.write_chunks_separated(&args, ",", multiline)?; - Ok(()) - })?; - } - Ok(()) - } - - /// Visit the block item. Attempt to write it on the single - /// line if requested. Surround by curly braces and indent - /// each line otherwise. Returns `true` if the block fit - /// on a single line - fn visit_block( - &mut self, - loc: Loc, - statements: &mut [T], - attempt_single_line: bool, - attempt_omit_braces: bool, - ) -> Result - where - T: Visitable + CodeLocation, - { - if attempt_single_line && statements.len() == 1 { - let fits_on_single = self.try_on_single_line(|fmt| { - if !attempt_omit_braces { - write!(fmt.buf(), "{{ ")?; - } - statements.first_mut().unwrap().visit(fmt)?; - if !attempt_omit_braces { - write!(fmt.buf(), " }}")?; - } - Ok(()) - })?; - - if fits_on_single { - return Ok(true) - } - } - - // Determine if any of start / end of the block is disabled and block lines boundaries. - let is_start_disabled = self.inline_config.is_disabled(loc.with_end(loc.start())); - let is_end_disabled = self.inline_config.is_disabled(loc.with_start(loc.end())); - let end_of_first_line = self.find_next_line(loc.start()).unwrap_or_default(); - let end_of_last_line = self.find_next_line(loc.end()).unwrap_or_default(); - - // Write first line of the block: - // - as it is until the end of line, if format disabled - // - start block if line formatted - if is_start_disabled { - self.write_raw_src(loc.with_end(end_of_first_line))?; - } else { - write_chunk!(self, "{{")?; - } - - // Write comments and close block if no statement. - if statements.is_empty() { - self.indented(1, |fmt| { - fmt.write_prefix_comments_before(loc.end())?; - fmt.write_postfix_comments_before(loc.end())?; - Ok(()) - })?; - - write_chunk!(self, "}}")?; - return Ok(false) - } - - // Determine writable statements by excluding statements from disabled start / end lines. - // We check the position of last statement from first line (if disabled) and position of - // first statement from last line (if disabled) and slice accordingly. - let writable_statments = match ( - statements.iter().rposition(|stmt| { - is_start_disabled && - self.find_next_line(stmt.loc().end()).unwrap_or_default() == - end_of_first_line - }), - statements.iter().position(|stmt| { - is_end_disabled && - self.find_next_line(stmt.loc().end()).unwrap_or_default() == end_of_last_line - }), - ) { - // We have statements on both disabled start / end lines. - (Some(start), Some(end)) => { - if start == end || start + 1 == end { - None - } else { - Some(&mut statements[start + 1..end]) - } - } - // We have statements only on disabled start line. - (Some(start), None) => { - if start + 1 == statements.len() { - None - } else { - Some(&mut statements[start + 1..]) - } - } - // We have statements only on disabled end line. - (None, Some(end)) => { - if end == 0 { - None - } else { - Some(&mut statements[..end]) - } - } - // No statements on disabled start / end line. - (None, None) => Some(statements), - }; - - // Write statements that are not on any disabled first / last block line. - let mut statements_loc = loc; - if let Some(writable_statements) = writable_statments { - if let Some(first_statement) = writable_statements.first() { - statements_loc = statements_loc.with_start(first_statement.loc().start()); - self.write_whitespace_separator(true)?; - self.write_postfix_comments_before(statements_loc.start())?; - } - // If last line is disabled then statements location ends where last block line starts. - if is_end_disabled { - if let Some(last_statement) = writable_statements.last() { - statements_loc = statements_loc.with_end( - self.find_next_line(last_statement.loc().end()).unwrap_or_default(), - ); - } - } - self.indented(1, |fmt| { - fmt.write_lined_visitable( - statements_loc, - writable_statements.iter_mut(), - |_, _| false, - )?; - Ok(()) - })?; - self.write_whitespace_separator(true)?; - } - - // Write last line of the block: - // - as it is from where statements location ends until the end of last line, if format - // disabled - // - close block if line formatted - if is_end_disabled { - self.write_raw_src(loc.with_start(statements_loc.end()).with_end(end_of_last_line))?; - } else { - if end_of_first_line != end_of_last_line { - self.write_whitespace_separator(true)?; - } - write_chunk!(self, loc.end(), "}}")?; - } - - Ok(false) - } - - /// Visit statement as `Statement::Block`. - fn visit_stmt_as_block( - &mut self, - stmt: &mut Statement, - attempt_single_line: bool, - ) -> Result { - match stmt { - Statement::Block { loc, statements, .. } => { - self.visit_block(*loc, statements, attempt_single_line, true) - } - _ => self.visit_block(stmt.loc(), &mut [stmt], attempt_single_line, true), - } - } - - /// Visit the generic member access expression and - /// attempt flatten it by checking if the inner expression - /// matches a given member access variant. - fn visit_member_access<'b, T, M>( - &mut self, - expr: &'b mut Box, - ident: &mut Identifier, - mut matcher: M, - ) -> Result<()> - where - T: CodeLocation + Visitable, - M: FnMut(&mut Self, &'b mut Box) -> Result, &'b mut Identifier)>>, - { - let chunk_member_access = |fmt: &mut Self, ident: &mut Identifier, expr: &mut Box| { - fmt.chunked(ident.loc.start(), Some(expr.loc().start()), |fmt| ident.visit(fmt)) - }; - - let mut chunks: Vec = vec![chunk_member_access(self, ident, expr)?]; - let mut remaining = expr; - while let Some((inner_expr, inner_ident)) = matcher(self, remaining)? { - chunks.push(chunk_member_access(self, inner_ident, inner_expr)?); - remaining = inner_expr; - } - - chunks.reverse(); - chunks.iter_mut().for_each(|chunk| chunk.content.insert(0, '.')); - - if !self.try_on_single_line(|fmt| fmt.write_chunks_separated(&chunks, "", false))? { - self.grouped(|fmt| fmt.write_chunks_separated(&chunks, "", true))?; - } - Ok(()) - } - - /// Visit the yul string with an optional identifier. - /// If the identifier is present, write the value in the format `:`. - /// - /// Ref: - fn visit_yul_string_with_ident( - &mut self, - loc: Loc, - val: &str, - ident: &mut Option, - ) -> Result<()> { - let ident = - if let Some(ident) = ident { format!(":{}", ident.name) } else { String::new() }; - write_chunk!(self, loc.start(), loc.end(), "{val}{ident}")?; - Ok(()) - } - - /// Format a quoted string as `prefix"string"` where the quote character is handled - /// by the configuration `quote_style` - fn quote_str(&self, loc: Loc, prefix: Option<&str>, string: &str) -> String { - let get_og_quote = || { - self.source[loc.range()] - .quote_state_char_indices() - .find_map( - |(state, _, ch)| { - if matches!(state, QuoteState::Opening(_)) { - Some(ch) - } else { - None - } - }, - ) - .expect("Could not find quote character for quoted string") - }; - let mut quote = self.config.quote_style.quote().unwrap_or_else(get_og_quote); - let mut quoted = format!("{quote}{string}{quote}"); - if !quoted.is_quoted() { - quote = get_og_quote(); - quoted = format!("{quote}{string}{quote}"); - } - let prefix = prefix.unwrap_or(""); - format!("{prefix}{quoted}") - } - - /// Write a quoted string. See `Formatter::quote_str` for more information - fn write_quoted_str(&mut self, loc: Loc, prefix: Option<&str>, string: &str) -> Result<()> { - write_chunk!(self, loc.start(), loc.end(), "{}", self.quote_str(loc, prefix, string)) - } - - /// Write and format numbers. This will fix underscores as well as remove unnecessary 0's and - /// exponents - fn write_num_literal( - &mut self, - loc: Loc, - value: &str, - fractional: Option<&str>, - exponent: &str, - unit: &mut Option, - ) -> Result<()> { - let config = self.config.number_underscore; - - // get source if we preserve underscores - let (value, fractional, exponent) = if config.is_preserve() { - let source = &self.source[loc.start()..loc.end()]; - // Strip unit - let (source, _) = source.split_once(' ').unwrap_or((source, "")); - let (val, exp) = source.split_once(['e', 'E']).unwrap_or((source, "")); - let (val, fract) = - val.split_once('.').map(|(val, fract)| (val, Some(fract))).unwrap_or((val, None)); - ( - val.trim().to_string(), - fract.map(|fract| fract.trim().to_string()), - exp.trim().to_string(), - ) - } else { - // otherwise strip underscores - ( - value.trim().replace('_', ""), - fractional.map(|fract| fract.trim().replace('_', "")), - exponent.trim().replace('_', ""), - ) - }; - - // strip any padded 0's - let val = value.trim_start_matches('0'); - let fract = fractional.as_ref().map(|fract| fract.trim_end_matches('0')); - let (exp_sign, mut exp) = if let Some(exp) = exponent.strip_prefix('-') { - ("-", exp) - } else { - ("", exponent.as_str()) - }; - exp = exp.trim().trim_start_matches('0'); - - let add_underscores = |string: &str, reversed: bool| -> String { - if !config.is_thousands() || string.len() < 5 { - return string.to_string() - } - if reversed { - Box::new(string.as_bytes().chunks(3)) as Box> - } else { - Box::new(string.as_bytes().rchunks(3).rev()) as Box> - } - .map(|chunk| std::str::from_utf8(chunk).expect("valid utf8 content.")) - .collect::>() - .join("_") - }; - - let mut out = String::new(); - if val.is_empty() { - out.push('0'); - } else { - out.push_str(&add_underscores(val, false)); - } - if let Some(fract) = fract { - out.push('.'); - if fract.is_empty() { - out.push('0'); - } else { - // TODO re-enable me on the next solang-parser v0.1.18 - // currently disabled because of the following bug - // https://github.com/hyperledger-labs/solang/pull/954 - // out.push_str(&add_underscores(fract, true)); - out.push_str(fract) - } - } - if !exp.is_empty() { - out.push('e'); - out.push_str(exp_sign); - out.push_str(&add_underscores(exp, false)); - } - - write_chunk!(self, loc.start(), loc.end(), "{out}")?; - self.write_unit(unit) - } - - /// Write and hex literals according to the configuration. - fn write_hex_literal(&mut self, lit: &HexLiteral) -> Result<()> { - let HexLiteral { loc, hex } = lit; - match self.config.hex_underscore { - HexUnderscore::Remove => self.write_quoted_str(*loc, Some("hex"), hex), - HexUnderscore::Preserve => { - let quote = &self.source[loc.start()..loc.end()].trim_start_matches("hex"); - // source is always quoted so we remove the quotes first so we can adhere to the - // configured quoting style - let hex = "e[1..quote.len() - 1]; - self.write_quoted_str(*loc, Some("hex"), hex) - } - HexUnderscore::Bytes => { - // split all bytes - let hex = hex - .chars() - .chunks(2) - .into_iter() - .map(|chunk| chunk.collect::()) - .collect::>() - .join("_"); - self.write_quoted_str(*loc, Some("hex"), &hex) - } - } - } - - /// Write built-in unit. - fn write_unit(&mut self, unit: &mut Option) -> Result<()> { - if let Some(unit) = unit { - write_chunk!(self, unit.loc.start(), unit.loc.end(), "{}", unit.name)?; - } - Ok(()) - } - - /// Write the function header - fn write_function_header( - &mut self, - func: &mut FunctionDefinition, - body_loc: Option, - header_multiline: bool, - ) -> Result { - let func_name = if let Some(ident) = &func.name { - format!("{} {}", func.ty, ident.name) - } else { - func.ty.to_string() - }; - - // calculate locations of chunk groups - let attrs_loc = func.attributes.first().map(|attr| attr.loc()); - let returns_loc = func.returns.first().map(|param| param.0); - - let params_next_offset = attrs_loc - .as_ref() - .or(returns_loc.as_ref()) - .or(body_loc.as_ref()) - .map(|loc| loc.start()); - let attrs_end = returns_loc.as_ref().or(body_loc.as_ref()).map(|loc| loc.start()); - let returns_end = body_loc.as_ref().map(|loc| loc.start()); - - let mut params_multiline = false; - - let params_loc = { - let mut loc = func.loc.with_end(func.loc.start()); - self.extend_loc_until(&mut loc, ')'); - loc - }; - let params_disabled = self.inline_config.is_disabled(params_loc); - if params_disabled { - let chunk = self.chunked(func.loc.start(), None, |fmt| fmt.visit_source(params_loc))?; - params_multiline = chunk.content.contains('\n'); - self.write_chunk(&chunk)?; - } else { - let first_surrounding = SurroundingChunk::new( - format!("{func_name}("), - Some(func.loc.start()), - Some( - func.params - .first() - .map(|param| param.0.start()) - .unwrap_or_else(|| params_loc.end()), - ), - ); - self.surrounded( - first_surrounding, - SurroundingChunk::new(")", None, params_next_offset), - |fmt, multiline| { - let params = fmt.items_to_chunks( - params_next_offset, - func.params - .iter_mut() - .filter_map(|(loc, param)| param.as_mut().map(|param| (*loc, param))), - )?; - let after_params = if !func.attributes.is_empty() || !func.returns.is_empty() { - "" - } else if func.body.is_some() { - " {" - } else { - ";" - }; - let should_multiline = header_multiline && - matches!( - fmt.config.multiline_func_header, - MultilineFuncHeaderStyle::ParamsFirst | - MultilineFuncHeaderStyle::ParamsFirstMulti | - MultilineFuncHeaderStyle::All | - MultilineFuncHeaderStyle::AllParams - ); - params_multiline = should_multiline || - multiline || - fmt.are_chunks_separated_multiline( - &format!("{{}}){after_params}"), - ¶ms, - ",", - )?; - // Write new line if we have only one parameter and params first set, - // or if the function definition is multiline and all params set. - let single_param_multiline = matches!( - fmt.config.multiline_func_header, - MultilineFuncHeaderStyle::ParamsFirst - ) || params_multiline && - matches!( - fmt.config.multiline_func_header, - MultilineFuncHeaderStyle::AllParams - ); - if params.len() == 1 && single_param_multiline { - writeln!(fmt.buf())?; - } - fmt.write_chunks_separated(¶ms, ",", params_multiline)?; - Ok(()) - }, - )?; - } - - let mut write_attributes = |fmt: &mut Self, multiline: bool| -> Result<()> { - // write attributes - if !func.attributes.is_empty() { - let attrs_loc = func - .attributes - .first() - .unwrap() - .loc() - .with_end_from(&func.attributes.last().unwrap().loc()); - if fmt.inline_config.is_disabled(attrs_loc) { - // If params are also disabled then write functions attributes on the same line. - if params_disabled { - fmt.write_whitespace_separator(false)?; - let attrs_src = - String::from_utf8(self.source.as_bytes()[attrs_loc.range()].to_vec()) - .map_err(FormatterError::custom)?; - fmt.write_raw(attrs_src)?; - } else { - fmt.indented(1, |fmt| fmt.visit_source(attrs_loc))?; - } - } else { - fmt.write_postfix_comments_before(attrs_loc.start())?; - fmt.write_whitespace_separator(multiline)?; - let attributes = - fmt.items_to_chunks_sorted(attrs_end, func.attributes.iter_mut())?; - fmt.indented(1, |fmt| { - fmt.write_chunks_separated(&attributes, "", multiline)?; - Ok(()) - })?; - } - } - - // write returns - if !func.returns.is_empty() { - let returns_start_loc = func.returns.first().unwrap().0; - let returns_loc = returns_start_loc.with_end_from(&func.returns.last().unwrap().0); - if fmt.inline_config.is_disabled(returns_loc) { - fmt.write_whitespace_separator(false)?; - let returns_src = - String::from_utf8(self.source.as_bytes()[returns_loc.range()].to_vec()) - .map_err(FormatterError::custom)?; - fmt.write_raw(format!("returns ({returns_src})"))?; - } else { - let mut returns = fmt.items_to_chunks( - returns_end, - func.returns - .iter_mut() - .filter_map(|(loc, param)| param.as_mut().map(|param| (*loc, param))), - )?; - - // there's an issue with function return value that would lead to indent issues because those can be formatted with line breaks - for function_chunk in - returns.iter_mut().filter(|chunk| chunk.content.starts_with("function(")) - { - // this will bypass the recursive indent that was applied when the function - // content was formatted in the chunk - function_chunk.content = function_chunk - .content - .split('\n') - .map(|s| s.trim_start()) - .collect::>() - .join("\n"); - } - - fmt.write_postfix_comments_before(returns_loc.start())?; - fmt.write_whitespace_separator(multiline)?; - fmt.indented(1, |fmt| { - fmt.surrounded( - SurroundingChunk::new("returns (", Some(returns_loc.start()), None), - SurroundingChunk::new(")", None, returns_end), - |fmt, multiline_hint| { - fmt.write_chunks_separated(&returns, ",", multiline_hint)?; - Ok(()) - }, - )?; - Ok(()) - })?; - } - } - Ok(()) - }; - - let should_multiline = header_multiline && - if params_multiline { - matches!( - self.config.multiline_func_header, - MultilineFuncHeaderStyle::All | MultilineFuncHeaderStyle::AllParams - ) - } else { - matches!( - self.config.multiline_func_header, - MultilineFuncHeaderStyle::AttributesFirst - ) - }; - let attrs_multiline = should_multiline || - !self.try_on_single_line(|fmt| { - write_attributes(fmt, false)?; - if !fmt.will_it_fit(if func.body.is_some() { " {" } else { ";" }) { - bail!(FormatterError::fmt()) - } - Ok(()) - })?; - if attrs_multiline { - write_attributes(self, true)?; - } - Ok(attrs_multiline) - } - - /// Write potentially nested `if statements` - fn write_if_stmt( - &mut self, - loc: Loc, - cond: &mut Expression, - if_branch: &mut Box, - else_branch: &mut Option>, - ) -> Result<(), FormatterError> { - let single_line_stmt_wide = self.context.if_stmt_single_line.unwrap_or_default(); - - visit_source_if_disabled_else!(self, loc.with_end(if_branch.loc().start()), { - self.surrounded( - SurroundingChunk::new("if (", Some(loc.start()), Some(cond.loc().start())), - SurroundingChunk::new(")", None, Some(if_branch.loc().start())), - |fmt, _| { - fmt.write_prefix_comments_before(cond.loc().end())?; - cond.visit(fmt)?; - fmt.write_postfix_comments_before(if_branch.loc().start()) - }, - )?; - }); - - let cond_close_paren_loc = - self.find_next_in_src(cond.loc().end(), ')').unwrap_or_else(|| cond.loc().end()); - let attempt_single_line = single_line_stmt_wide && - self.should_attempt_block_single_line(if_branch.as_mut(), cond_close_paren_loc); - let if_branch_is_single_line = self.visit_stmt_as_block(if_branch, attempt_single_line)?; - if single_line_stmt_wide && !if_branch_is_single_line { - bail!(FormatterError::fmt()) - } - - if let Some(else_branch) = else_branch { - self.write_postfix_comments_before(else_branch.loc().start())?; - if if_branch_is_single_line { - writeln!(self.buf())?; - } - write_chunk!(self, else_branch.loc().start(), "else")?; - if let Statement::If(loc, cond, if_branch, else_branch) = else_branch.as_mut() { - self.visit_if(*loc, cond, if_branch, else_branch, false)?; - } else { - let else_branch_is_single_line = - self.visit_stmt_as_block(else_branch, attempt_single_line)?; - if single_line_stmt_wide && !else_branch_is_single_line { - bail!(FormatterError::fmt()) - } - } - } - Ok(()) - } - - /// Sorts grouped import statement alphabetically. - fn sort_imports(&self, source_unit: &mut SourceUnit) { - // first we need to find the grouped import statements - // A group is defined as a set of import statements that are separated by a blank line - let mut import_groups = Vec::new(); - let mut current_group = Vec::new(); - let mut source_unit_parts = source_unit.0.iter().enumerate().peekable(); - while let Some((i, part)) = source_unit_parts.next() { - if let SourceUnitPart::ImportDirective(_) = part { - current_group.push(i); - let current_loc = part.loc(); - if let Some((_, next_part)) = source_unit_parts.peek() { - let next_loc = next_part.loc(); - // import statements are followed by a new line, so if there are more than one - // we have a group - if self.blank_lines(current_loc.end(), next_loc.start()) > 1 { - import_groups.push(std::mem::take(&mut current_group)); - } - } - } else if !current_group.is_empty() { - import_groups.push(std::mem::take(&mut current_group)); - } - } - - if !current_group.is_empty() { - import_groups.push(current_group); - } - - if import_groups.is_empty() { - // nothing to sort - return - } - - // order all groups alphabetically - for group in &import_groups { - // SAFETY: group is not empty - let first = group[0]; - let last = group.last().copied().expect("group is not empty"); - let import_directives = &mut source_unit.0[first..=last]; - - // sort rename style imports alphabetically based on the actual import and not the - // rename - for source_unit_part in import_directives.iter_mut() { - if let SourceUnitPart::ImportDirective(Import::Rename(_, renames, _)) = - source_unit_part - { - renames.sort_by_cached_key(|(og_ident, _)| og_ident.name.clone()); - } - } - - import_directives.sort_by_cached_key(|item| match item { - SourceUnitPart::ImportDirective(import) => match import { - Import::Plain(path, _) => path.to_string(), - Import::GlobalSymbol(path, _, _) => path.to_string(), - Import::Rename(path, _, _) => path.to_string(), - }, - _ => { - unreachable!("import group contains non-import statement") - } - }); - } - } -} - -// Traverse the Solidity Parse Tree and write to the code formatter -impl Visitor for Formatter<'_, W> { - type Error = FormatterError; - - #[instrument(name = "source", skip(self))] - fn visit_source(&mut self, loc: Loc) -> Result<()> { - let source = String::from_utf8(self.source.as_bytes()[loc.range()].to_vec()) - .map_err(FormatterError::custom)?; - let mut lines = source.splitn(2, '\n'); - - write_chunk!(self, loc.start(), "{}", lines.next().unwrap())?; - if let Some(remainder) = lines.next() { - // Call with `self.write_str` and not `write!`, so we can have `\n` at the beginning - // without triggering an indentation - self.write_raw(format!("\n{remainder}"))?; - } - - let _ = self.comments.remove_all_comments_before(loc.end()); - - Ok(()) - } - - #[instrument(name = "SU", skip_all)] - fn visit_source_unit(&mut self, source_unit: &mut SourceUnit) -> Result<()> { - if self.config.sort_imports { - self.sort_imports(source_unit); - } - // TODO: do we need to put pragma and import directives at the top of the file? - // source_unit.0.sort_by_key(|item| match item { - // SourceUnitPart::PragmaDirective(_, _, _) => 0, - // SourceUnitPart::ImportDirective(_, _) => 1, - // _ => usize::MAX, - // }); - let loc = Loc::File( - source_unit - .loc_opt() - .or_else(|| self.comments.iter().next().map(|comment| comment.loc)) - .map(|loc| loc.file_no()) - .unwrap_or_default(), - 0, - self.source.len(), - ); - - self.write_lined_visitable( - loc, - source_unit.0.iter_mut(), - |last_unit, unit| match last_unit { - SourceUnitPart::PragmaDirective(..) => { - !matches!(unit, SourceUnitPart::PragmaDirective(..)) - } - SourceUnitPart::ImportDirective(_) => { - !matches!(unit, SourceUnitPart::ImportDirective(_)) - } - SourceUnitPart::ErrorDefinition(_) => { - !matches!(unit, SourceUnitPart::ErrorDefinition(_)) - } - SourceUnitPart::Using(_) => !matches!(unit, SourceUnitPart::Using(_)), - SourceUnitPart::VariableDefinition(_) => { - !matches!(unit, SourceUnitPart::VariableDefinition(_)) - } - SourceUnitPart::Annotation(_) => false, - _ => true, - }, - )?; - - // EOF newline - if self.last_char() != Some('\n') { - writeln!(self.buf())?; - } - - Ok(()) - } - - #[instrument(name = "contract", skip_all)] - fn visit_contract(&mut self, contract: &mut ContractDefinition) -> Result<()> { - return_source_if_disabled!(self, contract.loc); - - self.with_contract_context(contract.clone(), |fmt| { - let contract_name = contract.name.safe_unwrap(); - - visit_source_if_disabled_else!( - fmt, - contract.loc.with_end_from( - &contract.base.first().map(|b| b.loc).unwrap_or(contract_name.loc) - ), - { - fmt.grouped(|fmt| { - write_chunk!(fmt, contract.loc.start(), "{}", contract.ty)?; - write_chunk!(fmt, contract_name.loc.end(), "{}", contract_name.name)?; - if !contract.base.is_empty() { - write_chunk!( - fmt, - contract_name.loc.end(), - contract.base.first().unwrap().loc.start(), - "is" - )?; - } - Ok(()) - })?; - } - ); - - if !contract.base.is_empty() { - visit_source_if_disabled_else!( - fmt, - contract - .base - .first() - .unwrap() - .loc - .with_end_from(&contract.base.last().unwrap().loc), - { - fmt.indented(1, |fmt| { - let base_end = contract.parts.first().map(|part| part.loc().start()); - let bases = fmt.items_to_chunks( - base_end, - contract.base.iter_mut().map(|base| (base.loc, base)), - )?; - let multiline = - fmt.are_chunks_separated_multiline("{}", &bases, ",")?; - fmt.write_chunks_separated(&bases, ",", multiline)?; - fmt.write_whitespace_separator(multiline)?; - Ok(()) - })?; - } - ); - } - - write_chunk!(fmt, "{{")?; - - fmt.indented(1, |fmt| { - if let Some(first) = contract.parts.first() { - fmt.write_postfix_comments_before(first.loc().start())?; - fmt.write_whitespace_separator(true)?; - } else { - return Ok(()) - } - - if fmt.config.contract_new_lines { - write_chunk!(fmt, "\n")?; - } - - fmt.write_lined_visitable( - contract.loc, - contract.parts.iter_mut(), - |last_part, part| match last_part { - ContractPart::ErrorDefinition(_) => { - !matches!(part, ContractPart::ErrorDefinition(_)) - } - ContractPart::EventDefinition(_) => { - !matches!(part, ContractPart::EventDefinition(_)) - } - ContractPart::VariableDefinition(_) => { - !matches!(part, ContractPart::VariableDefinition(_)) - } - ContractPart::TypeDefinition(_) => { - !matches!(part, ContractPart::TypeDefinition(_)) - } - ContractPart::EnumDefinition(_) => { - !matches!(part, ContractPart::EnumDefinition(_)) - } - ContractPart::Using(_) => !matches!(part, ContractPart::Using(_)), - ContractPart::FunctionDefinition(last_def) => { - if last_def.is_empty() { - match part { - ContractPart::FunctionDefinition(def) => !def.is_empty(), - _ => true, - } - } else { - true - } - } - ContractPart::Annotation(_) => false, - _ => true, - }, - ) - })?; - - if !contract.parts.is_empty() { - fmt.write_whitespace_separator(true)?; - - if fmt.config.contract_new_lines { - write_chunk!(fmt, "\n")?; - } - } - - write_chunk!(fmt, contract.loc.end(), "}}")?; - - Ok(()) - })?; - - Ok(()) - } - - // Support extension for Solana/Substrate - #[instrument(name = "annotation", skip_all)] - fn visit_annotation(&mut self, annotation: &mut Annotation) -> Result<()> { - return_source_if_disabled!(self, annotation.loc); - let id = self.simulate_to_string(|fmt| annotation.id.visit(fmt))?; - write!(self.buf(), "@{id}")?; - write!(self.buf(), "(")?; - annotation.value.visit(self)?; - write!(self.buf(), ")")?; - Ok(()) - } - - #[instrument(name = "pragma", skip_all)] - fn visit_pragma( - &mut self, - loc: Loc, - ident: &mut Option, - string: &mut Option, - ) -> Result<()> { - let (ident, string) = (ident.safe_unwrap(), string.safe_unwrap()); - return_source_if_disabled!(self, loc, ';'); - - let pragma_descriptor = if ident.name == "solidity" { - // There are some issues with parsing Solidity's versions with crates like `semver`: - // 1. Ranges like `>=0.4.21<0.6.0` or `>=0.4.21 <0.6.0` are not parseable at all. - // 2. Versions like `0.8.10` got transformed into `^0.8.10` which is not the same. - // TODO: semver-solidity crate :D - &string.string - } else { - &string.string - }; - - write_chunk!(self, string.loc.end(), "pragma {} {};", &ident.name, pragma_descriptor)?; - - Ok(()) - } - - #[instrument(name = "import_plain", skip_all)] - fn visit_import_plain(&mut self, loc: Loc, import: &mut ImportPath) -> Result<()> { - return_source_if_disabled!(self, loc, ';'); - - self.grouped(|fmt| { - write_chunk!(fmt, loc.start(), import.loc().start(), "import")?; - fmt.write_quoted_str(import.loc(), None, &import_path_string(import))?; - fmt.write_semicolon()?; - Ok(()) - })?; - Ok(()) - } - - #[instrument(name = "import_global", skip_all)] - fn visit_import_global( - &mut self, - loc: Loc, - global: &mut ImportPath, - alias: &mut Identifier, - ) -> Result<()> { - return_source_if_disabled!(self, loc, ';'); - - self.grouped(|fmt| { - write_chunk!(fmt, loc.start(), global.loc().start(), "import")?; - fmt.write_quoted_str(global.loc(), None, &import_path_string(global))?; - write_chunk!(fmt, loc.start(), alias.loc.start(), "as")?; - alias.visit(fmt)?; - fmt.write_semicolon()?; - Ok(()) - })?; - Ok(()) - } - - #[instrument(name = "import_renames", skip_all)] - fn visit_import_renames( - &mut self, - loc: Loc, - imports: &mut [(Identifier, Option)], - from: &mut ImportPath, - ) -> Result<()> { - return_source_if_disabled!(self, loc, ';'); - - if imports.is_empty() { - self.grouped(|fmt| { - write_chunk!(fmt, loc.start(), "import")?; - fmt.write_empty_brackets()?; - write_chunk!(fmt, loc.start(), from.loc().start(), "from")?; - fmt.write_quoted_str(from.loc(), None, &import_path_string(from))?; - fmt.write_semicolon()?; - Ok(()) - })?; - return Ok(()) - } - - let imports_start = imports.first().unwrap().0.loc.start(); - - write_chunk!(self, loc.start(), imports_start, "import")?; - - self.surrounded( - SurroundingChunk::new("{", Some(imports_start), None), - SurroundingChunk::new("}", None, Some(from.loc().start())), - |fmt, _multiline| { - let mut imports = imports.iter_mut().peekable(); - let mut import_chunks = Vec::new(); - while let Some((ident, alias)) = imports.next() { - import_chunks.push(fmt.chunked( - ident.loc.start(), - imports.peek().map(|(ident, _)| ident.loc.start()), - |fmt| { - fmt.grouped(|fmt| { - ident.visit(fmt)?; - if let Some(alias) = alias { - write_chunk!(fmt, ident.loc.end(), alias.loc.start(), "as")?; - alias.visit(fmt)?; - } - Ok(()) - })?; - Ok(()) - }, - )?); - } - - let multiline = fmt.are_chunks_separated_multiline( - &format!("{{}} }} from \"{}\";", import_path_string(from)), - &import_chunks, - ",", - )?; - fmt.write_chunks_separated(&import_chunks, ",", multiline)?; - Ok(()) - }, - )?; - - self.grouped(|fmt| { - write_chunk!(fmt, imports_start, from.loc().start(), "from")?; - fmt.write_quoted_str(from.loc(), None, &import_path_string(from))?; - fmt.write_semicolon()?; - Ok(()) - })?; - - Ok(()) - } - - #[instrument(name = "enum", skip_all)] - fn visit_enum(&mut self, enumeration: &mut EnumDefinition) -> Result<()> { - return_source_if_disabled!(self, enumeration.loc); - - let enum_name = enumeration.name.safe_unwrap_mut(); - let mut name = - self.visit_to_chunk(enum_name.loc.start(), Some(enum_name.loc.end()), enum_name)?; - name.content = format!("enum {} ", name.content); - if enumeration.values.is_empty() { - self.write_chunk(&name)?; - self.write_empty_brackets()?; - } else { - name.content.push('{'); - self.write_chunk(&name)?; - - self.indented(1, |fmt| { - let values = fmt.items_to_chunks( - Some(enumeration.loc.end()), - enumeration.values.iter_mut().map(|ident| { - let ident = ident.safe_unwrap_mut(); - (ident.loc, ident) - }), - )?; - fmt.write_chunks_separated(&values, ",", true)?; - writeln!(fmt.buf())?; - Ok(()) - })?; - write_chunk!(self, "}}")?; - } - - Ok(()) - } - - #[instrument(name = "assembly", skip_all)] - fn visit_assembly( - &mut self, - loc: Loc, - dialect: &mut Option, - block: &mut YulBlock, - flags: &mut Option>, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - - write_chunk!(self, loc.start(), "assembly")?; - if let Some(StringLiteral { loc, string, .. }) = dialect { - write_chunk!(self, loc.start(), loc.end(), "\"{string}\"")?; - } - if let Some(flags) = flags { - if !flags.is_empty() { - let loc_start = flags.first().unwrap().loc.start(); - self.surrounded( - SurroundingChunk::new("(", Some(loc_start), None), - SurroundingChunk::new(")", None, Some(block.loc.start())), - |fmt, _| { - let mut flags = flags.iter_mut().peekable(); - let mut chunks = vec![]; - while let Some(flag) = flags.next() { - let next_byte_offset = - flags.peek().map(|next_flag| next_flag.loc.start()); - chunks.push(fmt.chunked( - flag.loc.start(), - next_byte_offset, - |fmt| { - write!(fmt.buf(), "\"{}\"", flag.string)?; - Ok(()) - }, - )?); - } - fmt.write_chunks_separated(&chunks, ",", false)?; - Ok(()) - }, - )?; - } - } - - block.visit(self) - } - - #[instrument(name = "block", skip_all)] - fn visit_block( - &mut self, - loc: Loc, - unchecked: bool, - statements: &mut Vec, - ) -> Result<()> { - return_source_if_disabled!(self, loc); - if unchecked { - write_chunk!(self, loc.start(), "unchecked ")?; - } - - self.visit_block(loc, statements, false, false)?; - Ok(()) - } - - #[instrument(name = "args", skip_all)] - fn visit_args(&mut self, loc: Loc, args: &mut Vec) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - - write!(self.buf(), "{{")?; - - let mut args_iter = args.iter_mut().peekable(); - let mut chunks = Vec::new(); - while let Some(NamedArgument { loc: arg_loc, name, expr }) = args_iter.next() { - let next_byte_offset = args_iter - .peek() - .map(|NamedArgument { loc: arg_loc, .. }| arg_loc.start()) - .unwrap_or_else(|| loc.end()); - chunks.push(self.chunked(arg_loc.start(), Some(next_byte_offset), |fmt| { - fmt.grouped(|fmt| { - write_chunk!(fmt, name.loc.start(), "{}: ", name.name)?; - expr.visit(fmt) - })?; - Ok(()) - })?); - } - - if let Some(first) = chunks.first_mut() { - if first.prefixes.is_empty() && - first.postfixes_before.is_empty() && - !self.config.bracket_spacing - { - first.needs_space = Some(false); - } - } - let multiline = self.are_chunks_separated_multiline("{}}", &chunks, ",")?; - self.indented_if(multiline, 1, |fmt| fmt.write_chunks_separated(&chunks, ",", multiline))?; - - let prefix = if multiline && !self.is_beginning_of_line() { - "\n" - } else if self.config.bracket_spacing { - " " - } else { - "" - }; - let closing_bracket = format!("{prefix}{}", "}"); - if let Some(arg) = args.last() { - write_chunk!(self, arg.loc.end(), "{closing_bracket}")?; - } else { - write_chunk!(self, "{closing_bracket}")?; - } - - Ok(()) - } - - #[instrument(name = "expr", skip_all)] - fn visit_expr(&mut self, loc: Loc, expr: &mut Expression) -> Result<()> { - return_source_if_disabled!(self, loc); - - match expr { - Expression::Type(loc, typ) => match typ { - Type::Address => write_chunk!(self, loc.start(), "address")?, - Type::AddressPayable => write_chunk!(self, loc.start(), "address payable")?, - Type::Payable => write_chunk!(self, loc.start(), "payable")?, - Type::Bool => write_chunk!(self, loc.start(), "bool")?, - Type::String => write_chunk!(self, loc.start(), "string")?, - Type::Bytes(n) => write_chunk!(self, loc.start(), "bytes{}", n)?, - Type::Rational => write_chunk!(self, loc.start(), "rational")?, - Type::DynamicBytes => write_chunk!(self, loc.start(), "bytes")?, - Type::Int(ref n) | Type::Uint(ref n) => { - let int = if matches!(typ, Type::Int(_)) { "int" } else { "uint" }; - match n { - 256 => match self.config.int_types { - IntTypes::Long => write_chunk!(self, loc.start(), "{int}{n}")?, - IntTypes::Short => write_chunk!(self, loc.start(), "{int}")?, - IntTypes::Preserve => self.visit_source(*loc)?, - }, - _ => write_chunk!(self, loc.start(), "{int}{n}")?, - } - } - Type::Mapping { loc, key, key_name, value, value_name } => { - let arrow_loc = self.find_next_str_in_src(loc.start(), "=>"); - let close_paren_loc = - self.find_next_in_src(value.loc().end(), ')').unwrap_or(loc.end()); - let first = SurroundingChunk::new( - "mapping(", - Some(loc.start()), - Some(key.loc().start()), - ); - let last = SurroundingChunk::new(")", Some(close_paren_loc), Some(loc.end())) - .non_spaced(); - self.surrounded(first, last, |fmt, multiline| { - fmt.grouped(|fmt| { - key.visit(fmt)?; - - if let Some(name) = key_name { - let end_loc = arrow_loc.unwrap_or(value.loc().start()); - write_chunk!(fmt, name.loc.start(), end_loc, " {}", name)?; - } else if let Some(arrow_loc) = arrow_loc { - fmt.write_postfix_comments_before(arrow_loc)?; - } - - let mut write_arrow_and_value = |fmt: &mut Self| { - write!(fmt.buf(), "=> ")?; - value.visit(fmt)?; - if let Some(name) = value_name { - write_chunk!(fmt, name.loc.start(), " {}", name)?; - } - Ok(()) - }; - - let rest_str = fmt.simulate_to_string(&mut write_arrow_and_value)?; - let multiline = multiline && !fmt.will_it_fit(rest_str); - fmt.write_whitespace_separator(multiline)?; - - write_arrow_and_value(fmt)?; - - fmt.write_postfix_comments_before(close_paren_loc)?; - fmt.write_prefix_comments_before(close_paren_loc) - })?; - Ok(()) - })?; - } - Type::Function { .. } => self.visit_source(*loc)?, - }, - Expression::BoolLiteral(loc, val) => { - write_chunk!(self, loc.start(), loc.end(), "{val}")?; - } - Expression::NumberLiteral(loc, val, exp, unit) => { - self.write_num_literal(*loc, val, None, exp, unit)?; - } - Expression::HexNumberLiteral(loc, val, unit) => { - // ref: https://docs.soliditylang.org/en/latest/types.html?highlight=address%20literal#address-literals - let val = if val.len() == 42 { - Address::from_str(val).expect("").to_string() - } else { - val.to_owned() - }; - write_chunk!(self, loc.start(), loc.end(), "{val}")?; - self.write_unit(unit)?; - } - Expression::RationalNumberLiteral(loc, val, fraction, exp, unit) => { - self.write_num_literal(*loc, val, Some(fraction), exp, unit)?; - } - Expression::StringLiteral(vals) => { - for StringLiteral { loc, string, unicode } in vals { - let prefix = if *unicode { Some("unicode") } else { None }; - self.write_quoted_str(*loc, prefix, string)?; - } - } - Expression::HexLiteral(vals) => { - for val in vals { - self.write_hex_literal(val)?; - } - } - Expression::AddressLiteral(loc, val) => { - // support of solana/substrate address literals - self.write_quoted_str(*loc, Some("address"), val)?; - } - Expression::Parenthesis(loc, expr) => { - self.surrounded( - SurroundingChunk::new("(", Some(loc.start()), None), - SurroundingChunk::new(")", None, Some(loc.end())), - |fmt, _| expr.visit(fmt), - )?; - } - Expression::ArraySubscript(_, ty_exp, index_expr) => { - ty_exp.visit(self)?; - write!(self.buf(), "[")?; - index_expr.as_mut().map(|index| index.visit(self)).transpose()?; - write!(self.buf(), "]")?; - } - Expression::ArraySlice(loc, expr, start, end) => { - expr.visit(self)?; - write!(self.buf(), "[")?; - let mut write_slice = |fmt: &mut Self, multiline| -> Result<()> { - if multiline { - fmt.write_whitespace_separator(true)?; - } - fmt.grouped(|fmt| { - start.as_mut().map(|start| start.visit(fmt)).transpose()?; - write!(fmt.buf(), ":")?; - if let Some(end) = end { - let mut chunk = - fmt.chunked(end.loc().start(), Some(loc.end()), |fmt| { - end.visit(fmt) - })?; - if chunk.prefixes.is_empty() && - chunk.postfixes_before.is_empty() && - (start.is_none() || fmt.will_it_fit(&chunk.content)) - { - chunk.needs_space = Some(false); - } - fmt.write_chunk(&chunk)?; - } - Ok(()) - })?; - if multiline { - fmt.write_whitespace_separator(true)?; - } - Ok(()) - }; - - if !self.try_on_single_line(|fmt| write_slice(fmt, false))? { - self.indented(1, |fmt| write_slice(fmt, true))?; - } - - write!(self.buf(), "]")?; - } - Expression::ArrayLiteral(loc, exprs) => { - write_chunk!(self, loc.start(), "[")?; - let chunks = self.items_to_chunks( - Some(loc.end()), - exprs.iter_mut().map(|expr| (expr.loc(), expr)), - )?; - let multiline = self.are_chunks_separated_multiline("{}]", &chunks, ",")?; - self.indented_if(multiline, 1, |fmt| { - fmt.write_chunks_separated(&chunks, ",", multiline)?; - if multiline { - fmt.write_postfix_comments_before(loc.end())?; - fmt.write_prefix_comments_before(loc.end())?; - fmt.write_whitespace_separator(true)?; - } - Ok(()) - })?; - write_chunk!(self, loc.end(), "]")?; - } - Expression::PreIncrement(..) | - Expression::PostIncrement(..) | - Expression::PreDecrement(..) | - Expression::PostDecrement(..) | - Expression::Not(..) | - Expression::UnaryPlus(..) | - Expression::Add(..) | - Expression::Negate(..) | - Expression::Subtract(..) | - Expression::Power(..) | - Expression::Multiply(..) | - Expression::Divide(..) | - Expression::Modulo(..) | - Expression::ShiftLeft(..) | - Expression::ShiftRight(..) | - Expression::BitwiseNot(..) | - Expression::BitwiseAnd(..) | - Expression::BitwiseXor(..) | - Expression::BitwiseOr(..) | - Expression::Less(..) | - Expression::More(..) | - Expression::LessEqual(..) | - Expression::MoreEqual(..) | - Expression::And(..) | - Expression::Or(..) | - Expression::Equal(..) | - Expression::NotEqual(..) => { - let spaced = expr.has_space_around(); - let op = expr.operator().unwrap(); - - match expr.components_mut() { - (Some(left), Some(right)) => { - left.visit(self)?; - - let right_chunk = - self.chunked(right.loc().start(), Some(loc.end()), |fmt| { - write_chunk!(fmt, right.loc().start(), "{op}")?; - right.visit(fmt)?; - Ok(()) - })?; - - self.grouped(|fmt| fmt.write_chunk(&right_chunk))?; - } - (Some(left), None) => { - left.visit(self)?; - write_chunk_spaced!(self, loc.end(), Some(spaced), "{op}")?; - } - (None, Some(right)) => { - write_chunk!(self, right.loc().start(), "{op}")?; - let mut right_chunk = - self.visit_to_chunk(right.loc().end(), Some(loc.end()), right)?; - right_chunk.needs_space = Some(spaced); - self.write_chunk(&right_chunk)?; - } - (None, None) => {} - } - } - Expression::Assign(..) | - Expression::AssignOr(..) | - Expression::AssignAnd(..) | - Expression::AssignXor(..) | - Expression::AssignShiftLeft(..) | - Expression::AssignShiftRight(..) | - Expression::AssignAdd(..) | - Expression::AssignSubtract(..) | - Expression::AssignMultiply(..) | - Expression::AssignDivide(..) | - Expression::AssignModulo(..) => { - let op = expr.operator().unwrap(); - let (left, right) = expr.components_mut(); - let (left, right) = (left.unwrap(), right.unwrap()); - - left.visit(self)?; - write_chunk!(self, "{op}")?; - self.visit_assignment(right)?; - } - Expression::ConditionalOperator(loc, cond, first_expr, second_expr) => { - cond.visit(self)?; - - let first_expr = self.chunked( - first_expr.loc().start(), - Some(second_expr.loc().start()), - |fmt| { - write_chunk!(fmt, "?")?; - first_expr.visit(fmt) - }, - )?; - let second_expr = - self.chunked(second_expr.loc().start(), Some(loc.end()), |fmt| { - write_chunk!(fmt, ":")?; - second_expr.visit(fmt) - })?; - - let chunks = vec![first_expr, second_expr]; - if !self.try_on_single_line(|fmt| fmt.write_chunks_separated(&chunks, "", false))? { - self.grouped(|fmt| fmt.write_chunks_separated(&chunks, "", true))?; - } - } - Expression::Variable(ident) => { - write_chunk!(self, loc.end(), "{}", ident.name)?; - } - Expression::MemberAccess(_, expr, ident) => { - self.visit_member_access(expr, ident, |fmt, expr| match expr.as_mut() { - Expression::MemberAccess(_, inner_expr, inner_ident) => { - Ok(Some((inner_expr, inner_ident))) - } - expr => { - expr.visit(fmt)?; - Ok(None) - } - })?; - } - Expression::List(loc, items) => { - self.surrounded( - SurroundingChunk::new( - "(", - Some(loc.start()), - items.first().map(|item| item.0.start()), - ), - SurroundingChunk::new(")", None, Some(loc.end())), - |fmt, _| { - let items = fmt.items_to_chunks( - Some(loc.end()), - items.iter_mut().map(|(loc, item)| (*loc, item)), - )?; - let write_items = |fmt: &mut Self, multiline| { - fmt.write_chunks_separated(&items, ",", multiline) - }; - if !fmt.try_on_single_line(|fmt| write_items(fmt, false))? { - write_items(fmt, true)?; - } - Ok(()) - }, - )?; - } - Expression::FunctionCall(loc, expr, exprs) => { - self.visit_expr(expr.loc(), expr)?; - self.visit_list("", exprs, Some(expr.loc().end()), Some(loc.end()), true)?; - } - Expression::NamedFunctionCall(loc, expr, args) => { - self.visit_expr(expr.loc(), expr)?; - write!(self.buf(), "(")?; - self.visit_args(*loc, args)?; - write!(self.buf(), ")")?; - } - Expression::FunctionCallBlock(_, expr, stmt) => { - expr.visit(self)?; - stmt.visit(self)?; - } - Expression::New(_, expr) => { - write_chunk!(self, "new ")?; - self.visit_expr(expr.loc(), expr)?; - } - _ => self.visit_source(loc)?, - }; - - Ok(()) - } - - #[instrument(name = "ident", skip_all)] - fn visit_ident(&mut self, loc: Loc, ident: &mut Identifier) -> Result<()> { - return_source_if_disabled!(self, loc); - write_chunk!(self, loc.end(), "{}", ident.name)?; - Ok(()) - } - - #[instrument(name = "ident_path", skip_all)] - fn visit_ident_path(&mut self, idents: &mut IdentifierPath) -> Result<(), Self::Error> { - if idents.identifiers.is_empty() { - return Ok(()) - } - return_source_if_disabled!(self, idents.loc); - - idents.identifiers.iter_mut().skip(1).for_each(|chunk| { - if !chunk.name.starts_with('.') { - chunk.name.insert(0, '.') - } - }); - let chunks = self.items_to_chunks( - Some(idents.loc.end()), - idents.identifiers.iter_mut().map(|ident| (ident.loc, ident)), - )?; - self.grouped(|fmt| { - let multiline = fmt.are_chunks_separated_multiline("{}", &chunks, "")?; - fmt.write_chunks_separated(&chunks, "", multiline) - })?; - Ok(()) - } - - #[instrument(name = "emit", skip_all)] - fn visit_emit(&mut self, loc: Loc, event: &mut Expression) -> Result<()> { - return_source_if_disabled!(self, loc); - write_chunk!(self, loc.start(), "emit")?; - event.visit(self)?; - self.write_semicolon()?; - Ok(()) - } - - #[instrument(name = "var_definition", skip_all)] - fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<()> { - return_source_if_disabled!(self, var.loc, ';'); - - var.ty.visit(self)?; - - let multiline = self.grouped(|fmt| { - let var_name = var.name.safe_unwrap_mut(); - let name_start = var_name.loc.start(); - - let attrs = fmt.items_to_chunks_sorted(Some(name_start), var.attrs.iter_mut())?; - if !fmt.try_on_single_line(|fmt| fmt.write_chunks_separated(&attrs, "", false))? { - fmt.write_chunks_separated(&attrs, "", true)?; - } - - let mut name = fmt.visit_to_chunk(name_start, Some(var_name.loc.end()), var_name)?; - if var.initializer.is_some() { - name.content.push_str(" ="); - } - fmt.write_chunk(&name)?; - - Ok(()) - })?; - - var.initializer - .as_mut() - .map(|init| self.indented_if(multiline, 1, |fmt| fmt.visit_assignment(init))) - .transpose()?; - - self.write_semicolon()?; - - Ok(()) - } - - #[instrument(name = "var_definition_stmt", skip_all)] - fn visit_var_definition_stmt( - &mut self, - loc: Loc, - declaration: &mut VariableDeclaration, - expr: &mut Option, - ) -> Result<()> { - return_source_if_disabled!(self, loc, ';'); - - let declaration = self - .chunked(declaration.loc.start(), None, |fmt| fmt.visit_var_declaration(declaration))?; - let multiline = declaration.content.contains('\n'); - self.write_chunk(&declaration)?; - - if let Some(expr) = expr { - write!(self.buf(), " =")?; - self.indented_if(multiline, 1, |fmt| fmt.visit_assignment(expr))?; - } - - self.write_semicolon() - } - - #[instrument(name = "var_declaration", skip_all)] - fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<()> { - return_source_if_disabled!(self, var.loc); - self.grouped(|fmt| { - var.ty.visit(fmt)?; - if let Some(storage) = &var.storage { - write_chunk!(fmt, storage.loc().end(), "{storage}")?; - } - let var_name = var.name.safe_unwrap(); - write_chunk!(fmt, var_name.loc.end(), "{var_name}") - })?; - Ok(()) - } - - #[instrument(name = "return", skip_all)] - fn visit_return(&mut self, loc: Loc, expr: &mut Option) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc, ';'); - - self.write_postfix_comments_before(loc.start())?; - self.write_prefix_comments_before(loc.start())?; - - if expr.is_none() { - write_chunk!(self, loc.end(), "return;")?; - return Ok(()) - } - - let expr = expr.as_mut().unwrap(); - let expr_loc_start = expr.loc().start(); - let write_return = |fmt: &mut Self| -> Result<()> { - write_chunk!(fmt, loc.start(), "return")?; - fmt.write_postfix_comments_before(expr_loc_start)?; - Ok(()) - }; - - let mut write_return_with_expr = |fmt: &mut Self| -> Result<()> { - let fits_on_single = fmt.try_on_single_line(|fmt| { - write_return(fmt)?; - expr.visit(fmt) - })?; - if fits_on_single { - return Ok(()) - } - - let mut fit_on_next_line = false; - let tx = fmt.transact(|fmt| { - fmt.grouped(|fmt| { - write_return(fmt)?; - if !fmt.is_beginning_of_line() { - fmt.write_whitespace_separator(true)?; - } - fit_on_next_line = fmt.try_on_single_line(|fmt| expr.visit(fmt))?; - Ok(()) - })?; - Ok(()) - })?; - if fit_on_next_line { - tx.commit()?; - return Ok(()) - } - - write_return(fmt)?; - expr.visit(fmt)?; - Ok(()) - }; - - write_return_with_expr(self)?; - write_chunk!(self, loc.end(), ";")?; - Ok(()) - } - - #[instrument(name = "revert", skip_all)] - fn visit_revert( - &mut self, - loc: Loc, - error: &mut Option, - args: &mut Vec, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc, ';'); - write_chunk!(self, loc.start(), "revert")?; - if let Some(error) = error { - error.visit(self)?; - } - self.visit_list("", args, None, Some(loc.end()), true)?; - self.write_semicolon()?; - - Ok(()) - } - - #[instrument(name = "revert_named_args", skip_all)] - fn visit_revert_named_args( - &mut self, - loc: Loc, - error: &mut Option, - args: &mut Vec, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc, ';'); - - write_chunk!(self, loc.start(), "revert")?; - let mut error_indented = false; - if let Some(error) = error { - if !self.try_on_single_line(|fmt| error.visit(fmt))? { - error.visit(self)?; - error_indented = true; - } - } - - if args.is_empty() { - write!(self.buf(), "({{}});")?; - return Ok(()) - } - - write!(self.buf(), "(")?; - self.indented_if(error_indented, 1, |fmt| fmt.visit_args(loc, args))?; - write!(self.buf(), ")")?; - self.write_semicolon()?; - - Ok(()) - } - - #[instrument(name = "break", skip_all)] - fn visit_break(&mut self, loc: Loc, semicolon: bool) -> Result<()> { - if semicolon { - return_source_if_disabled!(self, loc, ';'); - } else { - return_source_if_disabled!(self, loc); - } - write_chunk!(self, loc.start(), loc.end(), "break{}", if semicolon { ";" } else { "" }) - } - - #[instrument(name = "continue", skip_all)] - fn visit_continue(&mut self, loc: Loc, semicolon: bool) -> Result<()> { - if semicolon { - return_source_if_disabled!(self, loc, ';'); - } else { - return_source_if_disabled!(self, loc); - } - write_chunk!(self, loc.start(), loc.end(), "continue{}", if semicolon { ";" } else { "" }) - } - - #[instrument(name = "try", skip_all)] - fn visit_try( - &mut self, - loc: Loc, - expr: &mut Expression, - returns: &mut Option<(Vec<(Loc, Option)>, Box)>, - clauses: &mut Vec, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - - let try_next_byte = clauses.first().map(|c| match c { - CatchClause::Simple(loc, ..) => loc.start(), - CatchClause::Named(loc, ..) => loc.start(), - }); - let try_chunk = self.chunked(loc.start(), try_next_byte, |fmt| { - write_chunk!(fmt, loc.start(), expr.loc().start(), "try")?; - expr.visit(fmt)?; - if let Some((params, stmt)) = returns { - let mut params = - params.iter_mut().filter(|(_, param)| param.is_some()).collect::>(); - let byte_offset = params.first().map_or(stmt.loc().start(), |p| p.0.start()); - fmt.surrounded( - SurroundingChunk::new("returns (", Some(byte_offset), None), - SurroundingChunk::new(")", None, params.last().map(|p| p.0.end())), - |fmt, _| { - let chunks = fmt.items_to_chunks( - Some(stmt.loc().start()), - params.iter_mut().map(|(loc, ref mut ident)| (*loc, ident)), - )?; - let multiline = fmt.are_chunks_separated_multiline("{})", &chunks, ",")?; - fmt.write_chunks_separated(&chunks, ",", multiline)?; - Ok(()) - }, - )?; - stmt.visit(fmt)?; - } - Ok(()) - })?; - - let mut chunks = vec![try_chunk]; - for clause in clauses { - let (loc, ident, mut param, stmt) = match clause { - CatchClause::Simple(loc, param, stmt) => (loc, None, param.as_mut(), stmt), - CatchClause::Named(loc, ident, param, stmt) => { - (loc, Some(ident), Some(param), stmt) - } - }; - - let chunk = self.chunked(loc.start(), Some(stmt.loc().start()), |fmt| { - write_chunk!(fmt, "catch")?; - if let Some(ident) = ident.as_ref() { - fmt.write_postfix_comments_before( - param.as_ref().map(|p| p.loc.start()).unwrap_or_else(|| ident.loc.end()), - )?; - write_chunk!(fmt, ident.loc.start(), "{}", ident.name)?; - } - if let Some(param) = param.as_mut() { - write_chunk_spaced!(fmt, param.loc.start(), Some(ident.is_none()), "(")?; - fmt.surrounded( - SurroundingChunk::new("", Some(param.loc.start()), None), - SurroundingChunk::new(")", None, Some(stmt.loc().start())), - |fmt, _| param.visit(fmt), - )?; - } - - stmt.visit(fmt)?; - Ok(()) - })?; - - chunks.push(chunk); - } - - let multiline = self.are_chunks_separated_multiline("{}", &chunks, "")?; - if !multiline { - self.write_chunks_separated(&chunks, "", false)?; - return Ok(()) - } - - let mut chunks = chunks.iter_mut().peekable(); - let mut prev_multiline = false; - - // write try chunk first - if let Some(chunk) = chunks.next() { - let chunk_str = self.simulate_to_string(|fmt| fmt.write_chunk(chunk))?; - write!(self.buf(), "{chunk_str}")?; - prev_multiline = chunk_str.contains('\n'); - } - - while let Some(chunk) = chunks.next() { - let chunk_str = self.simulate_to_string(|fmt| fmt.write_chunk(chunk))?; - let multiline = chunk_str.contains('\n'); - self.indented_if(!multiline, 1, |fmt| { - chunk.needs_space = Some(false); - let on_same_line = prev_multiline && (multiline || chunks.peek().is_none()); - let prefix = if fmt.is_beginning_of_line() { - "" - } else if on_same_line { - " " - } else { - "\n" - }; - let chunk_str = format!("{prefix}{chunk_str}"); - write!(fmt.buf(), "{chunk_str}")?; - Ok(()) - })?; - prev_multiline = multiline; - } - Ok(()) - } - - #[instrument(name = "if", skip_all)] - fn visit_if( - &mut self, - loc: Loc, - cond: &mut Expression, - if_branch: &mut Box, - else_branch: &mut Option>, - is_first_stmt: bool, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - - if !is_first_stmt { - self.write_if_stmt(loc, cond, if_branch, else_branch)?; - return Ok(()) - } - - self.context.if_stmt_single_line = Some(true); - let mut stmt_fits_on_single = false; - let tx = self.transact(|fmt| { - stmt_fits_on_single = match fmt.write_if_stmt(loc, cond, if_branch, else_branch) { - Ok(()) => true, - Err(FormatterError::Fmt(_)) => false, - Err(err) => bail!(err), - }; - Ok(()) - })?; - - if stmt_fits_on_single { - tx.commit()?; - } else { - self.context.if_stmt_single_line = Some(false); - self.write_if_stmt(loc, cond, if_branch, else_branch)?; - } - self.context.if_stmt_single_line = None; - - Ok(()) - } - - #[instrument(name = "do_while", skip_all)] - fn visit_do_while( - &mut self, - loc: Loc, - body: &mut Statement, - cond: &mut Expression, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc, ';'); - write_chunk!(self, loc.start(), "do ")?; - self.visit_stmt_as_block(body, false)?; - visit_source_if_disabled_else!(self, loc.with_start(body.loc().end()), { - self.surrounded( - SurroundingChunk::new("while (", Some(cond.loc().start()), None), - SurroundingChunk::new(");", None, Some(loc.end())), - |fmt, _| cond.visit(fmt), - )?; - }); - Ok(()) - } - - #[instrument(name = "while", skip_all)] - fn visit_while( - &mut self, - loc: Loc, - cond: &mut Expression, - body: &mut Statement, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - self.surrounded( - SurroundingChunk::new("while (", Some(loc.start()), None), - SurroundingChunk::new(")", None, Some(cond.loc().end())), - |fmt, _| { - cond.visit(fmt)?; - fmt.write_postfix_comments_before(body.loc().start()) - }, - )?; - - let cond_close_paren_loc = - self.find_next_in_src(cond.loc().end(), ')').unwrap_or_else(|| cond.loc().end()); - let attempt_single_line = self.should_attempt_block_single_line(body, cond_close_paren_loc); - self.visit_stmt_as_block(body, attempt_single_line)?; - Ok(()) - } - - #[instrument(name = "for", skip_all)] - fn visit_for( - &mut self, - loc: Loc, - init: &mut Option>, - cond: &mut Option>, - update: &mut Option>, - body: &mut Option>, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - - let next_byte_end = update.as_ref().map(|u| u.loc().end()); - self.surrounded( - SurroundingChunk::new("for (", Some(loc.start()), None), - SurroundingChunk::new(")", None, next_byte_end), - |fmt, _| { - let mut write_for_loop_header = |fmt: &mut Self, multiline: bool| -> Result<()> { - match init { - Some(stmt) => stmt.visit(fmt), - None => fmt.write_semicolon(), - }?; - if multiline { - fmt.write_whitespace_separator(true)?; - } - - cond.visit(fmt)?; - fmt.write_semicolon()?; - if multiline { - fmt.write_whitespace_separator(true)?; - } - - match update { - Some(expr) => expr.visit(fmt), - None => Ok(()), - } - }; - let multiline = !fmt.try_on_single_line(|fmt| write_for_loop_header(fmt, false))?; - if multiline { - write_for_loop_header(fmt, true)?; - } - Ok(()) - }, - )?; - match body { - Some(body) => { - self.visit_stmt_as_block(body, false)?; - } - None => { - self.write_empty_brackets()?; - } - }; - Ok(()) - } - - #[instrument(name = "function", skip_all)] - fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<()> { - if func.body.is_some() { - return_source_if_disabled!(self, func.loc()); - } else { - return_source_if_disabled!(self, func.loc(), ';'); - } - - self.with_function_context(func.clone(), |fmt| { - fmt.write_postfix_comments_before(func.loc.start())?; - fmt.write_prefix_comments_before(func.loc.start())?; - - let body_loc = func.body.as_ref().map(CodeLocation::loc); - let mut attrs_multiline = false; - let fits_on_single = fmt.try_on_single_line(|fmt| { - fmt.write_function_header(func, body_loc, false)?; - Ok(()) - })?; - if !fits_on_single { - attrs_multiline = fmt.write_function_header(func, body_loc, true)?; - } - - // write function body - match &mut func.body { - Some(body) => { - let body_loc = body.loc(); - // Handle case where block / statements starts on disabled line. - if fmt.inline_config.is_disabled(body_loc.with_end(body_loc.start())) { - match body { - Statement::Block { statements, .. } if !statements.is_empty() => { - fmt.write_whitespace_separator(false)?; - fmt.visit_block(body_loc, statements, false, false)?; - return Ok(()) - } - _ => { - // Attrs should be written on same line if first line is disabled - // and there's no statement. - attrs_multiline = false - } - } - } - - let byte_offset = body_loc.start(); - let body = fmt.visit_to_chunk(byte_offset, Some(body_loc.end()), body)?; - fmt.write_whitespace_separator( - attrs_multiline && !(func.attributes.is_empty() && func.returns.is_empty()), - )?; - fmt.write_chunk(&body)?; - } - None => fmt.write_semicolon()?, - } - Ok(()) - })?; - - Ok(()) - } - - #[instrument(name = "function_attribute", skip_all)] - fn visit_function_attribute(&mut self, attribute: &mut FunctionAttribute) -> Result<()> { - return_source_if_disabled!(self, attribute.loc()); - - match attribute { - FunctionAttribute::Mutability(mutability) => { - write_chunk!(self, mutability.loc().end(), "{mutability}")? - } - FunctionAttribute::Visibility(visibility) => { - // Visibility will always have a location in a Function attribute - write_chunk!(self, visibility.loc_opt().unwrap().end(), "{visibility}")? - } - FunctionAttribute::Virtual(loc) => write_chunk!(self, loc.end(), "virtual")?, - FunctionAttribute::Immutable(loc) => write_chunk!(self, loc.end(), "immutable")?, - FunctionAttribute::Override(loc, args) => { - write_chunk!(self, loc.start(), "override")?; - if !args.is_empty() && self.config.override_spacing { - self.write_whitespace_separator(false)?; - } - self.visit_list("", args, None, Some(loc.end()), false)? - } - FunctionAttribute::BaseOrModifier(loc, base) => { - // here we need to find out if this attribute belongs to the constructor because the - // modifier need to include the trailing parenthesis - // This is very ambiguous because the modifier can either by an inherited contract - // or a modifier here: e.g.: This is valid constructor: - // `constructor() public Ownable() OnlyOwner {}` - let is_constructor = self.context.is_constructor_function(); - // we can't make any decisions here regarding trailing `()` because we'd need to - // find out if the `base` is a solidity modifier or an - // interface/contract therefore we we its raw content. - - // we can however check if the contract `is` the `base`, this however also does - // not cover all cases - let is_contract_base = self.context.contract.as_ref().is_some_and(|contract| { - contract.base.iter().any(|contract_base| { - contract_base - .name - .identifiers - .iter() - .zip(&base.name.identifiers) - .all(|(l, r)| l.name == r.name) - }) - }); - - if is_contract_base { - base.visit(self)?; - } else if is_constructor { - // This is ambiguous because the modifier can either by an inherited - // contract modifiers with empty parenthesis are - // valid, but not required so we make the assumption - // here that modifiers are lowercase - let mut base_or_modifier = - self.visit_to_chunk(loc.start(), Some(loc.end()), base)?; - let is_lowercase = - base_or_modifier.content.chars().next().is_some_and(|c| c.is_lowercase()); - if is_lowercase && base_or_modifier.content.ends_with("()") { - base_or_modifier.content.truncate(base_or_modifier.content.len() - 2); - } - - self.write_chunk(&base_or_modifier)?; - } else { - let mut base_or_modifier = - self.visit_to_chunk(loc.start(), Some(loc.end()), base)?; - if base_or_modifier.content.ends_with("()") { - base_or_modifier.content.truncate(base_or_modifier.content.len() - 2); - } - self.write_chunk(&base_or_modifier)?; - } - } - FunctionAttribute::Error(loc) => self.visit_parser_error(*loc)?, - }; - - Ok(()) - } - - #[instrument(name = "var_attribute", skip_all)] - fn visit_var_attribute(&mut self, attribute: &mut VariableAttribute) -> Result<()> { - return_source_if_disabled!(self, attribute.loc()); - - let token = match attribute { - VariableAttribute::Visibility(visibility) => Some(visibility.to_string()), - VariableAttribute::Constant(_) => Some("constant".to_string()), - VariableAttribute::Immutable(_) => Some("immutable".to_string()), - VariableAttribute::Override(loc, idents) => { - write_chunk!(self, loc.start(), "override")?; - if !idents.is_empty() && self.config.override_spacing { - self.write_whitespace_separator(false)?; - } - self.visit_list("", idents, Some(loc.start()), Some(loc.end()), false)?; - None - } - }; - if let Some(token) = token { - let loc = attribute.loc(); - write_chunk!(self, loc.start(), loc.end(), "{}", token)?; - } - Ok(()) - } - - #[instrument(name = "base", skip_all)] - fn visit_base(&mut self, base: &mut Base) -> Result<()> { - return_source_if_disabled!(self, base.loc); - - let name_loc = &base.name.loc; - let mut name = self.chunked(name_loc.start(), Some(name_loc.end()), |fmt| { - fmt.visit_ident_path(&mut base.name)?; - Ok(()) - })?; - - if base.args.is_none() || base.args.as_ref().unwrap().is_empty() { - // This is ambiguous because the modifier can either by an inherited contract or a - // modifier - if self.context.function.is_some() { - name.content.push_str("()"); - } - self.write_chunk(&name)?; - return Ok(()) - } - - let args = base.args.as_mut().unwrap(); - let args_start = CodeLocation::loc(args.first().unwrap()).start(); - - name.content.push('('); - let formatted_name = self.chunk_to_string(&name)?; - - let multiline = !self.will_it_fit(&formatted_name); - - self.surrounded( - SurroundingChunk::new(&formatted_name, Some(args_start), None), - SurroundingChunk::new(")", None, Some(base.loc.end())), - |fmt, multiline_hint| { - let args = fmt.items_to_chunks( - Some(base.loc.end()), - args.iter_mut().map(|arg| (arg.loc(), arg)), - )?; - let multiline = multiline || - multiline_hint || - fmt.are_chunks_separated_multiline("{}", &args, ",")?; - fmt.write_chunks_separated(&args, ",", multiline)?; - Ok(()) - }, - )?; - - Ok(()) - } - - #[instrument(name = "parameter", skip_all)] - fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<()> { - return_source_if_disabled!(self, parameter.loc); - self.grouped(|fmt| { - parameter.ty.visit(fmt)?; - if let Some(storage) = ¶meter.storage { - write_chunk!(fmt, storage.loc().end(), "{storage}")?; - } - if let Some(name) = ¶meter.name { - write_chunk!(fmt, parameter.loc.end(), "{}", name.name)?; - } - Ok(()) - })?; - Ok(()) - } - - #[instrument(name = "struct", skip_all)] - fn visit_struct(&mut self, structure: &mut StructDefinition) -> Result<()> { - return_source_if_disabled!(self, structure.loc); - self.grouped(|fmt| { - let struct_name = structure.name.safe_unwrap_mut(); - write_chunk!(fmt, struct_name.loc.start(), "struct")?; - struct_name.visit(fmt)?; - if structure.fields.is_empty() { - return fmt.write_empty_brackets() - } - - write!(fmt.buf(), " {{")?; - fmt.surrounded( - SurroundingChunk::new("", Some(struct_name.loc.end()), None), - SurroundingChunk::new("}", None, Some(structure.loc.end())), - |fmt, _multiline| { - let chunks = fmt.items_to_chunks( - Some(structure.loc.end()), - structure.fields.iter_mut().map(|ident| (ident.loc, ident)), - )?; - for mut chunk in chunks { - chunk.content.push(';'); - fmt.write_chunk(&chunk)?; - fmt.write_whitespace_separator(true)?; - } - Ok(()) - }, - ) - })?; - - Ok(()) - } - - #[instrument(name = "event", skip_all)] - fn visit_event(&mut self, event: &mut EventDefinition) -> Result<()> { - return_source_if_disabled!(self, event.loc, ';'); - - let event_name = event.name.safe_unwrap_mut(); - let mut name = - self.visit_to_chunk(event_name.loc.start(), Some(event.loc.end()), event_name)?; - name.content = format!("event {}(", name.content); - - let last_chunk = if event.anonymous { ") anonymous;" } else { ");" }; - if event.fields.is_empty() { - name.content.push_str(last_chunk); - self.write_chunk(&name)?; - } else { - let byte_offset = event.fields.first().unwrap().loc.start(); - let first_chunk = self.chunk_to_string(&name)?; - self.surrounded( - SurroundingChunk::new(first_chunk, Some(byte_offset), None), - SurroundingChunk::new(last_chunk, None, Some(event.loc.end())), - |fmt, multiline| { - let params = fmt - .items_to_chunks(None, event.fields.iter_mut().map(|arg| (arg.loc, arg)))?; - - let multiline = - multiline && fmt.are_chunks_separated_multiline("{}", ¶ms, ",")?; - fmt.write_chunks_separated(¶ms, ",", multiline) - }, - )?; - } - - Ok(()) - } - - #[instrument(name = "event_parameter", skip_all)] - fn visit_event_parameter(&mut self, param: &mut EventParameter) -> Result<()> { - return_source_if_disabled!(self, param.loc); - - self.grouped(|fmt| { - param.ty.visit(fmt)?; - if param.indexed { - write_chunk!(fmt, param.loc.start(), "indexed")?; - } - if let Some(name) = ¶m.name { - write_chunk!(fmt, name.loc.end(), "{}", name.name)?; - } - Ok(()) - })?; - Ok(()) - } - - #[instrument(name = "error", skip_all)] - fn visit_error(&mut self, error: &mut ErrorDefinition) -> Result<()> { - return_source_if_disabled!(self, error.loc, ';'); - - let error_name = error.name.safe_unwrap_mut(); - let mut name = self.visit_to_chunk(error_name.loc.start(), None, error_name)?; - name.content = format!("error {}", name.content); - - let formatted_name = self.chunk_to_string(&name)?; - write!(self.buf(), "{formatted_name}")?; - let start_offset = error.fields.first().map(|f| f.loc.start()); - self.visit_list("", &mut error.fields, start_offset, Some(error.loc.end()), true)?; - self.write_semicolon()?; - - Ok(()) - } - - #[instrument(name = "error_parameter", skip_all)] - fn visit_error_parameter(&mut self, param: &mut ErrorParameter) -> Result<()> { - return_source_if_disabled!(self, param.loc); - self.grouped(|fmt| { - param.ty.visit(fmt)?; - if let Some(name) = ¶m.name { - write_chunk!(fmt, name.loc.end(), "{}", name.name)?; - } - Ok(()) - })?; - Ok(()) - } - - #[instrument(name = "type_definition", skip_all)] - fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<()> { - return_source_if_disabled!(self, def.loc, ';'); - self.grouped(|fmt| { - write_chunk!(fmt, def.loc.start(), def.name.loc.start(), "type")?; - def.name.visit(fmt)?; - write_chunk!(fmt, def.name.loc.end(), CodeLocation::loc(&def.ty).start(), "is")?; - def.ty.visit(fmt)?; - fmt.write_semicolon()?; - Ok(()) - })?; - Ok(()) - } - - #[instrument(name = "stray_semicolon", skip_all)] - fn visit_stray_semicolon(&mut self) -> Result<()> { - self.write_semicolon() - } - - #[instrument(name = "opening_paren", skip_all)] - fn visit_opening_paren(&mut self) -> Result<()> { - write_chunk!(self, "(")?; - Ok(()) - } - - #[instrument(name = "closing_paren", skip_all)] - fn visit_closing_paren(&mut self) -> Result<()> { - write_chunk!(self, ")")?; - Ok(()) - } - - #[instrument(name = "newline", skip_all)] - fn visit_newline(&mut self) -> Result<()> { - writeln_chunk!(self)?; - Ok(()) - } - - #[instrument(name = "using", skip_all)] - fn visit_using(&mut self, using: &mut Using) -> Result<()> { - return_source_if_disabled!(self, using.loc, ';'); - - write_chunk!(self, using.loc.start(), "using")?; - - let ty_start = using.ty.as_mut().map(|ty| CodeLocation::loc(&ty).start()); - let global_start = using.global.as_mut().map(|global| global.loc.start()); - let loc_end = using.loc.end(); - - let (is_library, mut list_chunks) = match &mut using.list { - UsingList::Library(library) => { - (true, vec![self.visit_to_chunk(library.loc.start(), None, library)?]) - } - UsingList::Functions(funcs) => { - let mut funcs = funcs.iter_mut().peekable(); - let mut chunks = Vec::new(); - while let Some(func) = funcs.next() { - let next_byte_end = funcs.peek().map(|func| func.loc.start()); - chunks.push(self.chunked(func.loc.start(), next_byte_end, |fmt| { - fmt.visit_ident_path(&mut func.path)?; - if let Some(op) = func.oper { - write!(fmt.buf(), " as {op}")?; - } - Ok(()) - })?); - } - (false, chunks) - } - UsingList::Error => return self.visit_parser_error(using.loc), - }; - - let for_chunk = self.chunk_at( - using.loc.start(), - Some(ty_start.or(global_start).unwrap_or(loc_end)), - None, - "for", - ); - let ty_chunk = if let Some(ty) = &mut using.ty { - self.visit_to_chunk(ty.loc().start(), Some(global_start.unwrap_or(loc_end)), ty)? - } else { - self.chunk_at(using.loc.start(), Some(global_start.unwrap_or(loc_end)), None, "*") - }; - let global_chunk = using - .global - .as_mut() - .map(|global| self.visit_to_chunk(global.loc.start(), Some(using.loc.end()), global)) - .transpose()?; - - let write_for_def = |fmt: &mut Self| { - fmt.grouped(|fmt| { - fmt.write_chunk(&for_chunk)?; - fmt.write_chunk(&ty_chunk)?; - if let Some(global_chunk) = global_chunk.as_ref() { - fmt.write_chunk(global_chunk)?; - } - Ok(()) - })?; - Ok(()) - }; - - let simulated_for_def = self.simulate_to_string(write_for_def)?; - - if is_library { - let chunk = list_chunks.pop().unwrap(); - if self.will_chunk_fit(&format!("{{}} {simulated_for_def};"), &chunk)? { - self.write_chunk(&chunk)?; - write_for_def(self)?; - } else { - self.write_whitespace_separator(true)?; - self.grouped(|fmt| { - fmt.write_chunk(&chunk)?; - Ok(()) - })?; - self.write_whitespace_separator(true)?; - write_for_def(self)?; - } - } else { - self.surrounded( - SurroundingChunk::new("{", Some(using.loc.start()), None), - SurroundingChunk::new( - "}", - None, - Some(ty_start.or(global_start).unwrap_or(loc_end)), - ), - |fmt, _multiline| { - let multiline = fmt.are_chunks_separated_multiline( - &format!("{{ {{}} }} {simulated_for_def};"), - &list_chunks, - ",", - )?; - fmt.write_chunks_separated(&list_chunks, ",", multiline)?; - Ok(()) - }, - )?; - write_for_def(self)?; - } - - self.write_semicolon()?; - - Ok(()) - } - - #[instrument(name = "yul_block", skip_all)] - fn visit_yul_block( - &mut self, - loc: Loc, - statements: &mut Vec, - attempt_single_line: bool, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - self.visit_block(loc, statements, attempt_single_line, false)?; - Ok(()) - } - - #[instrument(name = "yul_expr", skip_all)] - fn visit_yul_expr(&mut self, expr: &mut YulExpression) -> Result<(), Self::Error> { - return_source_if_disabled!(self, expr.loc()); - - match expr { - YulExpression::BoolLiteral(loc, val, ident) => { - let val = if *val { "true" } else { "false" }; - self.visit_yul_string_with_ident(*loc, val, ident) - } - YulExpression::FunctionCall(expr) => self.visit_yul_function_call(expr), - YulExpression::HexNumberLiteral(loc, val, ident) => { - self.visit_yul_string_with_ident(*loc, val, ident) - } - YulExpression::HexStringLiteral(val, ident) => self.visit_yul_string_with_ident( - val.loc, - &self.quote_str(val.loc, Some("hex"), &val.hex), - ident, - ), - YulExpression::NumberLiteral(loc, val, expr, ident) => { - let val = if expr.is_empty() { val.to_owned() } else { format!("{val}e{expr}") }; - self.visit_yul_string_with_ident(*loc, &val, ident) - } - YulExpression::StringLiteral(val, ident) => self.visit_yul_string_with_ident( - val.loc, - &self.quote_str(val.loc, None, &val.string), - ident, - ), - YulExpression::SuffixAccess(_, expr, ident) => { - self.visit_member_access(expr, ident, |fmt, expr| match expr.as_mut() { - YulExpression::SuffixAccess(_, inner_expr, inner_ident) => { - Ok(Some((inner_expr, inner_ident))) - } - expr => { - expr.visit(fmt)?; - Ok(None) - } - }) - } - YulExpression::Variable(ident) => { - write_chunk!(self, ident.loc.start(), ident.loc.end(), "{}", ident.name) - } - } - } - - #[instrument(name = "yul_assignment", skip_all)] - fn visit_yul_assignment( - &mut self, - loc: Loc, - exprs: &mut Vec, - expr: &mut Option<&mut YulExpression>, - ) -> Result<(), Self::Error> - where - T: Visitable + CodeLocation, - { - return_source_if_disabled!(self, loc); - - self.grouped(|fmt| { - let chunks = - fmt.items_to_chunks(None, exprs.iter_mut().map(|expr| (expr.loc(), expr)))?; - - let multiline = fmt.are_chunks_separated_multiline("{} := ", &chunks, ",")?; - fmt.write_chunks_separated(&chunks, ",", multiline)?; - - if let Some(expr) = expr { - write_chunk!(fmt, expr.loc().start(), ":=")?; - let chunk = fmt.visit_to_chunk(expr.loc().start(), Some(loc.end()), expr)?; - if !fmt.will_chunk_fit("{}", &chunk)? { - fmt.write_whitespace_separator(true)?; - } - fmt.write_chunk(&chunk)?; - } - Ok(()) - })?; - Ok(()) - } - - #[instrument(name = "yul_for", skip_all)] - fn visit_yul_for(&mut self, stmt: &mut YulFor) -> Result<(), Self::Error> { - return_source_if_disabled!(self, stmt.loc); - write_chunk!(self, stmt.loc.start(), "for")?; - self.visit_yul_block(stmt.init_block.loc, &mut stmt.init_block.statements, true)?; - stmt.condition.visit(self)?; - self.visit_yul_block(stmt.post_block.loc, &mut stmt.post_block.statements, true)?; - self.visit_yul_block(stmt.execution_block.loc, &mut stmt.execution_block.statements, true)?; - Ok(()) - } - - #[instrument(name = "yul_function_call", skip_all)] - fn visit_yul_function_call(&mut self, stmt: &mut YulFunctionCall) -> Result<(), Self::Error> { - return_source_if_disabled!(self, stmt.loc); - write_chunk!(self, stmt.loc.start(), "{}", stmt.id.name)?; - self.visit_list("", &mut stmt.arguments, None, Some(stmt.loc.end()), true) - } - - #[instrument(name = "yul_fun_def", skip_all)] - fn visit_yul_fun_def(&mut self, stmt: &mut YulFunctionDefinition) -> Result<(), Self::Error> { - return_source_if_disabled!(self, stmt.loc); - - write_chunk!(self, stmt.loc.start(), "function {}", stmt.id.name)?; - - self.visit_list("", &mut stmt.params, None, None, true)?; - - if !stmt.returns.is_empty() { - self.grouped(|fmt| { - write_chunk!(fmt, "->")?; - - let chunks = fmt.items_to_chunks( - Some(stmt.body.loc.start()), - stmt.returns.iter_mut().map(|param| (param.loc, param)), - )?; - let multiline = fmt.are_chunks_separated_multiline("{}", &chunks, ",")?; - fmt.write_chunks_separated(&chunks, ",", multiline)?; - if multiline { - fmt.write_whitespace_separator(true)?; - } - Ok(()) - })?; - } - - stmt.body.visit(self)?; - - Ok(()) - } - - #[instrument(name = "yul_if", skip_all)] - fn visit_yul_if( - &mut self, - loc: Loc, - expr: &mut YulExpression, - block: &mut YulBlock, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - write_chunk!(self, loc.start(), "if")?; - expr.visit(self)?; - self.visit_yul_block(block.loc, &mut block.statements, true) - } - - #[instrument(name = "yul_leave", skip_all)] - fn visit_yul_leave(&mut self, loc: Loc) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - write_chunk!(self, loc.start(), loc.end(), "leave") - } - - #[instrument(name = "yul_switch", skip_all)] - fn visit_yul_switch(&mut self, stmt: &mut YulSwitch) -> Result<(), Self::Error> { - return_source_if_disabled!(self, stmt.loc); - - write_chunk!(self, stmt.loc.start(), "switch")?; - stmt.condition.visit(self)?; - writeln_chunk!(self)?; - let mut cases = stmt.cases.iter_mut().peekable(); - while let Some(YulSwitchOptions::Case(loc, expr, block)) = cases.next() { - write_chunk!(self, loc.start(), "case")?; - expr.visit(self)?; - self.visit_yul_block(block.loc, &mut block.statements, true)?; - let is_last = cases.peek().is_none(); - if !is_last || stmt.default.is_some() { - writeln_chunk!(self)?; - } - } - if let Some(YulSwitchOptions::Default(loc, ref mut block)) = stmt.default { - write_chunk!(self, loc.start(), "default")?; - self.visit_yul_block(block.loc, &mut block.statements, true)?; - } - Ok(()) - } - - #[instrument(name = "yul_var_declaration", skip_all)] - fn visit_yul_var_declaration( - &mut self, - loc: Loc, - idents: &mut Vec, - expr: &mut Option, - ) -> Result<(), Self::Error> { - return_source_if_disabled!(self, loc); - self.grouped(|fmt| { - write_chunk!(fmt, loc.start(), "let")?; - fmt.visit_yul_assignment(loc, idents, &mut expr.as_mut()) - })?; - Ok(()) - } - - #[instrument(name = "yul_typed_ident", skip_all)] - fn visit_yul_typed_ident(&mut self, ident: &mut YulTypedIdentifier) -> Result<(), Self::Error> { - return_source_if_disabled!(self, ident.loc); - self.visit_yul_string_with_ident(ident.loc, &ident.id.name, &mut ident.ty) - } - - #[instrument(name = "parser_error", skip_all)] - fn visit_parser_error(&mut self, loc: Loc) -> Result<()> { - Err(FormatterError::InvalidParsedItem(loc)) - } -} - -/// An action which may be committed to a Formatter -struct Transaction<'f, 'a, W> { - fmt: &'f mut Formatter<'a, W>, - buffer: String, - comments: Comments, -} - -impl<'a, W> std::ops::Deref for Transaction<'_, 'a, W> { - type Target = Formatter<'a, W>; - fn deref(&self) -> &Self::Target { - self.fmt - } -} - -impl std::ops::DerefMut for Transaction<'_, '_, W> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.fmt - } -} - -impl<'f, 'a, W: Write> Transaction<'f, 'a, W> { - /// Create a new transaction from a callback - fn new( - fmt: &'f mut Formatter<'a, W>, - fun: impl FnMut(&mut Formatter<'a, W>) -> Result<()>, - ) -> Result { - let mut comments = fmt.comments.clone(); - let buffer = fmt.with_temp_buf(fun)?.w; - comments = std::mem::replace(&mut fmt.comments, comments); - Ok(Self { fmt, buffer, comments }) - } - - /// Commit the transaction to the Formatter - fn commit(self) -> Result { - self.fmt.comments = self.comments; - write_chunk!(self.fmt, "{}", self.buffer)?; - Ok(self.buffer) - } -} diff --git a/crates/fmt-2/src/helpers.rs b/crates/fmt-2/src/helpers.rs deleted file mode 100644 index 1d036ba6b66d0..0000000000000 --- a/crates/fmt-2/src/helpers.rs +++ /dev/null @@ -1,135 +0,0 @@ -use crate::{ - inline_config::{InlineConfig, InvalidInlineConfigItem}, - Comments, Formatter, FormatterConfig, FormatterError, Visitable, -}; -use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; -use itertools::Itertools; -use solang_parser::{diagnostics::Diagnostic, pt::*}; -use std::{fmt::Write, path::Path}; - -/// Result of parsing the source code -#[derive(Debug)] -pub struct Parsed<'a> { - /// The original source code. - pub src: &'a str, - /// The parse tree. - pub pt: SourceUnit, - /// Parsed comments. - pub comments: Comments, - /// Parsed inline config. - pub inline_config: InlineConfig, - /// Invalid inline config items parsed. - pub invalid_inline_config_items: Vec<(Loc, InvalidInlineConfigItem)>, -} - -/// Parse source code. -pub fn parse(src: &str) -> Result, FormatterError> { - parse_raw(src).map_err(|diag| FormatterError::Parse(src.to_string(), None, diag)) -} - -/// Parse source code with a path for diagnostics. -pub fn parse2<'s>(src: &'s str, path: Option<&Path>) -> Result, FormatterError> { - parse_raw(src) - .map_err(|diag| FormatterError::Parse(src.to_string(), path.map(ToOwned::to_owned), diag)) -} - -/// Parse source code, returning a list of diagnostics on failure. -pub fn parse_raw(src: &str) -> Result, Vec> { - let (pt, comments) = solang_parser::parse(src, 0)?; - let comments = Comments::new(comments, src); - let (inline_config_items, invalid_inline_config_items): (Vec<_>, Vec<_>) = - comments.parse_inline_config_items().partition_result(); - let inline_config = InlineConfig::new(inline_config_items, src); - Ok(Parsed { src, pt, comments, inline_config, invalid_inline_config_items }) -} - -/// Format parsed code -pub fn format_to( - writer: W, - mut parsed: Parsed<'_>, - config: FormatterConfig, -) -> Result<(), FormatterError> { - trace!(?parsed, ?config, "Formatting"); - let mut formatter = - Formatter::new(writer, parsed.src, parsed.comments, parsed.inline_config, config); - parsed.pt.visit(&mut formatter) -} - -/// Parse and format a string with default settings -pub fn format(src: &str) -> Result { - let parsed = parse(src)?; - - let mut output = String::new(); - format_to(&mut output, parsed, FormatterConfig::default())?; - - Ok(output) -} - -/// Converts the start offset of a `Loc` to `(line, col)` -pub fn offset_to_line_column(content: &str, start: usize) -> (usize, usize) { - debug_assert!(content.len() > start); - - // first line is `1` - let mut line_counter = 1; - for (offset, c) in content.chars().enumerate() { - if c == '\n' { - line_counter += 1; - } - if offset > start { - return (line_counter, offset - start) - } - } - - unreachable!("content.len() > start") -} - -/// Formats parser diagnostics -pub fn format_diagnostics_report( - content: &str, - path: Option<&Path>, - diagnostics: &[Diagnostic], -) -> String { - if diagnostics.is_empty() { - return String::new(); - } - - let filename = - path.map(|p| p.file_name().unwrap().to_string_lossy().to_string()).unwrap_or_default(); - let mut s = Vec::new(); - for diag in diagnostics { - let span = (filename.as_str(), diag.loc.start()..diag.loc.end()); - let mut report = Report::build(ReportKind::Error, span.clone()) - .with_message(format!("{:?}", diag.ty)) - .with_label( - Label::new(span) - .with_color(Color::Red) - .with_message(diag.message.as_str().fg(Color::Red)), - ); - - for note in &diag.notes { - report = report.with_note(¬e.message); - } - - report.finish().write((filename.as_str(), Source::from(content)), &mut s).unwrap(); - } - String::from_utf8(s).unwrap() -} - -pub fn import_path_string(path: &ImportPath) -> String { - match path { - ImportPath::Filename(s) => s.string.clone(), - ImportPath::Path(p) => p.to_string(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - // - #[test] - fn test_interface_format() { - let s = "interface I {\n function increment() external;\n function number() external view returns (uint256);\n function setNumber(uint256 newNumber) external;\n}"; - let _formatted = format(s).unwrap(); - } -} diff --git a/crates/fmt-2/src/inline_config.rs b/crates/fmt-2/src/inline_config.rs index 1de383475a39a..7aa8acd2ead8d 100644 --- a/crates/fmt-2/src/inline_config.rs +++ b/crates/fmt-2/src/inline_config.rs @@ -1,6 +1,5 @@ -use crate::comments::{CommentState, CommentStringExt}; use itertools::Itertools; -use solang_parser::pt::Loc; +use solar_parse::{ast::Span, lexer::token::RawTokenKind}; use std::{fmt, str::FromStr}; /// An inline config item @@ -37,7 +36,7 @@ pub struct InvalidInlineConfigItem(String); impl fmt::Display for InvalidInlineConfigItem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("Invalid inline config item: {}", self.0)) + write!(f, "invalid inline config item: {}", self.0) } } @@ -52,8 +51,8 @@ struct DisabledRange { } impl DisabledRange { - fn includes(&self, loc: Loc) -> bool { - loc.start() >= self.start && (if self.loose { loc.start() } else { loc.end() } <= self.end) + fn includes(&self, range: std::ops::Range) -> bool { + range.start >= self.start && (if self.loose { range.start } else { range.end } <= self.end) } } @@ -72,36 +71,43 @@ pub struct InlineConfig { impl InlineConfig { /// Build a new inline config with an iterator of inline config items and their locations in a /// source file. - pub fn new(items: impl IntoIterator, src: &str) -> Self { + pub fn new(items: impl IntoIterator, src: &str) -> Self { let mut disabled_ranges = vec![]; let mut disabled_range_start = None; let mut disabled_depth = 0usize; - for (loc, item) in items.into_iter().sorted_by_key(|(loc, _)| loc.start()) { + for (sp, item) in items.into_iter().sorted_by_key(|(loc, _)| loc.lo().to_usize()) { match item { InlineConfigItem::DisableNextItem => { - let offset = loc.end(); - let mut char_indices = src[offset..] - .comment_state_char_indices() - .filter_map(|(state, idx, ch)| match state { - CommentState::None => Some((idx, ch)), - _ => None, + use RawTokenKind::*; + let offset = sp.hi().to_usize(); + let mut idx = offset; + let mut tokens = solar_parse::Cursor::new(&src[offset..]) + .map(|token| { + let start = idx; + idx += token.len as usize; + (start, token) }) - .skip_while(|(_, ch)| ch.is_whitespace()); - if let Some((mut start, _)) = char_indices.next() { + .filter(|(_, t)| { + !matches!(t.kind, LineComment { .. } | BlockComment { .. }) + }) + .skip_while(|(_, t)| matches!(t.kind, Whitespace)); + if let Some((mut start, _)) = tokens.next() { start += offset; - let end = char_indices - .find(|(_, ch)| !ch.is_whitespace()) - .map(|(idx, _)| offset + idx) + let end = tokens + .find(|(_, t)| !matches!(t.kind, Whitespace)) + .map(|(idx, _)| idx) .unwrap_or(src.len()); disabled_ranges.push(DisabledRange { start, end, loose: true }); } } InlineConfigItem::DisableLine => { - let mut prev_newline = - src[..loc.start()].char_indices().rev().skip_while(|(_, ch)| *ch != '\n'); + let mut prev_newline = src[..sp.lo().to_usize()] + .char_indices() + .rev() + .skip_while(|(_, ch)| *ch != '\n'); let start = prev_newline.next().map(|(idx, _)| idx).unwrap_or_default(); - let end_offset = loc.end(); + let end_offset = sp.hi().to_usize(); let mut next_newline = src[end_offset..].char_indices().skip_while(|(_, ch)| *ch != '\n'); let end = @@ -110,7 +116,7 @@ impl InlineConfig { disabled_ranges.push(DisabledRange { start, end, loose: false }); } InlineConfigItem::DisableNextLine => { - let offset = loc.end(); + let offset = sp.hi().to_usize(); let mut char_indices = src[offset..].char_indices().skip_while(|(_, ch)| *ch != '\n').skip(1); if let Some((mut start, _)) = char_indices.next() { @@ -124,7 +130,7 @@ impl InlineConfig { } InlineConfigItem::DisableStart => { if disabled_depth == 0 { - disabled_range_start = Some(loc.end()); + disabled_range_start = Some(sp.hi().to_usize()); } disabled_depth += 1; } @@ -134,7 +140,7 @@ impl InlineConfig { if let Some(start) = disabled_range_start.take() { disabled_ranges.push(DisabledRange { start, - end: loc.start(), + end: sp.lo().to_usize(), loose: false, }) } @@ -149,7 +155,7 @@ impl InlineConfig { } /// Check if the location is in a disabled range - pub fn is_disabled(&self, loc: Loc) -> bool { - self.disabled_ranges.iter().any(|range| range.includes(loc)) + pub fn is_disabled(&self, span: Span) -> bool { + self.disabled_ranges.iter().any(|range| range.includes(span.to_range())) } } diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 4b196f6d62abc..84b51485ba2b5 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -5,25 +5,87 @@ #[macro_use] extern crate tracing; -pub mod printer; +pub mod inline_config; +pub use inline_config::InlineConfig; + +mod comment; -mod buffer; -pub mod chunk; mod comments; -mod formatter; -mod helpers; -pub mod inline_config; -mod macros; -pub mod solang_ext; -mod string; -pub mod visit; +pub use comments::Comments; + +mod state; + +mod pp; + +use solar_parse::interface::Session; +use std::path::Path; pub use foundry_config::fmt::*; -pub use comments::Comments; -pub use formatter::{Formatter, FormatterError}; -pub use helpers::{ - format, format_diagnostics_report, format_to, offset_to_line_column, parse, parse2, Parsed, -}; -pub use inline_config::InlineConfig; -pub use visit::{Visitable, Visitor}; +type Result = std::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum FormatterError { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Parse(#[from] solar_parse::interface::diagnostics::EmittedDiagnostics), +} + +pub fn format_file(path: &Path, config: FormatterConfig) -> Result { + let source = std::fs::read_to_string(path)?; + format_source(&source, Some(path), config) +} + +pub fn format_source(source: &str, path: Option<&Path>, config: FormatterConfig) -> Result { + let sess = + solar_parse::interface::Session::builder().with_buffer_emitter(Default::default()).build(); + let res = sess.enter(|| -> solar_parse::interface::Result<_> { + let name = match path { + Some(path) => solar_parse::interface::source_map::FileName::Real(path.to_path_buf()), + None => solar_parse::interface::source_map::FileName::Custom("fmt".to_string()), + }; + let arena = solar_parse::ast::Arena::new(); + let file = sess + .source_map() + .new_source_file(name, source) + .map_err(|e| sess.dcx.err(e.to_string()).emit())?; + let mut parser = solar_parse::Parser::from_source_file(&sess, &arena, &file); + let ast = parser.parse_file().map_err(|e| e.emit())?; + let comments = Comments::new(sess.source_map(), &file); + let inline_config = parse_inline_config(&sess, &comments, source); + Ok(format_source_unit(&ast, config, inline_config, comments)) + }); + sess.emitted_errors().unwrap()?; + Ok(res.unwrap()) +} + +fn format_source_unit( + source_unit: &solar_parse::ast::SourceUnit<'_>, + config: FormatterConfig, + inline_config: InlineConfig, + comments: Comments<'_>, +) -> String { + let mut state = state::State::new(config, inline_config, Some(comments)); + // state.source_unit(source_unit); + // state.eof() + // TODO(dani) + let _ = source_unit; + let _ = &mut state; + todo!() +} + +fn parse_inline_config(sess: &Session, comments: &Comments<'_>, src: &str) -> InlineConfig { + let items = comments.iter().filter_map(|comment| { + let item = comment.lines.first()?.trim_start().strip_prefix("forgefmt:")?.trim(); + let span = comment.span; + match item.parse::() { + Ok(item) => Some((span, item)), + Err(e) => { + let _ = sess.dcx.err(e.to_string()).span(span).emit(); + None + } + } + }); + InlineConfig::new(items, src) +} diff --git a/crates/fmt-2/src/macros.rs b/crates/fmt-2/src/macros.rs deleted file mode 100644 index c5c9d31a7d0e0..0000000000000 --- a/crates/fmt-2/src/macros.rs +++ /dev/null @@ -1,125 +0,0 @@ -macro_rules! write_chunk { - ($self:expr, $format_str:literal) => {{ - write_chunk!($self, $format_str,) - }}; - ($self:expr, $format_str:literal, $($arg:tt)*) => {{ - $self.write_chunk(&format!($format_str, $($arg)*).into()) - }}; - ($self:expr, $loc:expr) => {{ - write_chunk!($self, $loc, "") - }}; - ($self:expr, $loc:expr, $format_str:literal) => {{ - write_chunk!($self, $loc, $format_str,) - }}; - ($self:expr, $loc:expr, $format_str:literal, $($arg:tt)*) => {{ - let chunk = $self.chunk_at($loc, None, None, format_args!($format_str, $($arg)*),); - $self.write_chunk(&chunk) - }}; - ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal) => {{ - write_chunk!($self, $loc, $end_loc, $format_str,) - }}; - ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal, $($arg:tt)*) => {{ - let chunk = $self.chunk_at($loc, Some($end_loc), None, format_args!($format_str, $($arg)*),); - $self.write_chunk(&chunk) - }}; - ($self:expr, $loc:expr, $end_loc:expr, $needs_space:expr, $format_str:literal, $($arg:tt)*) => {{ - let chunk = $self.chunk_at($loc, Some($end_loc), Some($needs_space), format_args!($format_str, $($arg)*),); - $self.write_chunk(&chunk) - }}; -} - -macro_rules! writeln_chunk { - ($self:expr) => {{ - writeln_chunk!($self, "") - }}; - ($self:expr, $format_str:literal) => {{ - writeln_chunk!($self, $format_str,) - }}; - ($self:expr, $format_str:literal, $($arg:tt)*) => {{ - write_chunk!($self, "{}\n", format_args!($format_str, $($arg)*)) - }}; - ($self:expr, $loc:expr) => {{ - writeln_chunk!($self, $loc, "") - }}; - ($self:expr, $loc:expr, $format_str:literal) => {{ - writeln_chunk!($self, $loc, $format_str,) - }}; - ($self:expr, $loc:expr, $format_str:literal, $($arg:tt)*) => {{ - write_chunk!($self, $loc, "{}\n", format_args!($format_str, $($arg)*)) - }}; - ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal) => {{ - writeln_chunk!($self, $loc, $end_loc, $format_str,) - }}; - ($self:expr, $loc:expr, $end_loc:expr, $format_str:literal, $($arg:tt)*) => {{ - write_chunk!($self, $loc, $end_loc, "{}\n", format_args!($format_str, $($arg)*)) - }}; -} - -macro_rules! write_chunk_spaced { - ($self:expr, $loc:expr, $needs_space:expr, $format_str:literal) => {{ - write_chunk_spaced!($self, $loc, $needs_space, $format_str,) - }}; - ($self:expr, $loc:expr, $needs_space:expr, $format_str:literal, $($arg:tt)*) => {{ - let chunk = $self.chunk_at($loc, None, $needs_space, format_args!($format_str, $($arg)*),); - $self.write_chunk(&chunk) - }}; -} - -macro_rules! buf_fn { - ($vis:vis fn $name:ident(&self $(,)? $($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)?) => { - $vis fn $name(&self, $($arg_name : $arg_ty),*) $(-> $ret)? { - if self.temp_bufs.is_empty() { - self.buf.$name($($arg_name),*) - } else { - self.temp_bufs.last().unwrap().$name($($arg_name),*) - } - } - }; - ($vis:vis fn $name:ident(&mut self $(,)? $($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)?) => { - $vis fn $name(&mut self, $($arg_name : $arg_ty),*) $(-> $ret)? { - if self.temp_bufs.is_empty() { - self.buf.$name($($arg_name),*) - } else { - self.temp_bufs.last_mut().unwrap().$name($($arg_name),*) - } - } - }; -} - -macro_rules! return_source_if_disabled { - ($self:expr, $loc:expr) => {{ - let loc = $loc; - if $self.inline_config.is_disabled(loc) { - trace!("Returning because disabled: {loc:?}"); - return $self.visit_source(loc) - } - }}; - ($self:expr, $loc:expr, $suffix:literal) => {{ - let mut loc = $loc; - let has_suffix = $self.extend_loc_until(&mut loc, $suffix); - if $self.inline_config.is_disabled(loc) { - $self.visit_source(loc)?; - trace!("Returning because disabled: {loc:?}"); - if !has_suffix { - write!($self.buf(), "{}", $suffix)?; - } - return Ok(()) - } - }}; -} - -macro_rules! visit_source_if_disabled_else { - ($self:expr, $loc:expr, $block:block) => {{ - let loc = $loc; - if $self.inline_config.is_disabled(loc) { - $self.visit_source(loc)?; - } else $block - }}; -} - -pub(crate) use buf_fn; -pub(crate) use return_source_if_disabled; -pub(crate) use visit_source_if_disabled_else; -pub(crate) use write_chunk; -pub(crate) use write_chunk_spaced; -pub(crate) use writeln_chunk; diff --git a/crates/fmt-2/src/main.rs b/crates/fmt-2/src/main.rs index 60683d923cfa4..cc040ab0d8d37 100644 --- a/crates/fmt-2/src/main.rs +++ b/crates/fmt-2/src/main.rs @@ -10,7 +10,7 @@ fn main() { let path = PathBuf::from(&args[1]); (std::fs::read_to_string(&path).unwrap(), Some(path)) }; - match forge_fmt_2::printer::format_source(&src, path.as_deref(), Default::default()) { + match forge_fmt_2::format_source(&src, path.as_deref(), Default::default()) { Ok(formatted) => { print!("{formatted}"); } diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs new file mode 100644 index 0000000000000..225ec683be596 --- /dev/null +++ b/crates/fmt-2/src/pp/convenience.rs @@ -0,0 +1,85 @@ +use super::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, Token, SIZE_INFINITY}; +use std::borrow::Cow; + +impl Printer { + /// "raw box" + pub fn rbox(&mut self, indent: isize, breaks: Breaks) { + self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks }) + } + + /// Inconsistent breaking box + pub fn ibox(&mut self, indent: isize) { + self.rbox(indent, Breaks::Inconsistent) + } + + /// Consistent breaking box + pub fn cbox(&mut self, indent: isize) { + self.rbox(indent, Breaks::Consistent) + } + + pub fn visual_align(&mut self) { + self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent }); + } + + pub fn break_offset(&mut self, n: usize, off: isize) { + self.scan_break(BreakToken { + offset: off, + blank_space: n as isize, + ..BreakToken::default() + }); + } + + pub fn end(&mut self) { + self.scan_end() + } + + pub fn word>>(&mut self, wrd: S) { + let string = wrd.into(); + self.scan_string(string) + } + + fn spaces(&mut self, n: usize) { + self.break_offset(n, 0) + } + + pub fn zerobreak(&mut self) { + self.spaces(0) + } + + pub fn space(&mut self) { + self.spaces(1) + } + + pub fn hardbreak(&mut self) { + self.spaces(SIZE_INFINITY as usize) + } + + pub fn is_beginning_of_line(&self) -> bool { + match self.last_token() { + Some(last_token) => last_token.is_hardbreak_tok(), + None => true, + } + } + + pub(crate) fn hardbreak_tok_offset(offset: isize) -> Token { + Token::Break(BreakToken { offset, blank_space: SIZE_INFINITY, ..BreakToken::default() }) + } + + pub fn trailing_comma(&mut self) { + self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); + } + + pub fn trailing_comma_or_space(&mut self) { + self.scan_break(BreakToken { + blank_space: 1, + pre_break: Some(','), + ..BreakToken::default() + }); + } +} + +impl Token { + pub(crate) fn is_hardbreak_tok(&self) -> bool { + *self == Printer::hardbreak_tok_offset(0) + } +} diff --git a/crates/fmt-2/src/pp/helpers.rs b/crates/fmt-2/src/pp/helpers.rs new file mode 100644 index 0000000000000..dbdf9914e044c --- /dev/null +++ b/crates/fmt-2/src/pp/helpers.rs @@ -0,0 +1,48 @@ +use super::Printer; +use std::borrow::Cow; + +impl Printer { + pub fn word_space>>(&mut self, w: W) { + self.word(w); + self.space(); + } + + pub fn popen(&mut self) { + self.word("("); + } + + pub fn pclose(&mut self) { + self.word(")"); + } + + pub fn hardbreak_if_not_bol(&mut self) { + if !self.is_beginning_of_line() { + self.hardbreak() + } + } + + pub fn space_if_not_bol(&mut self) { + if !self.is_beginning_of_line() { + self.space(); + } + } + + pub fn nbsp(&mut self) { + self.word(" ") + } + + pub fn word_nbsp>>(&mut self, w: S) { + self.word(w); + self.nbsp() + } + + /// Synthesizes a comment that was not textually present in the original + /// source file. + pub fn synth_comment(&mut self, text: impl Into>) { + self.word("/*"); + self.space(); + self.word(text); + self.space(); + self.word("*/") + } +} diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs new file mode 100644 index 0000000000000..6c094c2498ca4 --- /dev/null +++ b/crates/fmt-2/src/pp/mod.rs @@ -0,0 +1,325 @@ +// Adapted from https://github.com/dtolnay/prettyplease/blob/8eb8c14649aea32e810732bd4d64fe519e6b752a/src/algorithm.rs. + +use ring::RingBuffer; +use std::{borrow::Cow, cmp, collections::VecDeque, iter}; + +mod convenience; +mod helpers; +mod ring; + +const DEBUG: bool = false; +const DEBUG_INDENT: bool = false; + +// TODO(dani): config + +// Target line width. +const MARGIN: isize = 89; + +// Number of spaces increment at each level of block indentation. +const INDENT: isize = 4; + +// Every line is allowed at least this much space, even if highly indented. +const MIN_SPACE: isize = 60; + +/// How to break. Described in more detail in the module docs. +#[derive(Clone, Copy, PartialEq)] +pub enum Breaks { + Consistent, + Inconsistent, +} + +#[derive(Clone, Copy, PartialEq)] +enum IndentStyle { + /// Vertically aligned under whatever column this block begins at. + /// + /// fn demo(arg1: usize, + /// arg2: usize) {} + Visual, + /// Indented relative to the indentation level of the previous line. + /// + /// fn demo( + /// arg1: usize, + /// arg2: usize, + /// ) {} + Block { offset: isize }, +} + +#[derive(Clone, Copy, Default, PartialEq)] +pub(crate) struct BreakToken { + offset: isize, + blank_space: isize, + pre_break: Option, +} + +#[derive(Clone, Copy, PartialEq)] +pub(crate) struct BeginToken { + indent: IndentStyle, + breaks: Breaks, +} + +#[derive(PartialEq)] +pub(crate) enum Token { + // In practice a string token contains either a `&'static str` or a + // `String`. `Cow` is overkill for this because we never modify the data, + // but it's more convenient than rolling our own more specialized type. + String(Cow<'static, str>), + Break(BreakToken), + Begin(BeginToken), + End, +} + +#[derive(Copy, Clone)] +enum PrintFrame { + Fits, + Broken { indent: usize, breaks: Breaks }, +} + +const SIZE_INFINITY: isize = 0xffff; + +pub struct Printer { + out: String, + /// Number of spaces left on line + space: isize, + /// Ring-buffer of tokens and calculated sizes + buf: RingBuffer, + /// Running size of stream "...left" + left_total: isize, + /// Running size of stream "...right" + right_total: isize, + /// Pseudo-stack, really a ring too. Holds the + /// primary-ring-buffers index of the Begin that started the + /// current block, possibly with the most recent Break after that + /// Begin (if there is any) on top of it. Stuff is flushed off the + /// bottom as it becomes irrelevant due to the primary ring-buffer + /// advancing. + scan_stack: VecDeque, + /// Stack of blocks-in-progress being flushed by print + print_stack: Vec, + /// Level of indentation of current line + indent: usize, + /// Buffered indentation to avoid writing trailing whitespace + pending_indentation: isize, + /// The token most recently popped from the left boundary of the + /// ring-buffer for printing + last_printed: Option, +} + +struct BufEntry { + token: Token, + size: isize, +} + +impl Printer { + pub fn new() -> Self { + Printer { + out: String::new(), + space: MARGIN, + buf: RingBuffer::new(), + left_total: 0, + right_total: 0, + scan_stack: VecDeque::new(), + print_stack: Vec::new(), + indent: 0, + pending_indentation: 0, + last_printed: None, + } + } + + pub(crate) fn last_token(&self) -> Option<&Token> { + self.last_token_still_buffered().or_else(|| self.last_printed.as_ref()) + } + + pub(crate) fn last_token_still_buffered(&self) -> Option<&Token> { + self.buf.last().map(|last| &last.token) + } + + /// Be very careful with this! + pub(crate) fn replace_last_token_still_buffered(&mut self, token: Token) { + self.buf.last_mut().unwrap().token = token; + } + + fn scan_eof(&mut self) { + if !self.scan_stack.is_empty() { + self.check_stack(0); + self.advance_left(); + } + } + + fn scan_begin(&mut self, token: BeginToken) { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.buf.clear(); + } + let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total }); + self.scan_stack.push_back(right); + } + + fn scan_end(&mut self) { + if self.scan_stack.is_empty() { + self.print_end(); + } else { + let right = self.buf.push(BufEntry { token: Token::End, size: -1 }); + self.scan_stack.push_back(right); + } + } + + fn scan_break(&mut self, token: BreakToken) { + if self.scan_stack.is_empty() { + self.left_total = 1; + self.right_total = 1; + self.buf.clear(); + } else { + self.check_stack(0); + } + let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total }); + self.scan_stack.push_back(right); + self.right_total += token.blank_space; + } + + fn scan_string(&mut self, string: Cow<'static, str>) { + if self.scan_stack.is_empty() { + self.print_string(&string); + } else { + let len = string.len() as isize; + self.buf.push(BufEntry { token: Token::String(string), size: len }); + self.right_total += len; + self.check_stream(); + } + } + + pub(crate) fn offset(&mut self, offset: isize) { + if let Some(BufEntry { token: Token::Break(token), .. }) = &mut self.buf.last_mut() { + token.offset += offset; + } + } + + fn check_stream(&mut self) { + while self.right_total - self.left_total > self.space { + if *self.scan_stack.front().unwrap() == self.buf.index_of_first() { + self.scan_stack.pop_front().unwrap(); + self.buf.first_mut().unwrap().size = SIZE_INFINITY; + } + self.advance_left(); + if self.buf.is_empty() { + break; + } + } + } + + fn advance_left(&mut self) { + while self.buf.first().unwrap().size >= 0 { + let left = self.buf.pop_first().unwrap(); + + match &left.token { + Token::String(string) => { + self.left_total += string.len() as isize; + self.print_string(string); + } + Token::Break(token) => { + self.left_total += token.blank_space; + self.print_break(*token, left.size); + } + Token::Begin(token) => self.print_begin(*token, left.size), + Token::End => self.print_end(), + } + + self.last_printed = Some(left.token); + + if self.buf.is_empty() { + break; + } + } + } + + fn check_stack(&mut self, mut depth: usize) { + while let Some(&index) = self.scan_stack.back() { + let entry = &mut self.buf[index]; + match entry.token { + Token::Begin(_) => { + if depth == 0 { + break; + } + self.scan_stack.pop_back().unwrap(); + entry.size += self.right_total; + depth -= 1; + } + Token::End => { + // paper says + not =, but that makes no sense. + self.scan_stack.pop_back().unwrap(); + entry.size = 1; + depth += 1; + } + _ => { + self.scan_stack.pop_back().unwrap(); + entry.size += self.right_total; + if depth == 0 { + break; + } + } + } + } + } + + fn get_top(&self) -> PrintFrame { + *self + .print_stack + .last() + .unwrap_or(&PrintFrame::Broken { indent: 0, breaks: Breaks::Inconsistent }) + } + + fn print_begin(&mut self, token: BeginToken, size: isize) { + if size > self.space { + self.print_stack.push(PrintFrame::Broken { indent: self.indent, breaks: token.breaks }); + self.indent = match token.indent { + IndentStyle::Block { offset } => { + usize::try_from(self.indent as isize + offset).unwrap() + } + IndentStyle::Visual => (MARGIN - self.space) as usize, + }; + } else { + self.print_stack.push(PrintFrame::Fits); + } + } + + fn print_end(&mut self) { + if let PrintFrame::Broken { indent, .. } = self.print_stack.pop().unwrap() { + self.indent = indent; + } + } + + fn print_break(&mut self, token: BreakToken, size: isize) { + let fits = match self.get_top() { + PrintFrame::Fits => true, + PrintFrame::Broken { breaks: Breaks::Consistent, .. } => false, + PrintFrame::Broken { breaks: Breaks::Inconsistent, .. } => size <= self.space, + }; + if fits { + self.pending_indentation += token.blank_space; + self.space -= token.blank_space; + } else { + if let Some(pre_break) = token.pre_break { + self.out.push(pre_break); + } + self.out.push('\n'); + let indent = self.indent as isize + token.offset; + self.pending_indentation = indent; + self.space = cmp::max(MARGIN - indent, MIN_SPACE); + } + } + + fn print_string(&mut self, string: &str) { + // Write the pending indent. A more concise way of doing this would be: + // + // write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?; + // + // But that is significantly slower. This code is sufficiently hot, and indents can get + // sufficiently large, that the difference is significant on some workloads. + self.out.reserve(self.pending_indentation as usize); + self.out.extend(iter::repeat(' ').take(self.pending_indentation as usize)); + self.pending_indentation = 0; + + self.out.push_str(string); + self.space -= string.len() as isize; + } +} diff --git a/crates/fmt-2/src/pp/ring.rs b/crates/fmt-2/src/pp/ring.rs new file mode 100644 index 0000000000000..c3ad88f788dc5 --- /dev/null +++ b/crates/fmt-2/src/pp/ring.rs @@ -0,0 +1,79 @@ +use std::{ + collections::VecDeque, + ops::{Index, IndexMut}, +}; + +/// A view onto a finite range of an infinitely long sequence of T. +/// +/// The Ts are indexed 0..infinity. A RingBuffer begins as a view of elements +/// 0..0 (i.e. nothing). The user of the RingBuffer advances its left and right +/// position independently, although only in the positive direction, and only +/// with left <= right at all times. +/// +/// Holding a RingBuffer whose view is elements left..right gives the ability to +/// use Index and IndexMut to access elements i in the infinitely long queue for +/// which left <= i < right. +pub(super) struct RingBuffer { + data: VecDeque, + // Abstract index of data[0] in the infinitely sized queue. + offset: usize, +} + +impl RingBuffer { + pub(super) fn new() -> Self { + RingBuffer { data: VecDeque::new(), offset: 0 } + } + + pub(super) fn is_empty(&self) -> bool { + self.data.is_empty() + } + + pub(super) fn push(&mut self, value: T) -> usize { + let index = self.offset + self.data.len(); + self.data.push_back(value); + index + } + + pub(super) fn clear(&mut self) { + self.data.clear(); + } + + pub(super) fn index_of_first(&self) -> usize { + self.offset + } + + pub(super) fn first(&self) -> Option<&T> { + self.data.front() + } + + pub(super) fn first_mut(&mut self) -> Option<&mut T> { + self.data.front_mut() + } + + pub(super) fn pop_first(&mut self) -> Option { + let first = self.data.pop_front()?; + self.offset += 1; + Some(first) + } + + pub(super) fn last(&self) -> Option<&T> { + self.data.back() + } + + pub(super) fn last_mut(&mut self) -> Option<&mut T> { + self.data.back_mut() + } +} + +impl Index for RingBuffer { + type Output = T; + fn index(&self, index: usize) -> &Self::Output { + &self.data[index.checked_sub(self.offset).unwrap()] + } +} + +impl IndexMut for RingBuffer { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.data[index.checked_sub(self.offset).unwrap()] + } +} diff --git a/crates/fmt-2/src/printer/algorithm.rs b/crates/fmt-2/src/printer/algorithm.rs deleted file mode 100644 index 56844f4acdde3..0000000000000 --- a/crates/fmt-2/src/printer/algorithm.rs +++ /dev/null @@ -1,380 +0,0 @@ -// Adapted from https://github.com/dtolnay/prettyplease/blob/8eb8c14649aea32e810732bd4d64fe519e6b752a/src/algorithm.rs. - -use super::ring::RingBuffer; -use std::{borrow::Cow, cmp, collections::VecDeque, iter}; - -const DEBUG: bool = false; -const DEBUG_INDENT: bool = false; - -// TODO(dani): config - -// Target line width. -const MARGIN: isize = 89; - -// Number of spaces increment at each level of block indentation. -const INDENT: isize = 4; - -// Every line is allowed at least this much space, even if highly indented. -const MIN_SPACE: isize = 60; - -#[derive(Clone, Copy, PartialEq)] -pub enum Breaks { - Consistent, - Inconsistent, -} - -#[derive(Clone, Copy, Default)] -pub struct BreakToken { - pub offset: isize, - pub blank_space: usize, - pub pre_break: Option, - pub post_break: &'static str, - pub no_break: Option, - pub if_nonempty: bool, - pub never_break: bool, -} - -#[derive(Clone, Copy)] -pub struct BeginToken { - pub offset: isize, - pub breaks: Breaks, -} - -#[derive(Clone)] -pub enum Token { - String(Cow<'static, str>), - Break(BreakToken), - Begin(BeginToken), - End, -} - -#[derive(Copy, Clone)] -enum PrintFrame { - Fits(Breaks), - Broken(usize, Breaks), -} - -pub const SIZE_INFINITY: isize = 0xffff; - -pub struct Printer { - out: String, - // Number of spaces left on line - space: isize, - // Ring-buffer of tokens and calculated sizes - buf: RingBuffer, - // Total size of tokens already printed - left_total: isize, - // Total size of tokens enqueued, including printed and not yet printed - right_total: isize, - // Holds the ring-buffer index of the Begin that started the current block, - // possibly with the most recent Break after that Begin (if there is any) on - // top of it. Values are pushed and popped on the back of the queue using it - // like stack, and elsewhere old values are popped from the front of the - // queue as they become irrelevant due to the primary ring-buffer advancing. - scan_stack: VecDeque, - // Stack of blocks-in-progress being flushed by print - print_stack: Vec, - // Level of indentation of current line - indent: usize, - // Buffered indentation to avoid writing trailing whitespace - pending_indentation: usize, -} - -#[derive(Clone)] -struct BufEntry { - token: Token, - size: isize, -} - -impl Printer { - pub fn new() -> Self { - Printer { - out: String::new(), - space: MARGIN, - buf: RingBuffer::new(), - left_total: 0, - right_total: 0, - scan_stack: VecDeque::new(), - print_stack: Vec::new(), - indent: 0, - pending_indentation: 0, - } - } - - pub fn eof(mut self) -> String { - if !self.scan_stack.is_empty() { - self.check_stack(0); - self.advance_left(); - } - self.out - } - - pub fn scan_begin(&mut self, token: BeginToken) { - if self.scan_stack.is_empty() { - self.left_total = 1; - self.right_total = 1; - self.buf.clear(); - } - let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total }); - self.scan_stack.push_back(right); - } - - pub fn scan_end(&mut self) { - if self.scan_stack.is_empty() { - self.print_end(); - } else { - if !self.buf.is_empty() { - if let Token::Break(break_token) = self.buf.last().token { - if self.buf.len() >= 2 { - if let Token::Begin(_) = self.buf.second_last().token { - self.buf.pop_last(); - self.buf.pop_last(); - self.scan_stack.pop_back(); - self.scan_stack.pop_back(); - self.right_total -= break_token.blank_space as isize; - return; - } - } - if break_token.if_nonempty { - self.buf.pop_last(); - self.scan_stack.pop_back(); - self.right_total -= break_token.blank_space as isize; - } - } - } - let right = self.buf.push(BufEntry { token: Token::End, size: -1 }); - self.scan_stack.push_back(right); - } - } - - pub fn scan_break(&mut self, token: BreakToken) { - if self.scan_stack.is_empty() { - self.left_total = 1; - self.right_total = 1; - self.buf.clear(); - } else { - self.check_stack(0); - } - let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total }); - self.scan_stack.push_back(right); - self.right_total += token.blank_space as isize; - } - - pub fn scan_string(&mut self, string: Cow<'static, str>) { - if self.scan_stack.is_empty() { - self.print_string(string); - } else { - let len = string.len() as isize; - self.buf.push(BufEntry { token: Token::String(string), size: len }); - self.right_total += len; - self.check_stream(); - } - } - - #[track_caller] - pub fn offset(&mut self, offset: isize) { - match &mut self.buf.last_mut().token { - Token::Break(token) => token.offset += offset, - Token::Begin(_) => {} - Token::String(_) | Token::End => unreachable!(), - } - } - - pub fn end_with_max_width(&mut self, max: isize) { - let mut depth = 1; - for &index in self.scan_stack.iter().rev() { - let entry = &self.buf[index]; - match entry.token { - Token::Begin(_) => { - depth -= 1; - if depth == 0 { - if entry.size < 0 { - let actual_width = entry.size + self.right_total; - if actual_width > max { - self.buf.push(BufEntry { - token: Token::String(Cow::Borrowed("")), - size: SIZE_INFINITY, - }); - self.right_total += SIZE_INFINITY; - } - } - break; - } - } - Token::End => depth += 1, - Token::Break(_) => {} - Token::String(_) => unreachable!(), - } - } - self.scan_end(); - } - - pub fn ends_with(&self, ch: char) -> bool { - for i in self.buf.index_range().rev() { - if let Token::String(token) = &self.buf[i].token { - return token.ends_with(ch); - } - } - self.out.ends_with(ch) - } - - fn check_stream(&mut self) { - while self.right_total - self.left_total > self.space { - if *self.scan_stack.front().unwrap() == self.buf.index_range().start { - self.scan_stack.pop_front().unwrap(); - self.buf.first_mut().size = SIZE_INFINITY; - } - - self.advance_left(); - - if self.buf.is_empty() { - break; - } - } - } - - fn advance_left(&mut self) { - while self.buf.first().size >= 0 { - let left = self.buf.pop_first(); - - match left.token { - Token::String(string) => { - self.left_total += left.size; - self.print_string(string); - } - Token::Break(token) => { - self.left_total += token.blank_space as isize; - self.print_break(token, left.size); - } - Token::Begin(token) => self.print_begin(token, left.size), - Token::End => self.print_end(), - } - - if self.buf.is_empty() { - break; - } - } - } - - fn check_stack(&mut self, mut depth: usize) { - while let Some(&index) = self.scan_stack.back() { - let entry = &mut self.buf[index]; - match entry.token { - Token::Begin(_) => { - if depth == 0 { - break; - } - self.scan_stack.pop_back().unwrap(); - entry.size += self.right_total; - depth -= 1; - } - Token::End => { - self.scan_stack.pop_back().unwrap(); - entry.size = 1; - depth += 1; - } - Token::Break(_) => { - self.scan_stack.pop_back().unwrap(); - entry.size += self.right_total; - if depth == 0 { - break; - } - } - Token::String(_) => unreachable!(), - } - } - } - - fn get_top(&self) -> PrintFrame { - const OUTER: PrintFrame = PrintFrame::Broken(0, Breaks::Inconsistent); - self.print_stack.last().map_or(OUTER, PrintFrame::clone) - } - - fn print_begin(&mut self, token: BeginToken, size: isize) { - if DEBUG { - self.out.push(match token.breaks { - Breaks::Consistent => '«', - Breaks::Inconsistent => '‹', - }); - if DEBUG_INDENT { - self.out.extend(token.offset.to_string().chars().map(|ch| match ch { - '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] - [(ch as u8 - b'0') as usize], - '-' => '₋', - _ => unreachable!(), - })); - } - } - if size > self.space { - self.print_stack.push(PrintFrame::Broken(self.indent, token.breaks)); - self.indent = usize::try_from(self.indent as isize + token.offset).unwrap(); - } else { - self.print_stack.push(PrintFrame::Fits(token.breaks)); - } - } - - fn print_end(&mut self) { - let breaks = match self.print_stack.pop().unwrap() { - PrintFrame::Broken(indent, breaks) => { - self.indent = indent; - breaks - } - PrintFrame::Fits(breaks) => breaks, - }; - if DEBUG { - self.out.push(match breaks { - Breaks::Consistent => '»', - Breaks::Inconsistent => '›', - }); - } - } - - fn print_break(&mut self, token: BreakToken, size: isize) { - let fits = token.never_break || - match self.get_top() { - PrintFrame::Fits(..) => true, - PrintFrame::Broken(.., Breaks::Consistent) => false, - PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space, - }; - if fits { - self.pending_indentation += token.blank_space; - self.space -= token.blank_space as isize; - if let Some(no_break) = token.no_break { - self.out.push(no_break); - self.space -= no_break.len_utf8() as isize; - } - if DEBUG { - self.out.push('·'); - } - } else { - if let Some(pre_break) = token.pre_break { - self.print_indent(); - self.out.push(pre_break); - } - if DEBUG { - self.out.push('·'); - } - self.out.push('\n'); - let indent = self.indent as isize + token.offset; - self.pending_indentation = usize::try_from(indent).unwrap(); - self.space = cmp::max(MARGIN - indent, MIN_SPACE); - if !token.post_break.is_empty() { - self.print_indent(); - self.out.push_str(token.post_break); - self.space -= token.post_break.len() as isize; - } - } - } - - fn print_string(&mut self, string: Cow<'static, str>) { - self.print_indent(); - self.out.push_str(&string); - self.space -= string.len() as isize; - } - - fn print_indent(&mut self) { - self.out.reserve(self.pending_indentation); - self.out.extend(iter::repeat(' ').take(self.pending_indentation)); - self.pending_indentation = 0; - } -} diff --git a/crates/fmt-2/src/printer/comments.rs b/crates/fmt-2/src/printer/comments.rs deleted file mode 100644 index 5e114ff3b1382..0000000000000 --- a/crates/fmt-2/src/printer/comments.rs +++ /dev/null @@ -1,179 +0,0 @@ -use super::comment::{Comment, CommentStyle}; -use solar_parse::{ - ast::Span, - interface::{source_map::SourceFile, BytePos, CharPos, SourceMap}, - lexer::token::RawTokenKind as TokenKind, -}; - -pub struct Comments<'a> { - sm: &'a SourceMap, - // Stored in reverse order so we can consume them by popping. - reversed_comments: Vec, -} - -/// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. -/// Otherwise returns `Some(k)` where `k` is first char offset after that leading -/// whitespace. Note that `k` may be outside bounds of `s`. -fn all_whitespace(s: &str, col: CharPos) -> Option { - let mut idx = 0; - for (i, ch) in s.char_indices().take(col.to_usize()) { - if !ch.is_whitespace() { - return None; - } - idx = i + ch.len_utf8(); - } - Some(idx) -} - -fn trim_whitespace_prefix(s: &str, col: CharPos) -> &str { - let len = s.len(); - match all_whitespace(s, col) { - Some(col) => { - if col < len { - &s[col..] - } else { - "" - } - } - None => s, - } -} - -fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec { - let mut res: Vec = vec![]; - let mut lines = text.lines(); - // just push the first line - res.extend(lines.next().map(|it| it.to_string())); - // for other lines, strip common whitespace prefix - for line in lines { - res.push(trim_whitespace_prefix(line, col).to_string()) - } - res -} - -/// Returns the `BytePos` of the beginning of the current line. -fn line_begin_pos(sf: &SourceFile, pos: BytePos) -> BytePos { - let pos = sf.relative_position(pos); - let line_index = sf.lookup_line(pos).unwrap(); - let line_start_pos = sf.lines()[line_index]; - sf.absolute_position(line_start_pos) -} - -fn gather_comments(sf: &SourceFile) -> Vec { - let text = sf.src.as_str(); - let start_bpos = sf.start_pos; - let mut pos = 0; - let mut comments: Vec = Vec::new(); - let mut code_to_the_left = false; - - let make_span = |start, end| Span::new(start, end); - - /* - if let Some(shebang_len) = strip_shebang(text) { - comments.push(Comment { - style: CommentStyle::Isolated, - lines: vec![text[..shebang_len].to_string()], - pos: start_bpos, - }); - pos += shebang_len; - } - */ - - for token in solar_parse::Cursor::new(&text[pos..]) { - let token_text = &text[pos..pos + token.len as usize]; - match token.kind { - TokenKind::Whitespace => { - if let Some(mut idx) = token_text.find('\n') { - code_to_the_left = false; - while let Some(next_newline) = token_text[idx + 1..].find('\n') { - idx += 1 + next_newline; - let start = start_bpos + BytePos((pos + idx) as u32); - let end = start; - comments.push(Comment { - style: CommentStyle::BlankLine, - lines: vec![], - span: make_span(start, end), - }); - } - } - } - TokenKind::BlockComment { is_doc, .. } => { - if !is_doc { - let code_to_the_right = !matches!( - text[pos + token.len as usize..].chars().next(), - Some('\r' | '\n') - ); - let style = match (code_to_the_left, code_to_the_right) { - (_, true) => CommentStyle::Mixed, - (false, false) => CommentStyle::Isolated, - (true, false) => CommentStyle::Trailing, - }; - - // Count the number of chars since the start of the line by rescanning. - let pos_in_file = start_bpos + BytePos(pos as u32); - let line_begin_in_file = line_begin_pos(sf, pos_in_file); - let line_begin_pos = (line_begin_in_file - start_bpos).to_usize(); - let col = CharPos(text[line_begin_pos..pos].chars().count()); - - let lines = split_block_comment_into_lines(token_text, col); - comments.push(Comment { style, lines, pos: pos_in_file }) - } - } - TokenKind::LineComment { is_doc } => { - if !is_doc { - comments.push(Comment { - style: if code_to_the_left { - CommentStyle::Trailing - } else { - CommentStyle::Isolated - }, - lines: vec![token_text.to_string()], - pos: start_bpos + BytePos(pos as u32), - }) - } - } - _ => { - code_to_the_left = true; - } - } - pos += token.len as usize; - } - - comments -} - -impl<'a> Comments<'a> { - pub fn new(sm: &'a SourceMap, sf: &SourceFile) -> Self { - let mut comments = gather_comments(sf); - comments.reverse(); - Comments { sm, reversed_comments: comments } - } - - pub fn peek(&self) -> Option<&Comment> { - self.reversed_comments.last() - } - - pub fn next(&mut self) -> Option { - self.reversed_comments.pop() - } - - pub fn iter(&self) -> impl Iterator { - self.reversed_comments.iter().rev() - } - - fn trailing_comment(&mut self, span: Span, next_pos: Option) -> Option { - if let Some(cmnt) = self.peek() { - if cmnt.style != CommentStyle::Trailing { - return None; - } - let span_line = self.sm.lookup_char_pos(span.hi()); - let comment_line = self.sm.lookup_char_pos(cmnt.pos); - let next = next_pos.unwrap_or_else(|| cmnt.pos + BytePos(1)); - if span.hi() < cmnt.pos && cmnt.pos < next && span_line.line == comment_line.line { - return Some(self.next().unwrap()); - } - } - - None - } -} diff --git a/crates/fmt-2/src/printer/convenience.rs b/crates/fmt-2/src/printer/convenience.rs deleted file mode 100644 index dc8ecc62b58c4..0000000000000 --- a/crates/fmt-2/src/printer/convenience.rs +++ /dev/null @@ -1,79 +0,0 @@ -use super::algorithm::{self, BeginToken, BreakToken, Breaks, Printer}; -use std::borrow::Cow; - -impl Printer { - pub fn ibox(&mut self, indent: isize) { - self.scan_begin(BeginToken { offset: indent, breaks: Breaks::Inconsistent }); - } - - pub fn cbox(&mut self, indent: isize) { - self.scan_begin(BeginToken { offset: indent, breaks: Breaks::Consistent }); - } - - pub fn end(&mut self) { - self.scan_end(); - } - - pub fn word>>(&mut self, wrd: S) { - let s = wrd.into(); - self.scan_string(s); - } - - fn spaces(&mut self, n: usize) { - self.scan_break(BreakToken { blank_space: n, ..BreakToken::default() }); - } - - pub fn zerobreak(&mut self) { - self.spaces(0); - } - - pub fn space(&mut self) { - self.spaces(1); - } - - pub fn nbsp(&mut self) { - self.word(" "); - } - - pub fn hardbreak(&mut self) { - self.spaces(algorithm::SIZE_INFINITY as usize); - } - - pub fn space_if_nonempty(&mut self) { - self.scan_break(BreakToken { blank_space: 1, if_nonempty: true, ..BreakToken::default() }); - } - - pub fn hardbreak_if_nonempty(&mut self) { - self.scan_break(BreakToken { - blank_space: algorithm::SIZE_INFINITY as usize, - if_nonempty: true, - ..BreakToken::default() - }); - } - - pub fn trailing_comma(&mut self, is_last: bool) { - if is_last { - self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); - } else { - self.word(","); - self.space(); - } - } - - pub fn trailing_comma_or_space(&mut self, is_last: bool) { - if is_last { - self.scan_break(BreakToken { - blank_space: 1, - pre_break: Some(','), - ..BreakToken::default() - }); - } else { - self.word(","); - self.space(); - } - } - - pub fn neverbreak(&mut self) { - self.scan_break(BreakToken { never_break: true, ..BreakToken::default() }); - } -} diff --git a/crates/fmt-2/src/printer/mod.rs b/crates/fmt-2/src/printer/mod.rs deleted file mode 100644 index 468cb2bfa1e5d..0000000000000 --- a/crates/fmt-2/src/printer/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -use comments::Comments; - -use crate::{FormatterConfig, FormatterError, InlineConfig}; -use std::path::Path; - -mod algorithm; -mod convenience; -mod ring; - -mod comment; -mod comments; - -type Result = std::result::Result; - -pub fn format_file(path: &Path, config: FormatterConfig) -> Result { - let source = std::fs::read_to_string(path).map_err(FormatterError::custom)?; - format_source(&source, Some(path), config) -} - -pub fn format_source(source: &str, path: Option<&Path>, config: FormatterConfig) -> Result { - let sess = - solar_parse::interface::Session::builder().with_buffer_emitter(Default::default()).build(); - let res = sess.enter(|| -> solar_parse::interface::Result<_> { - let name = match path { - Some(path) => solar_parse::interface::source_map::FileName::Real(path.to_path_buf()), - None => solar_parse::interface::source_map::FileName::Custom("fmt".to_string()), - }; - let arena = solar_parse::ast::Arena::new(); - let file = sess - .source_map() - .new_source_file(name, source) - .map_err(|e| sess.dcx.err(e.to_string()).emit())?; - let mut parser = solar_parse::Parser::from_source_file(&sess, &arena, &file); - let ast = parser.parse_file().map_err(|e| e.emit())?; - Ok(format_source_unit(&ast, source, Comments::new(sess.source_map(), &file), config)) - }); - sess.emitted_errors().unwrap().map_err(FormatterError::custom)?; - Ok(res.unwrap()) -} - -fn format_source_unit( - source_unit: &solar_parse::ast::SourceUnit<'_>, - src: &str, - comments: Comments<'_>, - config: FormatterConfig, -) -> String { - let mut state = State::new(config, inline_configs(&comments, src), Some(comments)); - state.source_unit(source_unit); - state.eof() -} - -struct State<'a> { - s: algorithm::Printer, - comments: Option>, - config: FormatterConfig, - inline_config: InlineConfig, -} - -impl std::ops::Deref for State<'_> { - type Target = algorithm::Printer; - - fn deref(&self) -> &Self::Target { - &self.s - } -} - -impl std::ops::DerefMut for State<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.s - } -} - -impl<'a> State<'a> { - fn new( - config: FormatterConfig, - inline_config: InlineConfig, - comments: Option>, - ) -> Self { - Self { s: algorithm::Printer::new(), comments, inline_config, config } - } - - fn once(config: FormatterConfig) { - Self::new(config, InlineConfig::default(), None); - } -} - -fn inline_configs(comments: &Comments<'_>, src: &str) -> InlineConfig { - comments - .iter() - .filter_map(|comment| { - Some((comment, comment.lines.first()?.trim_start().strip_prefix("forgefmt:")?.trim())) - }) - .map(|(comment, item)| { - let loc = comment.loc; - item.parse().map(|out| (loc, out)).map_err(|out| (loc, out)) - }) -} diff --git a/crates/fmt-2/src/printer/ring.rs b/crates/fmt-2/src/printer/ring.rs deleted file mode 100644 index 7ee04e6eec0f8..0000000000000 --- a/crates/fmt-2/src/printer/ring.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::{ - collections::VecDeque, - ops::{Index, IndexMut, Range}, -}; - -pub struct RingBuffer { - data: VecDeque, - // Abstract index of data[0] in infinitely sized queue - offset: usize, -} - -impl RingBuffer { - pub fn new() -> Self { - RingBuffer { data: VecDeque::new(), offset: 0 } - } - - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } - - pub fn len(&self) -> usize { - self.data.len() - } - - pub fn push(&mut self, value: T) -> usize { - let index = self.offset + self.data.len(); - self.data.push_back(value); - index - } - - pub fn clear(&mut self) { - self.data.clear(); - } - - pub fn index_range(&self) -> Range { - self.offset..self.offset + self.data.len() - } - - pub fn first(&self) -> &T { - &self.data[0] - } - - pub fn first_mut(&mut self) -> &mut T { - &mut self.data[0] - } - - pub fn pop_first(&mut self) -> T { - self.offset += 1; - self.data.pop_front().unwrap() - } - - pub fn last(&self) -> &T { - self.data.back().unwrap() - } - - pub fn last_mut(&mut self) -> &mut T { - self.data.back_mut().unwrap() - } - - pub fn second_last(&self) -> &T { - &self.data[self.data.len() - 2] - } - - pub fn pop_last(&mut self) { - self.data.pop_back().unwrap(); - } -} - -impl Index for RingBuffer { - type Output = T; - fn index(&self, index: usize) -> &Self::Output { - &self.data[index.checked_sub(self.offset).unwrap()] - } -} - -impl IndexMut for RingBuffer { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.data[index.checked_sub(self.offset).unwrap()] - } -} diff --git a/crates/fmt-2/src/solang_ext/ast_eq.rs b/crates/fmt-2/src/solang_ext/ast_eq.rs deleted file mode 100644 index 3c796a0d00128..0000000000000 --- a/crates/fmt-2/src/solang_ext/ast_eq.rs +++ /dev/null @@ -1,683 +0,0 @@ -use alloy_primitives::{Address, I256, U256}; -use solang_parser::pt::*; -use std::str::FromStr; - -/// Helper to convert a string number into a comparable one -fn to_num(string: &str) -> I256 { - if string.is_empty() { - return I256::ZERO - } - string.replace('_', "").trim().parse().unwrap() -} - -/// Helper to convert the fractional part of a number into a comparable one. -/// This will reverse the number so that 0's can be ignored -fn to_num_reversed(string: &str) -> U256 { - if string.is_empty() { - return U256::from(0) - } - string.replace('_', "").trim().chars().rev().collect::().parse().unwrap() -} - -/// Helper to filter [ParameterList] to omit empty -/// parameters -fn filter_params(list: &ParameterList) -> ParameterList { - list.iter().filter(|(_, param)| param.is_some()).cloned().collect::>() -} - -/// Check if two ParseTrees are equal ignoring location information or ordering if ordering does -/// not matter -pub trait AstEq { - fn ast_eq(&self, other: &Self) -> bool; -} - -impl AstEq for Loc { - fn ast_eq(&self, _other: &Self) -> bool { - true - } -} - -impl AstEq for IdentifierPath { - fn ast_eq(&self, other: &Self) -> bool { - self.identifiers.ast_eq(&other.identifiers) - } -} - -impl AstEq for SourceUnit { - fn ast_eq(&self, other: &Self) -> bool { - self.0.ast_eq(&other.0) - } -} - -impl AstEq for VariableDefinition { - fn ast_eq(&self, other: &Self) -> bool { - let sorted_attrs = |def: &Self| { - let mut attrs = def.attrs.clone(); - attrs.sort(); - attrs - }; - self.ty.ast_eq(&other.ty) && - self.name.ast_eq(&other.name) && - self.initializer.ast_eq(&other.initializer) && - sorted_attrs(self).ast_eq(&sorted_attrs(other)) - } -} - -impl AstEq for FunctionDefinition { - fn ast_eq(&self, other: &Self) -> bool { - // attributes - let sorted_attrs = |def: &Self| { - let mut attrs = def.attributes.clone(); - attrs.sort(); - attrs - }; - - // params - let left_params = filter_params(&self.params); - let right_params = filter_params(&other.params); - let left_returns = filter_params(&self.returns); - let right_returns = filter_params(&other.returns); - - self.ty.ast_eq(&other.ty) && - self.name.ast_eq(&other.name) && - left_params.ast_eq(&right_params) && - self.return_not_returns.ast_eq(&other.return_not_returns) && - left_returns.ast_eq(&right_returns) && - self.body.ast_eq(&other.body) && - sorted_attrs(self).ast_eq(&sorted_attrs(other)) - } -} - -impl AstEq for Base { - fn ast_eq(&self, other: &Self) -> bool { - self.name.ast_eq(&other.name) && - self.args.clone().unwrap_or_default().ast_eq(&other.args.clone().unwrap_or_default()) - } -} - -impl AstEq for Vec -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - if self.len() != other.len() { - false - } else { - self.iter().zip(other.iter()).all(|(left, right)| left.ast_eq(right)) - } - } -} - -impl AstEq for Option -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - match (self, other) { - (Some(left), Some(right)) => left.ast_eq(right), - (None, None) => true, - _ => false, - } - } -} - -impl AstEq for Box -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - T::ast_eq(self, other) - } -} - -impl AstEq for () { - fn ast_eq(&self, _other: &Self) -> bool { - true - } -} - -impl AstEq for &T -where - T: AstEq, -{ - fn ast_eq(&self, other: &Self) -> bool { - T::ast_eq(self, other) - } -} - -impl AstEq for String { - fn ast_eq(&self, other: &Self) -> bool { - match (Address::from_str(self), Address::from_str(other)) { - (Ok(left), Ok(right)) => left == right, - _ => self == other, - } - } -} - -macro_rules! ast_eq_field { - (#[ast_eq_use($convert_func:ident)] $field:ident) => { - $convert_func($field) - }; - ($field:ident) => { - $field - }; -} - -macro_rules! gen_ast_eq_enum { - ($self:expr, $other:expr, $name:ident { - $($unit_variant:ident),* $(,)? - _ - $($tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? )),* $(,)? - _ - $($struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? }),* $(,)? - }) => { - match $self { - $($name::$unit_variant => gen_ast_eq_enum!($other, $name, $unit_variant),)* - $($name::$tuple_variant($($tuple_field),*) => - gen_ast_eq_enum!($other, $name, $tuple_variant ($($(#[ast_eq_use($tuple_convert_func)])? $tuple_field),*)),)* - $($name::$struct_variant { $($struct_field),* } => - gen_ast_eq_enum!($other, $name, $struct_variant {$($(#[ast_eq_use($struct_convert_func)])? $struct_field),*}),)* - } - }; - ($other:expr, $name:ident, $unit_variant:ident) => { - { - matches!($other, $name::$unit_variant) - } - }; - ($other:expr, $name:ident, $tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? ) ) => { - { - let left = ($(ast_eq_field!($(#[ast_eq_use($tuple_convert_func)])? $tuple_field)),*); - if let $name::$tuple_variant($($tuple_field),*) = $other { - let right = ($(ast_eq_field!($(#[ast_eq_use($tuple_convert_func)])? $tuple_field)),*); - left.ast_eq(&right) - } else { - false - } - } - }; - ($other:expr, $name:ident, $struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? } ) => { - { - let left = ($(ast_eq_field!($(#[ast_eq_use($struct_convert_func)])? $struct_field)),*); - if let $name::$struct_variant { $($struct_field),* } = $other { - let right = ($(ast_eq_field!($(#[ast_eq_use($struct_convert_func)])? $struct_field)),*); - left.ast_eq(&right) - } else { - false - } - } - }; -} - -macro_rules! wrap_in_box { - ($stmt:expr, $loc:expr) => { - if !matches!(**$stmt, Statement::Block { .. }) { - Box::new(Statement::Block { - loc: $loc, - unchecked: false, - statements: vec![*$stmt.clone()], - }) - } else { - $stmt.clone() - } - }; -} - -impl AstEq for Statement { - fn ast_eq(&self, other: &Self) -> bool { - match self { - Self::If(loc, expr, stmt1, stmt2) => { - #[expect(clippy::borrowed_box)] - let wrap_if = |stmt1: &Box, stmt2: &Option>| { - ( - wrap_in_box!(stmt1, *loc), - stmt2.as_ref().map(|stmt2| { - if matches!(**stmt2, Self::If(..)) { - stmt2.clone() - } else { - wrap_in_box!(stmt2, *loc) - } - }), - ) - }; - let (stmt1, stmt2) = wrap_if(stmt1, stmt2); - let left = (loc, expr, &stmt1, &stmt2); - if let Self::If(loc, expr, stmt1, stmt2) = other { - let (stmt1, stmt2) = wrap_if(stmt1, stmt2); - let right = (loc, expr, &stmt1, &stmt2); - left.ast_eq(&right) - } else { - false - } - } - Self::While(loc, expr, stmt1) => { - let stmt1 = wrap_in_box!(stmt1, *loc); - let left = (loc, expr, &stmt1); - if let Self::While(loc, expr, stmt1) = other { - let stmt1 = wrap_in_box!(stmt1, *loc); - let right = (loc, expr, &stmt1); - left.ast_eq(&right) - } else { - false - } - } - Self::DoWhile(loc, stmt1, expr) => { - let stmt1 = wrap_in_box!(stmt1, *loc); - let left = (loc, &stmt1, expr); - if let Self::DoWhile(loc, stmt1, expr) = other { - let stmt1 = wrap_in_box!(stmt1, *loc); - let right = (loc, &stmt1, expr); - left.ast_eq(&right) - } else { - false - } - } - Self::For(loc, stmt1, expr, stmt2, stmt3) => { - let stmt3 = stmt3.as_ref().map(|stmt3| wrap_in_box!(stmt3, *loc)); - let left = (loc, stmt1, expr, stmt2, &stmt3); - if let Self::For(loc, stmt1, expr, stmt2, stmt3) = other { - let stmt3 = stmt3.as_ref().map(|stmt3| wrap_in_box!(stmt3, *loc)); - let right = (loc, stmt1, expr, stmt2, &stmt3); - left.ast_eq(&right) - } else { - false - } - } - Self::Try(loc, expr, returns, catch) => { - let left_returns = - returns.as_ref().map(|(params, stmt)| (filter_params(params), stmt)); - let left = (loc, expr, left_returns, catch); - if let Self::Try(loc, expr, returns, catch) = other { - let right_returns = - returns.as_ref().map(|(params, stmt)| (filter_params(params), stmt)); - let right = (loc, expr, right_returns, catch); - left.ast_eq(&right) - } else { - false - } - } - _ => gen_ast_eq_enum!(self, other, Statement { - _ - Args(loc, args), - Expression(loc, expr), - VariableDefinition(loc, decl, expr), - Continue(loc, ), - Break(loc, ), - Return(loc, expr), - Revert(loc, expr, expr2), - RevertNamedArgs(loc, expr, args), - Emit(loc, expr), - // provide overridden variants regardless - If(loc, expr, stmt1, stmt2), - While(loc, expr, stmt1), - DoWhile(loc, stmt1, expr), - For(loc, stmt1, expr, stmt2, stmt3), - Try(loc, expr, params, clause), - Error(loc) - _ - Block { - loc, - unchecked, - statements, - }, - Assembly { - loc, - dialect, - block, - flags, - }, - }), - } - } -} - -macro_rules! derive_ast_eq { - ($name:ident) => { - impl AstEq for $name { - fn ast_eq(&self, other: &Self) -> bool { - self == other - } - } - }; - (($($index:tt $gen:tt),*)) => { - impl < $( $gen ),* > AstEq for ($($gen,)*) where $($gen: AstEq),* { - fn ast_eq(&self, other: &Self) -> bool { - $( - if !self.$index.ast_eq(&other.$index) { - return false - } - )* - true - } - } - }; - (struct $name:ident { $($field:ident),* $(,)? }) => { - impl AstEq for $name { - fn ast_eq(&self, other: &Self) -> bool { - let $name { $($field),* } = self; - let left = ($($field),*); - let $name { $($field),* } = other; - let right = ($($field),*); - left.ast_eq(&right) - } - } - }; - (enum $name:ident { - $($unit_variant:ident),* $(,)? - _ - $($tuple_variant:ident ( $($(#[ast_eq_use($tuple_convert_func:ident)])? $tuple_field:ident),* $(,)? )),* $(,)? - _ - $($struct_variant:ident { $($(#[ast_eq_use($struct_convert_func:ident)])? $struct_field:ident),* $(,)? }),* $(,)? - }) => { - impl AstEq for $name { - fn ast_eq(&self, other: &Self) -> bool { - gen_ast_eq_enum!(self, other, $name { - $($unit_variant),* - _ - $($tuple_variant ( $($(#[ast_eq_use($tuple_convert_func)])? $tuple_field),* )),* - _ - $($struct_variant { $($(#[ast_eq_use($struct_convert_func)])? $struct_field),* }),* - }) - } - } - } -} - -derive_ast_eq! { (0 A) } -derive_ast_eq! { (0 A, 1 B) } -derive_ast_eq! { (0 A, 1 B, 2 C) } -derive_ast_eq! { (0 A, 1 B, 2 C, 3 D) } -derive_ast_eq! { (0 A, 1 B, 2 C, 3 D, 4 E) } -derive_ast_eq! { bool } -derive_ast_eq! { u8 } -derive_ast_eq! { u16 } -derive_ast_eq! { I256 } -derive_ast_eq! { U256 } -derive_ast_eq! { struct Identifier { loc, name } } -derive_ast_eq! { struct HexLiteral { loc, hex } } -derive_ast_eq! { struct StringLiteral { loc, unicode, string } } -derive_ast_eq! { struct Parameter { loc, annotation, ty, storage, name } } -derive_ast_eq! { struct NamedArgument { loc, name, expr } } -derive_ast_eq! { struct YulBlock { loc, statements } } -derive_ast_eq! { struct YulFunctionCall { loc, id, arguments } } -derive_ast_eq! { struct YulFunctionDefinition { loc, id, params, returns, body } } -derive_ast_eq! { struct YulSwitch { loc, condition, cases, default } } -derive_ast_eq! { struct YulFor { - loc, - init_block, - condition, - post_block, - execution_block, -}} -derive_ast_eq! { struct YulTypedIdentifier { loc, id, ty } } -derive_ast_eq! { struct VariableDeclaration { loc, ty, storage, name } } -derive_ast_eq! { struct Using { loc, list, ty, global } } -derive_ast_eq! { struct UsingFunction { loc, path, oper } } -derive_ast_eq! { struct TypeDefinition { loc, name, ty } } -derive_ast_eq! { struct ContractDefinition { loc, ty, name, base, parts } } -derive_ast_eq! { struct EventParameter { loc, ty, indexed, name } } -derive_ast_eq! { struct ErrorParameter { loc, ty, name } } -derive_ast_eq! { struct EventDefinition { loc, name, fields, anonymous } } -derive_ast_eq! { struct ErrorDefinition { loc, keyword, name, fields } } -derive_ast_eq! { struct StructDefinition { loc, name, fields } } -derive_ast_eq! { struct EnumDefinition { loc, name, values } } -derive_ast_eq! { struct Annotation { loc, id, value } } -derive_ast_eq! { enum UsingList { - Error, - _ - Library(expr), - Functions(exprs), - _ -}} -derive_ast_eq! { enum UserDefinedOperator { - BitwiseAnd, - BitwiseNot, - Negate, - BitwiseOr, - BitwiseXor, - Add, - Divide, - Modulo, - Multiply, - Subtract, - Equal, - More, - MoreEqual, - Less, - LessEqual, - NotEqual, - _ - _ -}} -derive_ast_eq! { enum Visibility { - _ - External(loc), - Public(loc), - Internal(loc), - Private(loc), - _ -}} -derive_ast_eq! { enum Mutability { - _ - Pure(loc), - View(loc), - Constant(loc), - Payable(loc), - _ -}} -derive_ast_eq! { enum FunctionAttribute { - _ - Mutability(muta), - Visibility(visi), - Virtual(loc), - Immutable(loc), - Override(loc, idents), - BaseOrModifier(loc, base), - Error(loc), - _ -}} -derive_ast_eq! { enum StorageLocation { - _ - Memory(loc), - Storage(loc), - Calldata(loc), - _ -}} -derive_ast_eq! { enum Type { - Address, - AddressPayable, - Payable, - Bool, - Rational, - DynamicBytes, - String, - _ - Int(int), - Uint(int), - Bytes(int), - _ - Mapping{ loc, key, key_name, value, value_name }, - Function { params, attributes, returns }, -}} -derive_ast_eq! { enum Expression { - _ - PostIncrement(loc, expr1), - PostDecrement(loc, expr1), - New(loc, expr1), - ArraySubscript(loc, expr1, expr2), - ArraySlice( - loc, - expr1, - expr2, - expr3, - ), - MemberAccess(loc, expr1, ident1), - FunctionCall(loc, expr1, exprs1), - FunctionCallBlock(loc, expr1, stmt), - NamedFunctionCall(loc, expr1, args), - Not(loc, expr1), - BitwiseNot(loc, expr1), - Delete(loc, expr1), - PreIncrement(loc, expr1), - PreDecrement(loc, expr1), - UnaryPlus(loc, expr1), - Negate(loc, expr1), - Power(loc, expr1, expr2), - Multiply(loc, expr1, expr2), - Divide(loc, expr1, expr2), - Modulo(loc, expr1, expr2), - Add(loc, expr1, expr2), - Subtract(loc, expr1, expr2), - ShiftLeft(loc, expr1, expr2), - ShiftRight(loc, expr1, expr2), - BitwiseAnd(loc, expr1, expr2), - BitwiseXor(loc, expr1, expr2), - BitwiseOr(loc, expr1, expr2), - Less(loc, expr1, expr2), - More(loc, expr1, expr2), - LessEqual(loc, expr1, expr2), - MoreEqual(loc, expr1, expr2), - Equal(loc, expr1, expr2), - NotEqual(loc, expr1, expr2), - And(loc, expr1, expr2), - Or(loc, expr1, expr2), - ConditionalOperator(loc, expr1, expr2, expr3), - Assign(loc, expr1, expr2), - AssignOr(loc, expr1, expr2), - AssignAnd(loc, expr1, expr2), - AssignXor(loc, expr1, expr2), - AssignShiftLeft(loc, expr1, expr2), - AssignShiftRight(loc, expr1, expr2), - AssignAdd(loc, expr1, expr2), - AssignSubtract(loc, expr1, expr2), - AssignMultiply(loc, expr1, expr2), - AssignDivide(loc, expr1, expr2), - AssignModulo(loc, expr1, expr2), - BoolLiteral(loc, bool1), - NumberLiteral(loc, #[ast_eq_use(to_num)] str1, #[ast_eq_use(to_num)] str2, unit), - RationalNumberLiteral( - loc, - #[ast_eq_use(to_num)] str1, - #[ast_eq_use(to_num_reversed)] str2, - #[ast_eq_use(to_num)] str3, - unit - ), - HexNumberLiteral(loc, str1, unit), - StringLiteral(strs1), - Type(loc, ty1), - HexLiteral(hexs1), - AddressLiteral(loc, str1), - Variable(ident1), - List(loc, params1), - ArrayLiteral(loc, exprs1), - Parenthesis(loc, expr) - _ -}} -derive_ast_eq! { enum CatchClause { - _ - Simple(param, ident, stmt), - Named(loc, ident, param, stmt), - _ -}} -derive_ast_eq! { enum YulStatement { - _ - Assign(loc, exprs, expr), - VariableDeclaration(loc, idents, expr), - If(loc, expr, block), - For(yul_for), - Switch(switch), - Leave(loc), - Break(loc), - Continue(loc), - Block(block), - FunctionDefinition(def), - FunctionCall(func), - Error(loc), - _ -}} -derive_ast_eq! { enum YulExpression { - _ - BoolLiteral(loc, boo, ident), - NumberLiteral(loc, string1, string2, ident), - HexNumberLiteral(loc, string, ident), - HexStringLiteral(hex, ident), - StringLiteral(string, ident), - Variable(ident), - FunctionCall(func), - SuffixAccess(loc, expr, ident), - _ -}} -derive_ast_eq! { enum YulSwitchOptions { - _ - Case(loc, expr, block), - Default(loc, block), - _ -}} -derive_ast_eq! { enum SourceUnitPart { - _ - ContractDefinition(def), - PragmaDirective(loc, ident, string), - ImportDirective(import), - EnumDefinition(def), - StructDefinition(def), - EventDefinition(def), - ErrorDefinition(def), - FunctionDefinition(def), - VariableDefinition(def), - TypeDefinition(def), - Using(using), - StraySemicolon(loc), - Annotation(annotation), - _ -}} -derive_ast_eq! { enum ImportPath { - _ - Filename(lit), - Path(path), - _ -}} -derive_ast_eq! { enum Import { - _ - Plain(string, loc), - GlobalSymbol(string, ident, loc), - Rename(string, idents, loc), - _ -}} -derive_ast_eq! { enum FunctionTy { - Constructor, - Function, - Fallback, - Receive, - Modifier, - _ - _ -}} -derive_ast_eq! { enum ContractPart { - _ - StructDefinition(def), - EventDefinition(def), - EnumDefinition(def), - ErrorDefinition(def), - VariableDefinition(def), - FunctionDefinition(def), - TypeDefinition(def), - StraySemicolon(loc), - Using(using), - Annotation(annotation), - _ -}} -derive_ast_eq! { enum ContractTy { - _ - Abstract(loc), - Contract(loc), - Interface(loc), - Library(loc), - _ -}} -derive_ast_eq! { enum VariableAttribute { - _ - Visibility(visi), - Constant(loc), - Immutable(loc), - Override(loc, idents), - _ -}} diff --git a/crates/fmt-2/src/solang_ext/loc.rs b/crates/fmt-2/src/solang_ext/loc.rs deleted file mode 100644 index 54bf771c6df90..0000000000000 --- a/crates/fmt-2/src/solang_ext/loc.rs +++ /dev/null @@ -1,156 +0,0 @@ -use solang_parser::pt; -use std::{borrow::Cow, rc::Rc, sync::Arc}; - -/// Returns the code location. -/// -/// Patched version of [`pt::CodeLocation`]: includes the block of a [`pt::FunctionDefinition`] in -/// its `loc`. -pub trait CodeLocationExt { - /// Returns the code location of `self`. - fn loc(&self) -> pt::Loc; -} - -impl CodeLocationExt for &T { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for &mut T { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Cow<'_, T> { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Box { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Rc { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -impl CodeLocationExt for Arc { - fn loc(&self) -> pt::Loc { - (**self).loc() - } -} - -// FunctionDefinition patch -impl CodeLocationExt for pt::FunctionDefinition { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - let mut loc = self.loc; - if let Some(ref body) = self.body { - loc.use_end_from(&pt::CodeLocation::loc(body)); - } - loc - } -} - -impl CodeLocationExt for pt::ContractPart { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - match self { - Self::FunctionDefinition(f) => f.loc(), - _ => pt::CodeLocation::loc(self), - } - } -} - -impl CodeLocationExt for pt::SourceUnitPart { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - match self { - Self::FunctionDefinition(f) => f.loc(), - _ => pt::CodeLocation::loc(self), - } - } -} - -impl CodeLocationExt for pt::ImportPath { - fn loc(&self) -> pt::Loc { - match self { - Self::Filename(s) => s.loc(), - Self::Path(i) => i.loc(), - } - } -} - -macro_rules! impl_delegate { - ($($t:ty),+ $(,)?) => {$( - impl CodeLocationExt for $t { - #[inline] - #[track_caller] - fn loc(&self) -> pt::Loc { - pt::CodeLocation::loc(self) - } - } - )+}; -} - -impl_delegate! { - pt::Annotation, - pt::Base, - pt::ContractDefinition, - pt::EnumDefinition, - pt::ErrorDefinition, - pt::ErrorParameter, - pt::EventDefinition, - pt::EventParameter, - // pt::FunctionDefinition, - pt::HexLiteral, - pt::Identifier, - pt::IdentifierPath, - pt::NamedArgument, - pt::Parameter, - // pt::SourceUnit, - pt::StringLiteral, - pt::StructDefinition, - pt::TypeDefinition, - pt::Using, - pt::UsingFunction, - pt::VariableDeclaration, - pt::VariableDefinition, - pt::YulBlock, - pt::YulFor, - pt::YulFunctionCall, - pt::YulFunctionDefinition, - pt::YulSwitch, - pt::YulTypedIdentifier, - - pt::CatchClause, - pt::Comment, - // pt::ContractPart, - pt::ContractTy, - pt::Expression, - pt::FunctionAttribute, - // pt::FunctionTy, - pt::Import, - pt::Loc, - pt::Mutability, - // pt::SourceUnitPart, - pt::Statement, - pt::StorageLocation, - // pt::Type, - // pt::UserDefinedOperator, - pt::UsingList, - pt::VariableAttribute, - // pt::Visibility, - pt::YulExpression, - pt::YulStatement, - pt::YulSwitchOptions, -} diff --git a/crates/fmt-2/src/solang_ext/mod.rs b/crates/fmt-2/src/solang_ext/mod.rs deleted file mode 100644 index aa4fe734ee64f..0000000000000 --- a/crates/fmt-2/src/solang_ext/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Extension traits and modules to the [`solang_parser`] crate. - -/// Same as [`solang_parser::pt`], but with the patched `CodeLocation`. -pub mod pt { - #[doc(no_inline)] - pub use super::loc::CodeLocationExt as CodeLocation; - - #[doc(no_inline)] - pub use solang_parser::pt::{ - Annotation, Base, CatchClause, Comment, ContractDefinition, ContractPart, ContractTy, - EnumDefinition, ErrorDefinition, ErrorParameter, EventDefinition, EventParameter, - Expression, FunctionAttribute, FunctionDefinition, FunctionTy, HexLiteral, Identifier, - IdentifierPath, Import, ImportPath, Loc, Mutability, NamedArgument, OptionalCodeLocation, - Parameter, ParameterList, SourceUnit, SourceUnitPart, Statement, StorageLocation, - StringLiteral, StructDefinition, Type, TypeDefinition, UserDefinedOperator, Using, - UsingFunction, UsingList, VariableAttribute, VariableDeclaration, VariableDefinition, - Visibility, YulBlock, YulExpression, YulFor, YulFunctionCall, YulFunctionDefinition, - YulStatement, YulSwitch, YulSwitchOptions, YulTypedIdentifier, - }; -} - -mod ast_eq; -mod loc; -mod safe_unwrap; - -pub use ast_eq::AstEq; -pub use loc::CodeLocationExt; -pub use safe_unwrap::SafeUnwrap; diff --git a/crates/fmt-2/src/solang_ext/safe_unwrap.rs b/crates/fmt-2/src/solang_ext/safe_unwrap.rs deleted file mode 100644 index fe2810ad9705a..0000000000000 --- a/crates/fmt-2/src/solang_ext/safe_unwrap.rs +++ /dev/null @@ -1,52 +0,0 @@ -use solang_parser::pt; - -/// Trait implemented to unwrap optional parse tree items initially introduced in -/// [hyperledger/solang#1068]. -/// -/// Note that the methods of this trait should only be used on parse tree items' fields, like -/// [pt::VariableDefinition] or [pt::EventDefinition], where the `name` field is `None` only when an -/// error occurred during parsing. -/// -/// [hyperledger/solang#1068]: https://github.com/hyperledger/solang/pull/1068 -pub trait SafeUnwrap { - /// See [SafeUnwrap]. - fn safe_unwrap(&self) -> &T; - - /// See [SafeUnwrap]. - fn safe_unwrap_mut(&mut self) -> &mut T; -} - -#[inline(never)] -#[cold] -#[track_caller] -fn invalid() -> ! { - panic!("invalid parse tree") -} - -macro_rules! impl_ { - ($($t:ty),+ $(,)?) => { - $( - impl SafeUnwrap<$t> for Option<$t> { - #[inline] - #[track_caller] - fn safe_unwrap(&self) -> &$t { - match *self { - Some(ref x) => x, - None => invalid(), - } - } - - #[inline] - #[track_caller] - fn safe_unwrap_mut(&mut self) -> &mut $t { - match *self { - Some(ref mut x) => x, - None => invalid(), - } - } - } - )+ - }; -} - -impl_!(pt::Identifier, pt::StringLiteral); diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs new file mode 100644 index 0000000000000..16a30c2c5ad74 --- /dev/null +++ b/crates/fmt-2/src/state.rs @@ -0,0 +1,176 @@ +use super::{ + comment::{Comment, CommentStyle}, + comments::Comments, + pp::{self, Breaks, Token}, +}; +use crate::{inline_config::InlineConfigItem, FormatterConfig, FormatterError, InlineConfig}; +use solar_parse::interface::{BytePos, Session}; +use std::path::Path; + +pub(super) struct State<'a> { + s: pp::Printer, + comments: Option>, + config: FormatterConfig, + inline_config: InlineConfig, +} + +impl std::ops::Deref for State<'_> { + type Target = pp::Printer; + + fn deref(&self) -> &Self::Target { + &self.s + } +} + +impl std::ops::DerefMut for State<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.s + } +} + +impl<'a> State<'a> { + pub(super) fn new( + config: FormatterConfig, + inline_config: InlineConfig, + comments: Option>, + ) -> Self { + Self { s: pp::Printer::new(), comments, inline_config, config } + } + + fn once(config: FormatterConfig) { + Self::new(config, InlineConfig::default(), None); + } + + fn comments(&self) -> Option<&Comments<'a>> { + self.comments.as_ref() + } + + fn comments_mut(&mut self) -> Option<&mut Comments<'a>> { + self.comments.as_mut() + } + + fn peek_comment<'b>(&'b self) -> Option<&'b Comment> + where + 'a: 'b, + { + self.comments().and_then(|c| c.peek()) + } + + fn next_comment(&mut self) -> Option { + self.comments_mut().and_then(|c| c.next()) + } + + fn strsep<'x, T: 'x, F, I>( + &mut self, + sep: &'static str, + space_before: bool, + b: Breaks, + elts: I, + mut op: F, + ) where + F: FnMut(&mut Self, &T), + I: IntoIterator, + { + let mut it = elts.into_iter(); + + self.rbox(0, b); + if let Some(first) = it.next() { + op(self, first); + for elt in it { + if space_before { + self.space(); + } + self.word_space(sep); + op(self, elt); + } + } + self.end(); + } + + fn commasep<'x, T: 'x, F, I>(&mut self, b: Breaks, elts: I, op: F) + where + F: FnMut(&mut Self, &T), + I: IntoIterator, + { + self.strsep(",", false, b, elts, op) + } + + fn maybe_print_comment(&mut self, pos: BytePos) -> bool { + let mut has_comment = false; + while let Some(cmnt) = self.peek_comment() { + if cmnt.pos() >= pos { + break; + } + has_comment = true; + let cmnt = self.next_comment().unwrap(); + self.print_comment(cmnt); + } + has_comment + } + + fn print_comment(&mut self, cmnt: Comment) { + match cmnt.style { + CommentStyle::Mixed => { + if !self.is_beginning_of_line() { + self.zerobreak(); + } + if let Some((last, lines)) = cmnt.lines.split_last() { + self.ibox(0); + + for line in lines { + self.word(line.clone()); + self.hardbreak() + } + + self.word(last.clone()); + self.space(); + + self.end(); + } + self.zerobreak() + } + CommentStyle::Isolated => { + self.hardbreak_if_not_bol(); + for line in &cmnt.lines { + // Don't print empty lines because they will end up as trailing + // whitespace. + if !line.is_empty() { + self.word(line.clone()); + } + self.hardbreak(); + } + } + CommentStyle::Trailing => { + if !self.is_beginning_of_line() { + self.word(" "); + } + if let [line] = cmnt.lines.as_slice() { + self.word(line.clone()); + self.hardbreak() + } else { + self.visual_align(); + for line in &cmnt.lines { + if !line.is_empty() { + self.word(line.clone()); + } + self.hardbreak(); + } + self.end(); + } + } + CommentStyle::BlankLine => { + // We need to do at least one, possibly two hardbreaks. + let twice = match self.last_token() { + Some(Token::String(s)) => ";" == s, + Some(Token::Begin(_)) => true, + Some(Token::End) => true, + _ => false, + }; + if twice { + self.hardbreak(); + } + self.hardbreak(); + } + } + } +} diff --git a/crates/fmt-2/src/string.rs b/crates/fmt-2/src/string.rs deleted file mode 100644 index ae570a39b827a..0000000000000 --- a/crates/fmt-2/src/string.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! Helpers for dealing with quoted strings - -/// The state of a character in a string with quotable components -/// This is a simplified version of the -/// [actual parser](https://docs.soliditylang.org/en/v0.8.15/grammar.html#a4.SolidityLexer.EscapeSequence) -/// as we don't care about hex or other character meanings -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum QuoteState { - /// Not currently in quoted string - #[default] - None, - /// The opening character of a quoted string - Opening(char), - /// A character in a quoted string - String(char), - /// The `\` in an escape sequence `"\n"` - Escaping(char), - /// The escaped character e.g. `n` in `"\n"` - Escaped(char), - /// The closing character - Closing(char), -} - -/// An iterator over characters and indices in a string slice with information about quoted string -/// states -pub struct QuoteStateCharIndices<'a> { - iter: std::str::CharIndices<'a>, - state: QuoteState, -} - -impl<'a> QuoteStateCharIndices<'a> { - fn new(string: &'a str) -> Self { - Self { iter: string.char_indices(), state: QuoteState::None } - } - pub fn with_state(mut self, state: QuoteState) -> Self { - self.state = state; - self - } -} - -impl Iterator for QuoteStateCharIndices<'_> { - type Item = (QuoteState, usize, char); - fn next(&mut self) -> Option { - let (idx, ch) = self.iter.next()?; - match self.state { - QuoteState::None | QuoteState::Closing(_) => { - if ch == '\'' || ch == '"' { - self.state = QuoteState::Opening(ch); - } else { - self.state = QuoteState::None - } - } - QuoteState::String(quote) | QuoteState::Opening(quote) | QuoteState::Escaped(quote) => { - if ch == quote { - self.state = QuoteState::Closing(quote) - } else if ch == '\\' { - self.state = QuoteState::Escaping(quote) - } else { - self.state = QuoteState::String(quote) - } - } - QuoteState::Escaping(quote) => self.state = QuoteState::Escaped(quote), - } - Some((self.state, idx, ch)) - } -} - -/// An iterator over the indices of quoted string locations -pub struct QuotedRanges<'a>(QuoteStateCharIndices<'a>); - -impl QuotedRanges<'_> { - pub fn with_state(mut self, state: QuoteState) -> Self { - self.0 = self.0.with_state(state); - self - } -} - -impl Iterator for QuotedRanges<'_> { - type Item = (char, usize, usize); - fn next(&mut self) -> Option { - let (quote, start) = loop { - let (state, idx, _) = self.0.next()?; - match state { - QuoteState::Opening(quote) | - QuoteState::Escaping(quote) | - QuoteState::Escaped(quote) | - QuoteState::String(quote) => break (quote, idx), - QuoteState::Closing(quote) => return Some((quote, idx, idx)), - QuoteState::None => {} - } - }; - for (state, idx, _) in self.0.by_ref() { - if matches!(state, QuoteState::Closing(_)) { - return Some((quote, start, idx)) - } - } - None - } -} - -/// Helpers for iterating over quoted strings -pub trait QuotedStringExt { - /// Returns an iterator of characters, indices and their quoted string state. - fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_>; - - /// Returns an iterator of quoted string ranges. - fn quoted_ranges(&self) -> QuotedRanges<'_> { - QuotedRanges(self.quote_state_char_indices()) - } - - /// Check to see if a string is quoted. This will return true if the first character - /// is a quote and the last character is a quote with no non-quoted sections in between. - fn is_quoted(&self) -> bool { - let mut iter = self.quote_state_char_indices(); - if !matches!(iter.next(), Some((QuoteState::Opening(_), _, _))) { - return false - } - while let Some((state, _, _)) = iter.next() { - if matches!(state, QuoteState::Closing(_)) { - return iter.next().is_none() - } - } - false - } -} - -impl QuotedStringExt for T -where - T: AsRef, -{ - fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_> { - QuoteStateCharIndices::new(self.as_ref()) - } -} - -impl QuotedStringExt for str { - fn quote_state_char_indices(&self) -> QuoteStateCharIndices<'_> { - QuoteStateCharIndices::new(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use similar_asserts::assert_eq; - - #[test] - fn quote_state_char_indices() { - assert_eq!( - r#"a'a"\'\"\n\\'a"#.quote_state_char_indices().collect::>(), - vec![ - (QuoteState::None, 0, 'a'), - (QuoteState::Opening('\''), 1, '\''), - (QuoteState::String('\''), 2, 'a'), - (QuoteState::String('\''), 3, '"'), - (QuoteState::Escaping('\''), 4, '\\'), - (QuoteState::Escaped('\''), 5, '\''), - (QuoteState::Escaping('\''), 6, '\\'), - (QuoteState::Escaped('\''), 7, '"'), - (QuoteState::Escaping('\''), 8, '\\'), - (QuoteState::Escaped('\''), 9, 'n'), - (QuoteState::Escaping('\''), 10, '\\'), - (QuoteState::Escaped('\''), 11, '\\'), - (QuoteState::Closing('\''), 12, '\''), - (QuoteState::None, 13, 'a'), - ] - ); - } - - #[test] - fn quoted_ranges() { - let string = r#"testing "double quoted" and 'single quoted' strings"#; - assert_eq!( - string - .quoted_ranges() - .map(|(quote, start, end)| (quote, &string[start..=end])) - .collect::>(), - vec![('"', r#""double quoted""#), ('\'', "'single quoted'")] - ); - } -} diff --git a/crates/fmt-2/src/visit.rs b/crates/fmt-2/src/visit.rs deleted file mode 100644 index db6287b72209d..0000000000000 --- a/crates/fmt-2/src/visit.rs +++ /dev/null @@ -1,642 +0,0 @@ -//! Visitor helpers to traverse the [solang Solidity Parse Tree](solang_parser::pt). - -use crate::solang_ext::pt::*; - -/// A trait that is invoked while traversing the Solidity Parse Tree. -/// Each method of the [Visitor] trait is a hook that can be potentially overridden. -/// -/// Currently the main implementer of this trait is the [`Formatter`](crate::Formatter<'_>) struct. -pub trait Visitor { - type Error: std::error::Error; - - fn visit_source(&mut self, _loc: Loc) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_source_unit(&mut self, _source_unit: &mut SourceUnit) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_contract(&mut self, _contract: &mut ContractDefinition) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_annotation(&mut self, annotation: &mut Annotation) -> Result<(), Self::Error> { - self.visit_source(annotation.loc) - } - - fn visit_pragma( - &mut self, - loc: Loc, - _ident: &mut Option, - _str: &mut Option, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_import_plain( - &mut self, - _loc: Loc, - _import: &mut ImportPath, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_import_global( - &mut self, - _loc: Loc, - _global: &mut ImportPath, - _alias: &mut Identifier, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_import_renames( - &mut self, - _loc: Loc, - _imports: &mut [(Identifier, Option)], - _from: &mut ImportPath, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_enum(&mut self, _enum: &mut EnumDefinition) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_assembly( - &mut self, - loc: Loc, - _dialect: &mut Option, - _block: &mut YulBlock, - _flags: &mut Option>, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_block( - &mut self, - loc: Loc, - _unchecked: bool, - _statements: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_args(&mut self, loc: Loc, _args: &mut Vec) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - /// Don't write semicolon at the end because expressions can appear as both - /// part of other node and a statement in the function body - fn visit_expr(&mut self, loc: Loc, _expr: &mut Expression) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_ident(&mut self, loc: Loc, _ident: &mut Identifier) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_ident_path(&mut self, idents: &mut IdentifierPath) -> Result<(), Self::Error> { - self.visit_source(idents.loc) - } - - fn visit_emit(&mut self, loc: Loc, _event: &mut Expression) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_var_definition(&mut self, var: &mut VariableDefinition) -> Result<(), Self::Error> { - self.visit_source(var.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_var_definition_stmt( - &mut self, - loc: Loc, - _declaration: &mut VariableDeclaration, - _expr: &mut Option, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon() - } - - fn visit_var_declaration(&mut self, var: &mut VariableDeclaration) -> Result<(), Self::Error> { - self.visit_source(var.loc) - } - - fn visit_return( - &mut self, - loc: Loc, - _expr: &mut Option, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_revert( - &mut self, - loc: Loc, - _error: &mut Option, - _args: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_revert_named_args( - &mut self, - loc: Loc, - _error: &mut Option, - _args: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_break(&mut self, loc: Loc, _semicolon: bool) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_continue(&mut self, loc: Loc, _semicolon: bool) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - #[expect(clippy::type_complexity)] - fn visit_try( - &mut self, - loc: Loc, - _expr: &mut Expression, - _returns: &mut Option<(Vec<(Loc, Option)>, Box)>, - _clauses: &mut Vec, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_if( - &mut self, - loc: Loc, - _cond: &mut Expression, - _if_branch: &mut Box, - _else_branch: &mut Option>, - _is_first_stmt: bool, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_do_while( - &mut self, - loc: Loc, - _body: &mut Statement, - _cond: &mut Expression, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_while( - &mut self, - loc: Loc, - _cond: &mut Expression, - _body: &mut Statement, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_for( - &mut self, - loc: Loc, - _init: &mut Option>, - _cond: &mut Option>, - _update: &mut Option>, - _body: &mut Option>, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_function(&mut self, func: &mut FunctionDefinition) -> Result<(), Self::Error> { - self.visit_source(func.loc())?; - if func.body.is_none() { - self.visit_stray_semicolon()?; - } - - Ok(()) - } - - fn visit_function_attribute( - &mut self, - attribute: &mut FunctionAttribute, - ) -> Result<(), Self::Error> { - self.visit_source(attribute.loc())?; - Ok(()) - } - - fn visit_var_attribute( - &mut self, - attribute: &mut VariableAttribute, - ) -> Result<(), Self::Error> { - self.visit_source(attribute.loc())?; - Ok(()) - } - - fn visit_base(&mut self, base: &mut Base) -> Result<(), Self::Error> { - self.visit_source(base.loc) - } - - fn visit_parameter(&mut self, parameter: &mut Parameter) -> Result<(), Self::Error> { - self.visit_source(parameter.loc) - } - - fn visit_struct(&mut self, structure: &mut StructDefinition) -> Result<(), Self::Error> { - self.visit_source(structure.loc)?; - - Ok(()) - } - - fn visit_event(&mut self, event: &mut EventDefinition) -> Result<(), Self::Error> { - self.visit_source(event.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_event_parameter(&mut self, param: &mut EventParameter) -> Result<(), Self::Error> { - self.visit_source(param.loc) - } - - fn visit_error(&mut self, error: &mut ErrorDefinition) -> Result<(), Self::Error> { - self.visit_source(error.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_error_parameter(&mut self, param: &mut ErrorParameter) -> Result<(), Self::Error> { - self.visit_source(param.loc) - } - - fn visit_type_definition(&mut self, def: &mut TypeDefinition) -> Result<(), Self::Error> { - self.visit_source(def.loc) - } - - fn visit_stray_semicolon(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_opening_paren(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_closing_paren(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_newline(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - fn visit_using(&mut self, using: &mut Using) -> Result<(), Self::Error> { - self.visit_source(using.loc)?; - self.visit_stray_semicolon()?; - - Ok(()) - } - - fn visit_yul_block( - &mut self, - loc: Loc, - _stmts: &mut Vec, - _attempt_single_line: bool, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_expr(&mut self, expr: &mut YulExpression) -> Result<(), Self::Error> { - self.visit_source(expr.loc()) - } - - fn visit_yul_assignment( - &mut self, - loc: Loc, - _exprs: &mut Vec, - _expr: &mut Option<&mut YulExpression>, - ) -> Result<(), Self::Error> - where - T: Visitable + CodeLocation, - { - self.visit_source(loc) - } - - fn visit_yul_for(&mut self, stmt: &mut YulFor) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_function_call(&mut self, stmt: &mut YulFunctionCall) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_fun_def(&mut self, stmt: &mut YulFunctionDefinition) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_if( - &mut self, - loc: Loc, - _expr: &mut YulExpression, - _block: &mut YulBlock, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_leave(&mut self, loc: Loc) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_switch(&mut self, stmt: &mut YulSwitch) -> Result<(), Self::Error> { - self.visit_source(stmt.loc) - } - - fn visit_yul_var_declaration( - &mut self, - loc: Loc, - _idents: &mut Vec, - _expr: &mut Option, - ) -> Result<(), Self::Error> { - self.visit_source(loc) - } - - fn visit_yul_typed_ident(&mut self, ident: &mut YulTypedIdentifier) -> Result<(), Self::Error> { - self.visit_source(ident.loc) - } - - fn visit_parser_error(&mut self, loc: Loc) -> Result<(), Self::Error> { - self.visit_source(loc) - } -} - -/// Visitable trait for [`solang_parser::pt`] types. -/// -/// All [`solang_parser::pt`] types, such as [Statement], should implement the [Visitable] trait -/// that accepts a trait [Visitor] implementation, which has various callback handles for Solidity -/// Parse Tree nodes. -/// -/// We want to take a `&mut self` to be able to implement some advanced features in the future such -/// as modifying the Parse Tree before formatting it. -pub trait Visitable { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor; -} - -impl Visitable for &mut T -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - T::visit(self, v) - } -} - -impl Visitable for Option -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - if let Some(inner) = self.as_mut() { - inner.visit(v) - } else { - Ok(()) - } - } -} - -impl Visitable for Box -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - T::visit(self, v) - } -} - -impl Visitable for Vec -where - T: Visitable, -{ - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - for item in self.iter_mut() { - item.visit(v)?; - } - Ok(()) - } -} - -impl Visitable for SourceUnitPart { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::ContractDefinition(contract) => v.visit_contract(contract), - Self::PragmaDirective(loc, ident, str) => v.visit_pragma(*loc, ident, str), - Self::ImportDirective(import) => import.visit(v), - Self::EnumDefinition(enumeration) => v.visit_enum(enumeration), - Self::StructDefinition(structure) => v.visit_struct(structure), - Self::EventDefinition(event) => v.visit_event(event), - Self::ErrorDefinition(error) => v.visit_error(error), - Self::FunctionDefinition(function) => v.visit_function(function), - Self::VariableDefinition(variable) => v.visit_var_definition(variable), - Self::TypeDefinition(def) => v.visit_type_definition(def), - Self::StraySemicolon(_) => v.visit_stray_semicolon(), - Self::Using(using) => v.visit_using(using), - Self::Annotation(annotation) => v.visit_annotation(annotation), - } - } -} - -impl Visitable for Import { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::Plain(import, loc) => v.visit_import_plain(*loc, import), - Self::GlobalSymbol(global, import_as, loc) => { - v.visit_import_global(*loc, global, import_as) - } - Self::Rename(from, imports, loc) => v.visit_import_renames(*loc, imports, from), - } - } -} - -impl Visitable for ContractPart { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::StructDefinition(structure) => v.visit_struct(structure), - Self::EventDefinition(event) => v.visit_event(event), - Self::ErrorDefinition(error) => v.visit_error(error), - Self::EnumDefinition(enumeration) => v.visit_enum(enumeration), - Self::VariableDefinition(variable) => v.visit_var_definition(variable), - Self::FunctionDefinition(function) => v.visit_function(function), - Self::TypeDefinition(def) => v.visit_type_definition(def), - Self::StraySemicolon(_) => v.visit_stray_semicolon(), - Self::Using(using) => v.visit_using(using), - Self::Annotation(annotation) => v.visit_annotation(annotation), - } - } -} - -impl Visitable for Statement { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::Block { loc, unchecked, statements } => { - v.visit_block(*loc, *unchecked, statements) - } - Self::Assembly { loc, dialect, block, flags } => { - v.visit_assembly(*loc, dialect, block, flags) - } - Self::Args(loc, args) => v.visit_args(*loc, args), - Self::If(loc, cond, if_branch, else_branch) => { - v.visit_if(*loc, cond, if_branch, else_branch, true) - } - Self::While(loc, cond, body) => v.visit_while(*loc, cond, body), - Self::Expression(loc, expr) => { - v.visit_expr(*loc, expr)?; - v.visit_stray_semicolon() - } - Self::VariableDefinition(loc, declaration, expr) => { - v.visit_var_definition_stmt(*loc, declaration, expr) - } - Self::For(loc, init, cond, update, body) => v.visit_for(*loc, init, cond, update, body), - Self::DoWhile(loc, body, cond) => v.visit_do_while(*loc, body, cond), - Self::Continue(loc) => v.visit_continue(*loc, true), - Self::Break(loc) => v.visit_break(*loc, true), - Self::Return(loc, expr) => v.visit_return(*loc, expr), - Self::Revert(loc, error, args) => v.visit_revert(*loc, error, args), - Self::RevertNamedArgs(loc, error, args) => v.visit_revert_named_args(*loc, error, args), - Self::Emit(loc, event) => v.visit_emit(*loc, event), - Self::Try(loc, expr, returns, clauses) => v.visit_try(*loc, expr, returns, clauses), - Self::Error(loc) => v.visit_parser_error(*loc), - } - } -} - -impl Visitable for Loc { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_source(*self) - } -} - -impl Visitable for Expression { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_expr(self.loc(), self) - } -} - -impl Visitable for Identifier { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_ident(self.loc, self) - } -} - -impl Visitable for VariableDeclaration { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_var_declaration(self) - } -} - -impl Visitable for YulBlock { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.visit_yul_block(self.loc, self.statements.as_mut(), false) - } -} - -impl Visitable for YulStatement { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - match self { - Self::Assign(loc, exprs, expr) => v.visit_yul_assignment(*loc, exprs, &mut Some(expr)), - Self::Block(block) => v.visit_yul_block(block.loc, block.statements.as_mut(), false), - Self::Break(loc) => v.visit_break(*loc, false), - Self::Continue(loc) => v.visit_continue(*loc, false), - Self::For(stmt) => v.visit_yul_for(stmt), - Self::FunctionCall(stmt) => v.visit_yul_function_call(stmt), - Self::FunctionDefinition(stmt) => v.visit_yul_fun_def(stmt), - Self::If(loc, expr, block) => v.visit_yul_if(*loc, expr, block), - Self::Leave(loc) => v.visit_yul_leave(*loc), - Self::Switch(stmt) => v.visit_yul_switch(stmt), - Self::VariableDeclaration(loc, idents, expr) => { - v.visit_yul_var_declaration(*loc, idents, expr) - } - Self::Error(loc) => v.visit_parser_error(*loc), - } - } -} - -macro_rules! impl_visitable { - ($type:ty, $func:ident) => { - impl Visitable for $type { - fn visit(&mut self, v: &mut V) -> Result<(), V::Error> - where - V: Visitor, - { - v.$func(self) - } - } - }; -} - -impl_visitable!(SourceUnit, visit_source_unit); -impl_visitable!(FunctionAttribute, visit_function_attribute); -impl_visitable!(VariableAttribute, visit_var_attribute); -impl_visitable!(Parameter, visit_parameter); -impl_visitable!(Base, visit_base); -impl_visitable!(EventParameter, visit_event_parameter); -impl_visitable!(ErrorParameter, visit_error_parameter); -impl_visitable!(IdentifierPath, visit_ident_path); -impl_visitable!(YulExpression, visit_yul_expr); -impl_visitable!(YulTypedIdentifier, visit_yul_typed_ident); From 4e7abc19403700232db93da0f6ffdab6e2941361 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 14 Apr 2025 22:31:43 +0200 Subject: [PATCH 04/54] add dbg from prettyplease --- crates/fmt-2/src/pp/mod.rs | 109 ++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 31 deletions(-) diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 6c094c2498ca4..65456d0f3088c 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -1,4 +1,5 @@ -// Adapted from https://github.com/dtolnay/prettyplease/blob/8eb8c14649aea32e810732bd4d64fe519e6b752a/src/algorithm.rs. +//! Adapted from [`rustc_ast_pretty`](https://github.com/rust-lang/rust/blob/07d3fd1d9b9c1f07475b96a9d168564bf528db68/compiler/rustc_ast_pretty/src/pp.rs) +//! and [`prettyplease`](https://github.com/dtolnay/prettyplease/blob/8eb8c14649aea32e810732bd4d64fe519e6b752a/src/algorithm.rs). use ring::RingBuffer; use std::{borrow::Cow, cmp, collections::VecDeque, iter}; @@ -70,8 +71,8 @@ pub(crate) enum Token { #[derive(Copy, Clone)] enum PrintFrame { - Fits, - Broken { indent: usize, breaks: Breaks }, + Fits(Breaks), + Broken(usize, Breaks), } const SIZE_INFINITY: isize = 0xffff; @@ -98,7 +99,7 @@ pub struct Printer { /// Level of indentation of current line indent: usize, /// Buffered indentation to avoid writing trailing whitespace - pending_indentation: isize, + pending_indentation: usize, /// The token most recently popped from the left boundary of the /// ring-buffer for printing last_printed: Option, @@ -189,7 +190,7 @@ impl Printer { } pub(crate) fn offset(&mut self, offset: isize) { - if let Some(BufEntry { token: Token::Break(token), .. }) = &mut self.buf.last_mut() { + if let Some(BufEntry { token: Token::Break(token), .. }) = self.buf.last_mut() { token.offset += offset; } } @@ -262,15 +263,30 @@ impl Printer { } fn get_top(&self) -> PrintFrame { - *self - .print_stack - .last() - .unwrap_or(&PrintFrame::Broken { indent: 0, breaks: Breaks::Inconsistent }) + *self.print_stack.last().unwrap_or(&PrintFrame::Broken(0, Breaks::Inconsistent)) } fn print_begin(&mut self, token: BeginToken, size: isize) { + if DEBUG { + self.out.push(match token.breaks { + Breaks::Consistent => '«', + Breaks::Inconsistent => '‹', + }); + // TODO(dani) + /* + if DEBUG_INDENT { + self.out.extend(token.offset.to_string().chars().map(|ch| match ch { + '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] + [(ch as u8 - b'0') as usize], + '-' => '₋', + _ => unreachable!(), + })); + } + */ + } + if size > self.space { - self.print_stack.push(PrintFrame::Broken { indent: self.indent, breaks: token.breaks }); + self.print_stack.push(PrintFrame::Broken(self.indent, token.breaks)); self.indent = match token.indent { IndentStyle::Block { offset } => { usize::try_from(self.indent as isize + offset).unwrap() @@ -278,48 +294,79 @@ impl Printer { IndentStyle::Visual => (MARGIN - self.space) as usize, }; } else { - self.print_stack.push(PrintFrame::Fits); + self.print_stack.push(PrintFrame::Fits(token.breaks)); } } fn print_end(&mut self) { - if let PrintFrame::Broken { indent, .. } = self.print_stack.pop().unwrap() { - self.indent = indent; + let breaks = match self.print_stack.pop().unwrap() { + PrintFrame::Broken(indent, breaks) => { + self.indent = indent; + breaks + } + PrintFrame::Fits(breaks) => breaks, + }; + if DEBUG { + self.out.push(match breaks { + Breaks::Consistent => '»', + Breaks::Inconsistent => '›', + }); } } fn print_break(&mut self, token: BreakToken, size: isize) { - let fits = match self.get_top() { - PrintFrame::Fits => true, - PrintFrame::Broken { breaks: Breaks::Consistent, .. } => false, - PrintFrame::Broken { breaks: Breaks::Inconsistent, .. } => size <= self.space, - }; + let fits = /* token.never_break || */ + match self.get_top() { + PrintFrame::Fits(..) => true, + PrintFrame::Broken(.., Breaks::Consistent) => false, + PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space, + }; if fits { - self.pending_indentation += token.blank_space; - self.space -= token.blank_space; + self.pending_indentation += usize::try_from(token.blank_space).unwrap(); + self.space -= token.blank_space as isize; + // TODO(dani) + /* + if let Some(no_break) = token.no_break { + self.out.push(no_break); + self.space -= no_break.len_utf8() as isize; + } + */ + if DEBUG { + self.out.push('·'); + } } else { if let Some(pre_break) = token.pre_break { + self.print_indent(); self.out.push(pre_break); } + if DEBUG { + self.out.push('·'); + } self.out.push('\n'); let indent = self.indent as isize + token.offset; - self.pending_indentation = indent; + self.pending_indentation = usize::try_from(indent).unwrap(); + // TODO(dani): config self.space = cmp::max(MARGIN - indent, MIN_SPACE); + // TODO(dani) + /* + if !token.post_break.is_empty() { + self.print_indent(); + self.out.push_str(token.post_break); + self.space -= token.post_break.len() as isize; + } + */ } } fn print_string(&mut self, string: &str) { - // Write the pending indent. A more concise way of doing this would be: - // - // write!(self.out, "{: >n$}", "", n = self.pending_indentation as usize)?; - // - // But that is significantly slower. This code is sufficiently hot, and indents can get - // sufficiently large, that the difference is significant on some workloads. - self.out.reserve(self.pending_indentation as usize); - self.out.extend(iter::repeat(' ').take(self.pending_indentation as usize)); - self.pending_indentation = 0; - + self.print_indent(); self.out.push_str(string); self.space -= string.len() as isize; } + + fn print_indent(&mut self) { + self.out.reserve(self.pending_indentation); + self.out.extend(iter::repeat(' ').take(self.pending_indentation)); + self.pending_indentation = 0; + } } From 350332a02ca8763d962b7bd9781ca06d3bb934aa Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:45:52 +0200 Subject: [PATCH 05/54] wip --- Cargo.lock | 1 - a.rs | 4 + a.sol | 3 + crates/fmt-2/Cargo.toml | 2 +- crates/fmt-2/src/comments.rs | 20 +- crates/fmt-2/src/lib.rs | 32 ++- crates/fmt-2/src/pp/convenience.rs | 10 +- crates/fmt-2/src/pp/helpers.rs | 6 +- crates/fmt-2/src/pp/mod.rs | 16 +- crates/fmt-2/src/pp/ring.rs | 1 + crates/fmt-2/src/state.rs | 304 ++++++++++++++++++++++++++--- 11 files changed, 329 insertions(+), 70 deletions(-) create mode 100644 a.rs create mode 100644 a.sol diff --git a/Cargo.lock b/Cargo.lock index 13863b854360f..393aaf40cd363 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3571,7 +3571,6 @@ dependencies = [ name = "forge-fmt-2" version = "1.1.0" dependencies = [ - "alloy-primitives", "foundry-config", "itertools 0.14.0", "similar-asserts", diff --git a/a.rs b/a.rs new file mode 100644 index 0000000000000..170118b26f79c --- /dev/null +++ b/a.rs @@ -0,0 +1,4 @@ +/// Doc +// Com +/// Doc2 +fn main() {} diff --git a/a.sol b/a.sol new file mode 100644 index 0000000000000..8440aeec25883 --- /dev/null +++ b/a.sol @@ -0,0 +1,3 @@ +import {a}from"path.sol"; +// import {a,b,c} from "path.sol"; +// import "a"; diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml index 30cda0270703e..9c83f16456379 100644 --- a/crates/fmt-2/Cargo.toml +++ b/crates/fmt-2/Cargo.toml @@ -17,7 +17,7 @@ foundry-config.workspace = true solar-parse.workspace = true -alloy-primitives.workspace = true +# alloy-primitives.workspace = true itertools.workspace = true thiserror.workspace = true tracing.workspace = true diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index 623266b6573b2..e949ae54e3d13 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -5,8 +5,7 @@ use solar_parse::{ lexer::token::RawTokenKind as TokenKind, }; -pub struct Comments<'a> { - sm: &'a SourceMap, +pub struct Comments { // Stored in reverse order so we can consume them by popping. reversed_comments: Vec, } @@ -146,11 +145,11 @@ fn gather_comments(sf: &SourceFile) -> Vec { comments } -impl<'a> Comments<'a> { - pub fn new(sm: &'a SourceMap, sf: &SourceFile) -> Self { +impl Comments { + pub fn new(sf: &SourceFile) -> Self { let mut comments = gather_comments(sf); comments.reverse(); - Comments { sm, reversed_comments: comments } + Comments { reversed_comments: comments } } pub fn peek(&self) -> Option<&Comment> { @@ -165,13 +164,18 @@ impl<'a> Comments<'a> { self.reversed_comments.iter().rev() } - fn trailing_comment(&mut self, span: Span, next_pos: Option) -> Option { + pub fn trailing_comment( + &mut self, + sm: &SourceMap, + span: Span, + next_pos: Option, + ) -> Option { if let Some(cmnt) = self.peek() { if cmnt.style != CommentStyle::Trailing { return None; } - let span_line = self.sm.lookup_char_pos(span.hi()); - let comment_line = self.sm.lookup_char_pos(cmnt.pos()); + let span_line = sm.lookup_char_pos(span.hi()); + let comment_line = sm.lookup_char_pos(cmnt.pos()); let next = next_pos.unwrap_or_else(|| cmnt.pos() + BytePos(1)); if span.hi() < cmnt.pos() && cmnt.pos() < next && span_line.line == comment_line.line { return Some(self.next().unwrap()); diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 84b51485ba2b5..5d375df0fbc79 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -2,8 +2,10 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#[macro_use] -extern crate tracing; +// TODO(dani) +// #[macro_use] +// extern crate tracing; +use tracing as _; pub mod inline_config; pub use inline_config::InlineConfig; @@ -28,7 +30,7 @@ type Result = std::result::Result; pub enum FormatterError { #[error(transparent)] Io(#[from] std::io::Error), - #[error(transparent)] + #[error("\n{0}")] Parse(#[from] solar_parse::interface::diagnostics::EmittedDiagnostics), } @@ -52,30 +54,18 @@ pub fn format_source(source: &str, path: Option<&Path>, config: FormatterConfig) .map_err(|e| sess.dcx.err(e.to_string()).emit())?; let mut parser = solar_parse::Parser::from_source_file(&sess, &arena, &file); let ast = parser.parse_file().map_err(|e| e.emit())?; - let comments = Comments::new(sess.source_map(), &file); + let comments = Comments::new(&file); let inline_config = parse_inline_config(&sess, &comments, source); - Ok(format_source_unit(&ast, config, inline_config, comments)) + + let mut state = state::State::new(sess.source_map(), config, inline_config, comments); + state.print_source_unit(&ast); + Ok(state.s.eof()) }); sess.emitted_errors().unwrap()?; Ok(res.unwrap()) } -fn format_source_unit( - source_unit: &solar_parse::ast::SourceUnit<'_>, - config: FormatterConfig, - inline_config: InlineConfig, - comments: Comments<'_>, -) -> String { - let mut state = state::State::new(config, inline_config, Some(comments)); - // state.source_unit(source_unit); - // state.eof() - // TODO(dani) - let _ = source_unit; - let _ = &mut state; - todo!() -} - -fn parse_inline_config(sess: &Session, comments: &Comments<'_>, src: &str) -> InlineConfig { +fn parse_inline_config(sess: &Session, comments: &Comments, src: &str) -> InlineConfig { let items = comments.iter().filter_map(|comment| { let item = comment.lines.first()?.trim_start().strip_prefix("forgefmt:")?.trim(); let span = comment.span; diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 225ec683be596..a5c331c1f6cd3 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -33,9 +33,13 @@ impl Printer { self.scan_end() } - pub fn word>>(&mut self, wrd: S) { - let string = wrd.into(); - self.scan_string(string) + pub fn eof(mut self) -> String { + self.scan_eof(); + self.out + } + + pub fn word(&mut self, w: impl Into>) { + self.scan_string(w.into()) } fn spaces(&mut self, n: usize) { diff --git a/crates/fmt-2/src/pp/helpers.rs b/crates/fmt-2/src/pp/helpers.rs index dbdf9914e044c..07c7032948e3f 100644 --- a/crates/fmt-2/src/pp/helpers.rs +++ b/crates/fmt-2/src/pp/helpers.rs @@ -2,7 +2,7 @@ use super::Printer; use std::borrow::Cow; impl Printer { - pub fn word_space>>(&mut self, w: W) { + pub fn word_space(&mut self, w: impl Into>) { self.word(w); self.space(); } @@ -31,9 +31,9 @@ impl Printer { self.word(" ") } - pub fn word_nbsp>>(&mut self, w: S) { + pub fn word_nbsp(&mut self, w: impl Into>) { self.word(w); - self.nbsp() + self.nbsp(); } /// Synthesizes a comment that was not textually present in the original diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 65456d0f3088c..247865671cd02 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -23,13 +23,13 @@ const INDENT: isize = 4; const MIN_SPACE: isize = 60; /// How to break. Described in more detail in the module docs. -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Breaks { Consistent, Inconsistent, } -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] enum IndentStyle { /// Vertically aligned under whatever column this block begins at. /// @@ -45,20 +45,20 @@ enum IndentStyle { Block { offset: isize }, } -#[derive(Clone, Copy, Default, PartialEq)] +#[derive(Debug, Clone, Copy, Default, PartialEq)] pub(crate) struct BreakToken { offset: isize, blank_space: isize, pre_break: Option, } -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub(crate) struct BeginToken { indent: IndentStyle, breaks: Breaks, } -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub(crate) enum Token { // In practice a string token contains either a `&'static str` or a // `String`. `Cow` is overkill for this because we never modify the data, @@ -69,7 +69,7 @@ pub(crate) enum Token { End, } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] enum PrintFrame { Fits(Breaks), Broken(usize, Breaks), @@ -77,6 +77,7 @@ enum PrintFrame { const SIZE_INFINITY: isize = 0xffff; +#[derive(Debug)] pub struct Printer { out: String, /// Number of spaces left on line @@ -105,6 +106,7 @@ pub struct Printer { last_printed: Option, } +#[derive(Debug)] struct BufEntry { token: Token, size: isize, @@ -263,7 +265,7 @@ impl Printer { } fn get_top(&self) -> PrintFrame { - *self.print_stack.last().unwrap_or(&PrintFrame::Broken(0, Breaks::Inconsistent)) + self.print_stack.last().copied().unwrap_or(PrintFrame::Broken(0, Breaks::Inconsistent)) } fn print_begin(&mut self, token: BeginToken, size: isize) { diff --git a/crates/fmt-2/src/pp/ring.rs b/crates/fmt-2/src/pp/ring.rs index c3ad88f788dc5..ce10adefda4d9 100644 --- a/crates/fmt-2/src/pp/ring.rs +++ b/crates/fmt-2/src/pp/ring.rs @@ -13,6 +13,7 @@ use std::{ /// Holding a RingBuffer whose view is elements left..right gives the ability to /// use Index and IndexMut to access elements i in the infinitely long queue for /// which left <= i < right. +#[derive(Debug)] pub(super) struct RingBuffer { data: VecDeque, // Abstract index of data[0] in the infinitely sized queue. diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 16a30c2c5ad74..6d8157319ef31 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -3,13 +3,22 @@ use super::{ comments::Comments, pp::{self, Breaks, Token}, }; -use crate::{inline_config::InlineConfigItem, FormatterConfig, FormatterError, InlineConfig}; -use solar_parse::interface::{BytePos, Session}; -use std::path::Path; +use crate::{FormatterConfig, InlineConfig}; +use foundry_config::fmt as config; +use itertools::Itertools; +use solar_parse::{ + ast::{self, token, Span}, + interface::{BytePos, SourceMap}, +}; +use std::borrow::Cow; + +// TODO(dani): config +const INDENT_UNIT: isize = 4; pub(super) struct State<'a> { - s: pp::Printer, - comments: Option>, + pub(crate) s: pp::Printer, + sm: &'a SourceMap, + comments: Comments, config: FormatterConfig, inline_config: InlineConfig, } @@ -28,36 +37,23 @@ impl std::ops::DerefMut for State<'_> { } } +/// Generic methods. impl<'a> State<'a> { pub(super) fn new( + sm: &'a SourceMap, config: FormatterConfig, inline_config: InlineConfig, - comments: Option>, + comments: Comments, ) -> Self { - Self { s: pp::Printer::new(), comments, inline_config, config } - } - - fn once(config: FormatterConfig) { - Self::new(config, InlineConfig::default(), None); - } - - fn comments(&self) -> Option<&Comments<'a>> { - self.comments.as_ref() + Self { s: pp::Printer::new(), sm, comments, inline_config, config } } - fn comments_mut(&mut self) -> Option<&mut Comments<'a>> { - self.comments.as_mut() + fn comments(&self) -> &Comments { + &self.comments } - fn peek_comment<'b>(&'b self) -> Option<&'b Comment> - where - 'a: 'b, - { - self.comments().and_then(|c| c.peek()) - } - - fn next_comment(&mut self) -> Option { - self.comments_mut().and_then(|c| c.next()) + fn comments_mut(&mut self) -> &mut Comments { + &mut self.comments } fn strsep<'x, T: 'x, F, I>( @@ -173,4 +169,260 @@ impl<'a> State<'a> { } } } + + fn peek_comment<'b>(&'b self) -> Option<&'b Comment> + where + 'a: 'b, + { + self.comments().peek() + } + + fn next_comment(&mut self) -> Option { + self.comments_mut().next() + } + + fn maybe_print_trailing_comment(&mut self, span: Span, next_pos: Option) { + if let Some(cmnt) = self.comments.trailing_comment(self.sm, span, next_pos) { + self.print_comment(cmnt); + } + } + + fn print_remaining_comments(&mut self) { + // If there aren't any remaining comments, then we need to manually + // make sure there is a line break at the end. + if self.peek_comment().is_none() { + self.hardbreak(); + } + while let Some(cmnt) = self.next_comment() { + self.print_comment(cmnt); + } + } + + fn bopen(&mut self) { + self.word("{"); + self.end(); // Close the head-box. + } + + fn bclose_maybe_open(&mut self, span: Span, empty: bool, close_box: bool) { + let has_comment = self.maybe_print_comment(span.hi()); + if !empty || has_comment { + self.break_offset_if_not_bol(1, -INDENT_UNIT); + } + self.word("}"); + if close_box { + self.end(); // Close the outer-box. + } + } + + fn bclose(&mut self, span: Span, empty: bool) { + let close_box = true; + self.bclose_maybe_open(span, empty, close_box) + } + + fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { + if !self.is_beginning_of_line() { + self.break_offset(n, off) + } else if off != 0 { + if let Some(last_token) = self.last_token_still_buffered() { + if last_token.is_hardbreak_tok() { + // We do something pretty sketchy here: tuck the nonzero + // offset-adjustment we were going to deposit along with the + // break into the previous hardbreak. + self.replace_last_token_still_buffered(pp::Printer::hardbreak_tok_offset(off)); + } + } + } + } +} + +/// Span to source. +impl State<'_> { + fn char_at(&self, pos: BytePos) -> char { + let res = self.sm.lookup_byte_offset(pos); + res.sf.src[res.pos.to_usize()..].chars().next().unwrap() + } +} + +/// Language-specific pretty printing. +impl State<'_> { + pub fn print_source_unit(&mut self, source_unit: &ast::SourceUnit<'_>) { + for item in source_unit.items.iter() { + self.print_item(item); + } + self.print_remaining_comments(); + } + + fn print_item(&mut self, item: &ast::Item<'_>) { + let ast::Item { docs, span, kind } = item; + self.hardbreak_if_not_bol(); + self.print_docs(docs); + self.maybe_print_comment(span.lo()); + self.cbox(0); + match kind { + ast::ItemKind::Pragma(ast::PragmaDirective { tokens }) => { + self.word("pragma "); + match tokens { + ast::PragmaTokens::Version(ident, semver_req) => { + self.print_ident(*ident); + self.nbsp(); + self.word(semver_req.to_string()); + } + ast::PragmaTokens::Custom(a, b) => { + self.print_ident_or_strlit(a); + if let Some(b) = b { + self.nbsp(); + self.print_ident_or_strlit(b); + } + } + ast::PragmaTokens::Verbatim(tokens) => { + self.print_tokens(tokens); + } + } + self.word(";"); + } + ast::ItemKind::Import(ast::ImportDirective { path, items }) => { + self.word("import "); + match items { + ast::ImportItems::Plain(ident) => { + self.print_ast_str_lit(path); + if let Some(ident) = ident { + self.nbsp(); + self.word("as "); + self.print_ident(*ident); + } + } + ast::ImportItems::Aliases(aliases) => { + self.bopen(); + self.commasep( + Breaks::Consistent, + aliases.iter(), + |this, (ident, alias)| { + this.print_ident(*ident); + if let Some(alias) = alias { + this.word("as "); + this.print_ident(*alias); + } + }, + ); + self.bclose(item.span, aliases.is_empty()); + self.nbsp(); + self.word("from "); + self.print_ast_str_lit(path); + } + ast::ImportItems::Glob(ident) => { + self.word("* as "); + self.print_ident(*ident); + self.word(" from "); + self.print_ast_str_lit(path); + } + } + self.word(";"); + } + ast::ItemKind::Using(using) => todo!("Using"), + ast::ItemKind::Contract(contract) => todo!("Contract"), + ast::ItemKind::Function(func) => todo!("Function"), + ast::ItemKind::Variable(var) => todo!("Variable"), + ast::ItemKind::Struct(strukt) => todo!("Struct"), + ast::ItemKind::Enum(enumm) => todo!("Enum"), + ast::ItemKind::Udvt(udvt) => todo!("Udvt"), + ast::ItemKind::Error(error) => todo!("Error"), + ast::ItemKind::Event(event) => todo!("Event"), + } + } + + fn print_docs(&mut self, docs: &ast::DocComments<'_>) { + for &ast::DocComment { kind, span, symbol } in docs.iter() { + self.maybe_print_comment(span.lo()); + self.word(match kind { + ast::CommentKind::Line => { + format!("///{symbol}") + } + ast::CommentKind::Block => { + format!("/**{symbol}*/") + } + }); + self.hardbreak(); + } + } + + fn print_ident_or_strlit(&mut self, value: &ast::IdentOrStrLit) { + match value { + ast::IdentOrStrLit::Ident(ident) => self.print_ident(*ident), + ast::IdentOrStrLit::StrLit(strlit) => self.print_ast_str_lit(strlit), + } + } + + fn print_tokens(&mut self, tokens: &[token::Token]) { + let s = tokens.iter().map(|t| self.token_to_string(t)).join(" ").to_string(); + self.word(s); + } + + fn token_to_string<'a>(&self, token: &'a token::Token) -> Cow<'a, str> { + match token.kind { + token::TokenKind::Literal(kind, sym) => match kind { + token::TokenLitKind::Str | + token::TokenLitKind::UnicodeStr | + token::TokenLitKind::HexStr => { + let kind = match kind { + token::TokenLitKind::Str => ast::StrKind::Str, + token::TokenLitKind::UnicodeStr => ast::StrKind::Unicode, + token::TokenLitKind::HexStr => ast::StrKind::Hex, + _ => unreachable!(), + }; + return Cow::Owned(self.str_lit_to_string(token.span, sym.as_str(), kind)); + } + token::TokenLitKind::Integer | + token::TokenLitKind::Rational | + token::TokenLitKind::Err(_) => {} + }, + _ => {} + } + Cow::Borrowed(token.as_str()) + } + + fn print_ident(&mut self, ident: ast::Ident) { + // TODO(dani): is this right? + self.maybe_print_comment(ident.span.lo()); + + self.word(ident.to_string()); + } + + /// Prints a raw AST string literal, which is unescaped. + fn print_ast_str_lit(&mut self, strlit: &ast::StrLit) { + self.print_str_lit_unescaped(strlit.span, strlit.value.as_str(), ast::StrKind::Str); + } + + fn print_str_lit(&mut self, span: Span, s: &str, kind: ast::StrKind) { + let s = self.str_lit_to_string(span, s, kind); + self.word(s); + } + fn print_str_lit_unescaped(&mut self, span: Span, s: &str, kind: ast::StrKind) { + let s = self.str_lit_to_string_unescaped(span, s, kind); + self.word(s); + } + + fn str_lit_to_string(&self, span: Span, s: &str, kind: ast::StrKind) -> String { + self.str_lit_to_string_inner(span, s.escape_debug(), kind) + } + fn str_lit_to_string_unescaped(&self, span: Span, s: &str, kind: ast::StrKind) -> String { + self.str_lit_to_string_inner(span, s, kind) + } + fn str_lit_to_string_inner( + &self, + span: Span, + s: impl std::fmt::Display, + kind: ast::StrKind, + ) -> String { + let prefix = match kind { + ast::StrKind::Str => "", + ast::StrKind::Unicode => "unicode", + ast::StrKind::Hex => "hex", + }; + let quote = match self.config.quote_style { + config::QuoteStyle::Double => '\"', + config::QuoteStyle::Single => '\'', + config::QuoteStyle::Preserve => self.char_at(span.lo() + prefix.len() as u32), + }; + format!("{prefix}{quote}{s}{quote}") + } } From 4b54bf91b161ee545499729c1158e49ba8a94f32 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 15 Apr 2025 21:26:05 +0200 Subject: [PATCH 06/54] fixes, pragma&imports --- a.sol | 6 +- crates/fmt-2/src/comments.rs | 3 +- crates/fmt-2/src/iter.rs | 31 +++++ crates/fmt-2/src/lib.rs | 3 + crates/fmt-2/src/main.rs | 4 + crates/fmt-2/src/pp/convenience.rs | 66 +++++++---- crates/fmt-2/src/pp/helpers.rs | 14 +-- crates/fmt-2/src/pp/mod.rs | 111 ++++++++++-------- crates/fmt-2/src/pp/ring.rs | 61 +++++----- crates/fmt-2/src/state.rs | 67 ++++++----- .../ImportDirective/bracket-spacing.fmt.sol | 4 +- crates/fmt/testdata/ImportDirective/fmt.sol | 4 +- .../fmt/testdata/ImportDirective/original.sol | 4 +- .../ImportDirective/preserve-quote.fmt.sol | 4 +- .../ImportDirective/single-quote.fmt.sol | 4 +- 15 files changed, 230 insertions(+), 156 deletions(-) create mode 100644 crates/fmt-2/src/iter.rs diff --git a/a.sol b/a.sol index 8440aeec25883..15da8825dbc32 100644 --- a/a.sol +++ b/a.sol @@ -1,3 +1,3 @@ -import {a}from"path.sol"; -// import {a,b,c} from "path.sol"; -// import "a"; +import {a}from"a.sol"; +import {a,b,c} from "a.sol"; +import "a.sol"; diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index e949ae54e3d13..91c6c96682638 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -149,13 +149,14 @@ impl Comments { pub fn new(sf: &SourceFile) -> Self { let mut comments = gather_comments(sf); comments.reverse(); - Comments { reversed_comments: comments } + Self { reversed_comments: comments } } pub fn peek(&self) -> Option<&Comment> { self.reversed_comments.last() } + #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Option { self.reversed_comments.pop() } diff --git a/crates/fmt-2/src/iter.rs b/crates/fmt-2/src/iter.rs new file mode 100644 index 0000000000000..98443d416f02f --- /dev/null +++ b/crates/fmt-2/src/iter.rs @@ -0,0 +1,31 @@ +use std::iter::Peekable; + +pub(crate) struct Delimited { + is_first: bool, + iter: Peekable, +} + +pub(crate) trait IterDelimited: Iterator + Sized { + fn delimited(self) -> Delimited { + Delimited { is_first: true, iter: self.peekable() } + } +} + +impl IterDelimited for I {} + +pub(crate) struct IteratorPosition { + pub(crate) is_first: bool, + pub(crate) is_last: bool, +} + +impl Iterator for Delimited { + type Item = (IteratorPosition, I::Item); + + fn next(&mut self) -> Option { + let item = self.iter.next()?; + let position = + IteratorPosition { is_first: self.is_first, is_last: self.iter.peek().is_none() }; + self.is_first = false; + Some((position, item)) + } +} diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 5d375df0fbc79..4d43caee870f3 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![allow(dead_code)] // TODO(dani) // TODO(dani) // #[macro_use] @@ -17,6 +18,8 @@ pub use comments::Comments; mod state; +pub(crate) mod iter; + mod pp; use solar_parse::interface::Session; diff --git a/crates/fmt-2/src/main.rs b/crates/fmt-2/src/main.rs index cc040ab0d8d37..b9a406d40236b 100644 --- a/crates/fmt-2/src/main.rs +++ b/crates/fmt-2/src/main.rs @@ -1,3 +1,7 @@ +// TODO(dani): tmp for testing + +#![allow(dead_code, clippy::disallowed_macros)] + use std::{io::Read, path::PathBuf}; fn main() { diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index a5c331c1f6cd3..0975151e68a1c 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -4,17 +4,17 @@ use std::borrow::Cow; impl Printer { /// "raw box" pub fn rbox(&mut self, indent: isize, breaks: Breaks) { - self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks }) + self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks }); } /// Inconsistent breaking box pub fn ibox(&mut self, indent: isize) { - self.rbox(indent, Breaks::Inconsistent) + self.rbox(indent, Breaks::Inconsistent); } /// Consistent breaking box pub fn cbox(&mut self, indent: isize) { - self.rbox(indent, Breaks::Consistent) + self.rbox(indent, Breaks::Consistent); } pub fn visual_align(&mut self) { @@ -22,15 +22,11 @@ impl Printer { } pub fn break_offset(&mut self, n: usize, off: isize) { - self.scan_break(BreakToken { - offset: off, - blank_space: n as isize, - ..BreakToken::default() - }); + self.scan_break(BreakToken { offset: off, blank_space: n, ..BreakToken::default() }); } pub fn end(&mut self) { - self.scan_end() + self.scan_end(); } pub fn eof(mut self) -> String { @@ -39,23 +35,23 @@ impl Printer { } pub fn word(&mut self, w: impl Into>) { - self.scan_string(w.into()) + self.scan_string(w.into()); } fn spaces(&mut self, n: usize) { - self.break_offset(n, 0) + self.break_offset(n, 0); } pub fn zerobreak(&mut self) { - self.spaces(0) + self.spaces(0); } pub fn space(&mut self) { - self.spaces(1) + self.spaces(1); } pub fn hardbreak(&mut self) { - self.spaces(SIZE_INFINITY as usize) + self.spaces(SIZE_INFINITY as usize); } pub fn is_beginning_of_line(&self) -> bool { @@ -66,20 +62,50 @@ impl Printer { } pub(crate) fn hardbreak_tok_offset(offset: isize) -> Token { - Token::Break(BreakToken { offset, blank_space: SIZE_INFINITY, ..BreakToken::default() }) + Token::Break(BreakToken { + offset, + blank_space: SIZE_INFINITY as usize, + ..BreakToken::default() + }) } - pub fn trailing_comma(&mut self) { - self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); + pub fn space_if_nonempty(&mut self) { + self.scan_break(BreakToken { blank_space: 1, if_nonempty: true, ..BreakToken::default() }); } - pub fn trailing_comma_or_space(&mut self) { + pub fn hardbreak_if_nonempty(&mut self) { self.scan_break(BreakToken { - blank_space: 1, - pre_break: Some(','), + blank_space: SIZE_INFINITY as usize, + if_nonempty: true, ..BreakToken::default() }); } + + pub fn trailing_comma(&mut self, is_last: bool) { + if is_last { + self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); + } else { + self.word(","); + self.space(); + } + } + + pub fn trailing_comma_or_space(&mut self, is_last: bool) { + if is_last { + self.scan_break(BreakToken { + blank_space: 1, + pre_break: Some(','), + ..BreakToken::default() + }); + } else { + self.word(","); + self.space(); + } + } + + pub fn neverbreak(&mut self) { + self.scan_break(BreakToken { never_break: true, ..BreakToken::default() }); + } } impl Token { diff --git a/crates/fmt-2/src/pp/helpers.rs b/crates/fmt-2/src/pp/helpers.rs index 07c7032948e3f..dfe2a2f5de62a 100644 --- a/crates/fmt-2/src/pp/helpers.rs +++ b/crates/fmt-2/src/pp/helpers.rs @@ -7,17 +7,9 @@ impl Printer { self.space(); } - pub fn popen(&mut self) { - self.word("("); - } - - pub fn pclose(&mut self) { - self.word(")"); - } - pub fn hardbreak_if_not_bol(&mut self) { if !self.is_beginning_of_line() { - self.hardbreak() + self.hardbreak(); } } @@ -28,7 +20,7 @@ impl Printer { } pub fn nbsp(&mut self) { - self.word(" ") + self.word(" "); } pub fn word_nbsp(&mut self, w: impl Into>) { @@ -43,6 +35,6 @@ impl Printer { self.space(); self.word(text); self.space(); - self.word("*/") + self.word("*/"); } } diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 247865671cd02..4b66f322bfd97 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -8,7 +8,7 @@ mod convenience; mod helpers; mod ring; -const DEBUG: bool = false; +const DEBUG: bool = true; const DEBUG_INDENT: bool = false; // TODO(dani): config @@ -23,13 +23,13 @@ const INDENT: isize = 4; const MIN_SPACE: isize = 60; /// How to break. Described in more detail in the module docs. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub enum Breaks { Consistent, Inconsistent, } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq)] enum IndentStyle { /// Vertically aligned under whatever column this block begins at. /// @@ -45,20 +45,22 @@ enum IndentStyle { Block { offset: isize }, } -#[derive(Debug, Clone, Copy, Default, PartialEq)] +#[derive(Clone, Copy, Default, PartialEq)] pub(crate) struct BreakToken { offset: isize, - blank_space: isize, + blank_space: usize, pre_break: Option, + if_nonempty: bool, + never_break: bool, } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub(crate) struct BeginToken { indent: IndentStyle, breaks: Breaks, } -#[derive(Debug, PartialEq)] +#[derive(PartialEq)] pub(crate) enum Token { // In practice a string token contains either a `&'static str` or a // `String`. `Cow` is overkill for this because we never modify the data, @@ -69,7 +71,7 @@ pub(crate) enum Token { End, } -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] enum PrintFrame { Fits(Breaks), Broken(usize, Breaks), @@ -77,7 +79,6 @@ enum PrintFrame { const SIZE_INFINITY: isize = 0xffff; -#[derive(Debug)] pub struct Printer { out: String, /// Number of spaces left on line @@ -106,7 +107,6 @@ pub struct Printer { last_printed: Option, } -#[derive(Debug)] struct BufEntry { token: Token, size: isize, @@ -114,7 +114,7 @@ struct BufEntry { impl Printer { pub fn new() -> Self { - Printer { + Self { out: String::new(), space: MARGIN, buf: RingBuffer::new(), @@ -129,16 +129,19 @@ impl Printer { } pub(crate) fn last_token(&self) -> Option<&Token> { - self.last_token_still_buffered().or_else(|| self.last_printed.as_ref()) + self.last_token_still_buffered().or(self.last_printed.as_ref()) } pub(crate) fn last_token_still_buffered(&self) -> Option<&Token> { - self.buf.last().map(|last| &last.token) + if self.buf.is_empty() { + return None; + } + Some(&self.buf.last().token) } /// Be very careful with this! pub(crate) fn replace_last_token_still_buffered(&mut self, token: Token) { - self.buf.last_mut().unwrap().token = token; + self.buf.last_mut().token = token; } fn scan_eof(&mut self) { @@ -162,6 +165,25 @@ impl Printer { if self.scan_stack.is_empty() { self.print_end(); } else { + if !self.buf.is_empty() { + if let Token::Break(break_token) = self.buf.last().token { + if self.buf.len() >= 2 { + if let Token::Begin(_) = self.buf.second_last().token { + self.buf.pop_last(); + self.buf.pop_last(); + self.scan_stack.pop_back(); + self.scan_stack.pop_back(); + self.right_total -= break_token.blank_space as isize; + return; + } + } + if break_token.if_nonempty { + self.buf.pop_last(); + self.scan_stack.pop_back(); + self.right_total -= break_token.blank_space as isize; + } + } + } let right = self.buf.push(BufEntry { token: Token::End, size: -1 }); self.scan_stack.push_back(right); } @@ -177,7 +199,7 @@ impl Printer { } let right = self.buf.push(BufEntry { token: Token::Break(token), size: -self.right_total }); self.scan_stack.push_back(right); - self.right_total += token.blank_space; + self.right_total += token.blank_space as isize; } fn scan_string(&mut self, string: Cow<'static, str>) { @@ -191,19 +213,24 @@ impl Printer { } } + #[track_caller] pub(crate) fn offset(&mut self, offset: isize) { - if let Some(BufEntry { token: Token::Break(token), .. }) = self.buf.last_mut() { - token.offset += offset; + match &mut self.buf.last_mut().token { + Token::Break(token) => token.offset += offset, + Token::Begin(_) => {} + Token::String(_) | Token::End => unreachable!(), } } fn check_stream(&mut self) { while self.right_total - self.left_total > self.space { - if *self.scan_stack.front().unwrap() == self.buf.index_of_first() { + if *self.scan_stack.front().unwrap() == self.buf.index_range().start { self.scan_stack.pop_front().unwrap(); - self.buf.first_mut().unwrap().size = SIZE_INFINITY; + self.buf.first_mut().size = SIZE_INFINITY; } + self.advance_left(); + if self.buf.is_empty() { break; } @@ -211,16 +238,16 @@ impl Printer { } fn advance_left(&mut self) { - while self.buf.first().unwrap().size >= 0 { - let left = self.buf.pop_first().unwrap(); + while self.buf.first().size >= 0 { + let left = self.buf.pop_first(); match &left.token { Token::String(string) => { - self.left_total += string.len() as isize; + self.left_total += left.size; self.print_string(string); } Token::Break(token) => { - self.left_total += token.blank_space; + self.left_total += token.blank_space as isize; self.print_break(*token, left.size); } Token::Begin(token) => self.print_begin(*token, left.size), @@ -274,17 +301,16 @@ impl Printer { Breaks::Consistent => '«', Breaks::Inconsistent => '‹', }); - // TODO(dani) - /* if DEBUG_INDENT { - self.out.extend(token.offset.to_string().chars().map(|ch| match ch { - '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] - [(ch as u8 - b'0') as usize], - '-' => '₋', - _ => unreachable!(), - })); + if let IndentStyle::Block { offset } = token.indent { + self.out.extend(offset.to_string().chars().map(|ch| match ch { + '0'..='9' => ['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'] + [(ch as u8 - b'0') as usize], + '-' => '₋', + _ => unreachable!(), + })); + } } - */ } if size > self.space { @@ -317,22 +343,15 @@ impl Printer { } fn print_break(&mut self, token: BreakToken, size: isize) { - let fits = /* token.never_break || */ + let fits = token.never_break || match self.get_top() { PrintFrame::Fits(..) => true, PrintFrame::Broken(.., Breaks::Consistent) => false, PrintFrame::Broken(.., Breaks::Inconsistent) => size <= self.space, }; if fits { - self.pending_indentation += usize::try_from(token.blank_space).unwrap(); + self.pending_indentation += token.blank_space; self.space -= token.blank_space as isize; - // TODO(dani) - /* - if let Some(no_break) = token.no_break { - self.out.push(no_break); - self.space -= no_break.len_utf8() as isize; - } - */ if DEBUG { self.out.push('·'); } @@ -349,14 +368,6 @@ impl Printer { self.pending_indentation = usize::try_from(indent).unwrap(); // TODO(dani): config self.space = cmp::max(MARGIN - indent, MIN_SPACE); - // TODO(dani) - /* - if !token.post_break.is_empty() { - self.print_indent(); - self.out.push_str(token.post_break); - self.space -= token.post_break.len() as isize; - } - */ } } @@ -368,7 +379,7 @@ impl Printer { fn print_indent(&mut self) { self.out.reserve(self.pending_indentation); - self.out.extend(iter::repeat(' ').take(self.pending_indentation)); + self.out.extend(iter::repeat_n(' ', self.pending_indentation)); self.pending_indentation = 0; } } diff --git a/crates/fmt-2/src/pp/ring.rs b/crates/fmt-2/src/pp/ring.rs index ce10adefda4d9..2e50b2f02bec3 100644 --- a/crates/fmt-2/src/pp/ring.rs +++ b/crates/fmt-2/src/pp/ring.rs @@ -1,68 +1,69 @@ use std::{ collections::VecDeque, - ops::{Index, IndexMut}, + ops::{Index, IndexMut, Range}, }; -/// A view onto a finite range of an infinitely long sequence of T. -/// -/// The Ts are indexed 0..infinity. A RingBuffer begins as a view of elements -/// 0..0 (i.e. nothing). The user of the RingBuffer advances its left and right -/// position independently, although only in the positive direction, and only -/// with left <= right at all times. -/// -/// Holding a RingBuffer whose view is elements left..right gives the ability to -/// use Index and IndexMut to access elements i in the infinitely long queue for -/// which left <= i < right. #[derive(Debug)] -pub(super) struct RingBuffer { +pub struct RingBuffer { data: VecDeque, // Abstract index of data[0] in the infinitely sized queue. offset: usize, } impl RingBuffer { - pub(super) fn new() -> Self { - RingBuffer { data: VecDeque::new(), offset: 0 } + pub fn new() -> Self { + Self { data: VecDeque::new(), offset: 0 } } - pub(super) fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { self.data.is_empty() } - pub(super) fn push(&mut self, value: T) -> usize { + pub fn len(&self) -> usize { + self.data.len() + } + + pub fn push(&mut self, value: T) -> usize { let index = self.offset + self.data.len(); self.data.push_back(value); index } - pub(super) fn clear(&mut self) { + pub fn clear(&mut self) { self.data.clear(); } - pub(super) fn index_of_first(&self) -> usize { - self.offset + pub fn index_range(&self) -> Range { + self.offset..self.offset + self.data.len() } - pub(super) fn first(&self) -> Option<&T> { - self.data.front() + pub fn first(&self) -> &T { + &self.data[0] } - pub(super) fn first_mut(&mut self) -> Option<&mut T> { - self.data.front_mut() + pub fn first_mut(&mut self) -> &mut T { + &mut self.data[0] } - pub(super) fn pop_first(&mut self) -> Option { - let first = self.data.pop_front()?; + pub fn pop_first(&mut self) -> T { self.offset += 1; - Some(first) + self.data.pop_front().unwrap() + } + + pub fn last(&self) -> &T { + self.data.back().unwrap() + } + + pub fn last_mut(&mut self) -> &mut T { + self.data.back_mut().unwrap() } - pub(super) fn last(&self) -> Option<&T> { - self.data.back() + pub fn second_last(&self) -> &T { + &self.data[self.data.len() - 2] } - pub(super) fn last_mut(&mut self) -> Option<&mut T> { - self.data.back_mut() + pub fn pop_last(&mut self) { + self.data.pop_back().unwrap(); } } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 6d8157319ef31..d231cde46f47e 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -3,7 +3,7 @@ use super::{ comments::Comments, pp::{self, Breaks, Token}, }; -use crate::{FormatterConfig, InlineConfig}; +use crate::{iter::IterDelimited, FormatterConfig, InlineConfig}; use foundry_config::fmt as config; use itertools::Itertools; use solar_parse::{ @@ -13,7 +13,7 @@ use solar_parse::{ use std::borrow::Cow; // TODO(dani): config -const INDENT_UNIT: isize = 4; +const INDENT: isize = 4; pub(super) struct State<'a> { pub(crate) s: pp::Printer, @@ -206,7 +206,7 @@ impl<'a> State<'a> { fn bclose_maybe_open(&mut self, span: Span, empty: bool, close_box: bool) { let has_comment = self.maybe_print_comment(span.hi()); if !empty || has_comment { - self.break_offset_if_not_bol(1, -INDENT_UNIT); + self.break_offset_if_not_bol(1, -INDENT); } self.word("}"); if close_box { @@ -257,7 +257,6 @@ impl State<'_> { self.hardbreak_if_not_bol(); self.print_docs(docs); self.maybe_print_comment(span.lo()); - self.cbox(0); match kind { ast::ItemKind::Pragma(ast::PragmaDirective { tokens }) => { self.word("pragma "); @@ -279,6 +278,7 @@ impl State<'_> { } } self.word(";"); + self.hardbreak(); } ast::ItemKind::Import(ast::ImportDirective { path, items }) => { self.word("import "); @@ -286,27 +286,30 @@ impl State<'_> { ast::ImportItems::Plain(ident) => { self.print_ast_str_lit(path); if let Some(ident) = ident { - self.nbsp(); - self.word("as "); + self.word(" as "); self.print_ident(*ident); } } ast::ImportItems::Aliases(aliases) => { - self.bopen(); - self.commasep( - Breaks::Consistent, - aliases.iter(), - |this, (ident, alias)| { - this.print_ident(*ident); - if let Some(alias) = alias { - this.word("as "); - this.print_ident(*alias); - } - }, - ); - self.bclose(item.span, aliases.is_empty()); - self.nbsp(); - self.word("from "); + self.cbox(INDENT); + self.word("{"); + self.zerobreak(); // TODO(dani): braces space + for (pos, (ident, alias)) in aliases.iter().delimited() { + self.print_ident(*ident); + if let Some(alias) = alias { + self.word(" as "); + self.print_ident(*alias); + } + if !pos.is_last { + self.word(","); + self.space(); + } + } + self.zerobreak(); // TODO(dani): braces space + self.offset(-INDENT); + self.word("}"); + self.end(); + self.word(" from "); self.print_ast_str_lit(path); } ast::ImportItems::Glob(ident) => { @@ -317,16 +320,17 @@ impl State<'_> { } } self.word(";"); + self.hardbreak(); } - ast::ItemKind::Using(using) => todo!("Using"), - ast::ItemKind::Contract(contract) => todo!("Contract"), - ast::ItemKind::Function(func) => todo!("Function"), - ast::ItemKind::Variable(var) => todo!("Variable"), - ast::ItemKind::Struct(strukt) => todo!("Struct"), - ast::ItemKind::Enum(enumm) => todo!("Enum"), - ast::ItemKind::Udvt(udvt) => todo!("Udvt"), - ast::ItemKind::Error(error) => todo!("Error"), - ast::ItemKind::Event(event) => todo!("Event"), + ast::ItemKind::Using(_using) => todo!("Using"), + ast::ItemKind::Contract(_contract) => todo!("Contract"), + ast::ItemKind::Function(_func) => todo!("Function"), + ast::ItemKind::Variable(_var) => todo!("Variable"), + ast::ItemKind::Struct(_strukt) => todo!("Struct"), + ast::ItemKind::Enum(_enumm) => todo!("Enum"), + ast::ItemKind::Udvt(_udvt) => todo!("Udvt"), + ast::ItemKind::Error(_error) => todo!("Error"), + ast::ItemKind::Event(_event) => todo!("Event"), } } @@ -353,10 +357,11 @@ impl State<'_> { } fn print_tokens(&mut self, tokens: &[token::Token]) { - let s = tokens.iter().map(|t| self.token_to_string(t)).join(" ").to_string(); + let s = tokens.iter().map(|t| self.token_to_string(t)).join(" "); self.word(s); } + #[allow(clippy::single_match)] fn token_to_string<'a>(&self, token: &'a token::Token) -> Cow<'a, str> { match token.kind { token::TokenKind::Literal(kind, sym) => match kind { diff --git a/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol b/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol index 1db94929ab7c7..5c5ae93e9a692 100644 --- a/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/ImportDirective/bracket-spacing.fmt.sol @@ -5,8 +5,8 @@ import "SomeFile.sol" as SomeOtherFile; import "SomeFile.sol" as SomeOtherFile; import "AnotherFile.sol" as SomeSymbol; import "AnotherFile.sol" as SomeSymbol; -import { symbol1 as alias, symbol2 } from "File.sol"; -import { symbol1 as alias, symbol2 } from "File.sol"; +import { symbol1 as alias0, symbol2 } from "File.sol"; +import { symbol1 as alias0, symbol2 } from "File.sol"; import { symbol1 as alias1, symbol2 as alias2, diff --git a/crates/fmt/testdata/ImportDirective/fmt.sol b/crates/fmt/testdata/ImportDirective/fmt.sol index 4915b8ab203c8..83a739f4e1e73 100644 --- a/crates/fmt/testdata/ImportDirective/fmt.sol +++ b/crates/fmt/testdata/ImportDirective/fmt.sol @@ -4,8 +4,8 @@ import "SomeFile.sol" as SomeOtherFile; import "SomeFile.sol" as SomeOtherFile; import "AnotherFile.sol" as SomeSymbol; import "AnotherFile.sol" as SomeSymbol; -import {symbol1 as alias, symbol2} from "File.sol"; -import {symbol1 as alias, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from "File.sol"; import { symbol1 as alias1, symbol2 as alias2, diff --git a/crates/fmt/testdata/ImportDirective/original.sol b/crates/fmt/testdata/ImportDirective/original.sol index 0e18e10c14dab..f027174512196 100644 --- a/crates/fmt/testdata/ImportDirective/original.sol +++ b/crates/fmt/testdata/ImportDirective/original.sol @@ -4,7 +4,7 @@ import "SomeFile.sol" as SomeOtherFile; import 'SomeFile.sol' as SomeOtherFile; import * as SomeSymbol from "AnotherFile.sol"; import * as SomeSymbol from 'AnotherFile.sol'; -import {symbol1 as alias, symbol2} from "File.sol"; -import {symbol1 as alias, symbol2} from 'File.sol'; +import {symbol1 as alias0, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from 'File.sol'; import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from "File2.sol"; import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; diff --git a/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol b/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol index d1bf9852c02e5..66d2a1d1ec6c1 100644 --- a/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol +++ b/crates/fmt/testdata/ImportDirective/preserve-quote.fmt.sol @@ -5,8 +5,8 @@ import "SomeFile.sol" as SomeOtherFile; import 'SomeFile.sol' as SomeOtherFile; import "AnotherFile.sol" as SomeSymbol; import 'AnotherFile.sol' as SomeSymbol; -import {symbol1 as alias, symbol2} from "File.sol"; -import {symbol1 as alias, symbol2} from 'File.sol'; +import {symbol1 as alias0, symbol2} from "File.sol"; +import {symbol1 as alias0, symbol2} from 'File.sol'; import { symbol1 as alias1, symbol2 as alias2, diff --git a/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol b/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol index 10449e079ae81..d72e043f4f5d7 100644 --- a/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol +++ b/crates/fmt/testdata/ImportDirective/single-quote.fmt.sol @@ -5,8 +5,8 @@ import 'SomeFile.sol' as SomeOtherFile; import 'SomeFile.sol' as SomeOtherFile; import 'AnotherFile.sol' as SomeSymbol; import 'AnotherFile.sol' as SomeSymbol; -import {symbol1 as alias, symbol2} from 'File.sol'; -import {symbol1 as alias, symbol2} from 'File.sol'; +import {symbol1 as alias0, symbol2} from 'File.sol'; +import {symbol1 as alias0, symbol2} from 'File.sol'; import { symbol1 as alias1, symbol2 as alias2, From 7ed556b1ce91c75149cc3dae66ae631b0147f2c2 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 16 Apr 2025 19:54:34 +0200 Subject: [PATCH 07/54] feat: using, types, literals --- a.rs | 4 - a.sol | 6 +- crates/fmt-2/src/main.rs | 5 +- crates/fmt-2/src/pp/mod.rs | 2 +- crates/fmt-2/src/state.rs | 421 +++++++++++++++--- .../NumberLiteralUnderscore/original.sol | 18 +- crates/forge/src/cmd/fmt.rs | 2 +- env.sh | 25 ++ 8 files changed, 400 insertions(+), 83 deletions(-) delete mode 100644 a.rs create mode 100644 env.sh diff --git a/a.rs b/a.rs deleted file mode 100644 index 170118b26f79c..0000000000000 --- a/a.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Doc -// Com -/// Doc2 -fn main() {} diff --git a/a.sol b/a.sol index 15da8825dbc32..a9923086f8b82 100644 --- a/a.sol +++ b/a.sol @@ -1,3 +1,3 @@ -import {a}from"a.sol"; -import {a,b,c} from "a.sol"; -import "a.sol"; +using {test, test as +} for uint; +using {test, test as +} for uint256 global; +using {test, test as +, insanelylongstringinsanelylongstringinsanelylongstringinsanelylongstringinsanelylongstringinsanelylongstringinsanelylongstring} for uint global; diff --git a/crates/fmt-2/src/main.rs b/crates/fmt-2/src/main.rs index b9a406d40236b..34d840f48e188 100644 --- a/crates/fmt-2/src/main.rs +++ b/crates/fmt-2/src/main.rs @@ -6,7 +6,7 @@ use std::{io::Read, path::PathBuf}; fn main() { let args = std::env::args().collect::>(); - let (src, path) = if args.len() < 2 { + let (src, path) = if args.len() < 2 || args[1] == "-" { let mut s = String::new(); std::io::stdin().read_to_string(&mut s).unwrap(); (s, None) @@ -14,7 +14,8 @@ fn main() { let path = PathBuf::from(&args[1]); (std::fs::read_to_string(&path).unwrap(), Some(path)) }; - match forge_fmt_2::format_source(&src, path.as_deref(), Default::default()) { + let config = foundry_config::Config::load().unwrap(); + match forge_fmt_2::format_source(&src, path.as_deref(), config.fmt) { Ok(formatted) => { print!("{formatted}"); } diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 4b66f322bfd97..0d0ad806493d8 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -8,7 +8,7 @@ mod convenience; mod helpers; mod ring; -const DEBUG: bool = true; +const DEBUG: bool = false || option_env!("FMT_DEBUG").is_some(); const DEBUG_INDENT: bool = false; // TODO(dani): config diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index d231cde46f47e..40972918257c7 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -5,7 +5,7 @@ use super::{ }; use crate::{iter::IterDelimited, FormatterConfig, InlineConfig}; use foundry_config::fmt as config; -use itertools::Itertools; +use itertools::{Either, Itertools}; use solar_parse::{ ast::{self, token, Span}, interface::{BytePos, SourceMap}, @@ -15,6 +15,64 @@ use std::borrow::Cow; // TODO(dani): config const INDENT: isize = 4; +/* +- [ ] +/// Maximum line length where formatter will try to wrap the line +pub line_length: usize, + +- [ ] +/// Number of spaces per indentation level +pub tab_width: usize, + +- [x] +/// Print spaces between brackets +pub bracket_spacing: bool, + +- [x] +/// Style of uint/int256 types +pub int_types: IntTypes, + +- [ ] +/// Style of multiline function header in case it doesn't fit +pub multiline_func_header: MultilineFuncHeaderStyle, + +- [x] +/// Style of quotation marks +pub quote_style: QuoteStyle, + +- [x] +/// Style of underscores in number literals +pub number_underscore: NumberUnderscore, + +- [x] +/// Style of underscores in hex literals +pub hex_underscore: HexUnderscore, + +- [ ] +/// Style of single line blocks in statements +pub single_line_statement_blocks: SingleLineBlockStyle, + +- [ ] +/// Print space in state variable, function and modifier `override` attribute +pub override_spacing: bool, + +- [ ] +/// Wrap comments on `line_length` reached +pub wrap_comments: bool, + +- [N/A] +/// Globs to ignore +pub ignore: Vec, + +- [ ] +/// Add new line at start and end of contract declarations +pub contract_new_lines: bool, + +- [ ] +/// Sort import statements alphabetically in groups (a group is separated by a newline). +pub sort_imports: bool, +*/ + pub(super) struct State<'a> { pub(crate) s: pp::Printer, sm: &'a SourceMap, @@ -91,6 +149,7 @@ impl<'a> State<'a> { self.strsep(",", false, b, elts, op) } + // TODO(dani): remove bool? fn maybe_print_comment(&mut self, pos: BytePos) -> bool { let mut has_comment = false; while let Some(cmnt) = self.peek_comment() { @@ -104,21 +163,21 @@ impl<'a> State<'a> { has_comment } - fn print_comment(&mut self, cmnt: Comment) { + fn print_comment(&mut self, mut cmnt: Comment) { match cmnt.style { CommentStyle::Mixed => { if !self.is_beginning_of_line() { self.zerobreak(); } - if let Some((last, lines)) = cmnt.lines.split_last() { + if let Some(last) = cmnt.lines.pop() { self.ibox(0); - for line in lines { - self.word(line.clone()); + for line in cmnt.lines { + self.word(line); self.hardbreak() } - self.word(last.clone()); + self.word(last); self.space(); self.end(); @@ -127,11 +186,11 @@ impl<'a> State<'a> { } CommentStyle::Isolated => { self.hardbreak_if_not_bol(); - for line in &cmnt.lines { + for line in cmnt.lines { // Don't print empty lines because they will end up as trailing // whitespace. if !line.is_empty() { - self.word(line.clone()); + self.word(line); } self.hardbreak(); } @@ -140,14 +199,14 @@ impl<'a> State<'a> { if !self.is_beginning_of_line() { self.word(" "); } - if let [line] = cmnt.lines.as_slice() { - self.word(line.clone()); + if cmnt.lines.len() == 1 { + self.word(cmnt.lines.pop().unwrap()); self.hardbreak() } else { self.visual_align(); - for line in &cmnt.lines { + for line in cmnt.lines { if !line.is_empty() { - self.word(line.clone()); + self.word(line); } self.hardbreak(); } @@ -233,6 +292,14 @@ impl<'a> State<'a> { } } } + + fn braces_break(&mut self) { + if self.config.bracket_spacing { + self.space(); + } else { + self.zerobreak(); + } + } } /// Span to source. @@ -241,6 +308,36 @@ impl State<'_> { let res = self.sm.lookup_byte_offset(pos); res.sf.src[res.pos.to_usize()..].chars().next().unwrap() } + + /// Returns `true` if the span is disabled and has been printed as-is. + #[must_use] + fn handle_span(&mut self, span: Span) -> bool { + self.maybe_print_comment(span.lo()); + self.print_span_if_disabled(span) + } + + /// Returns `true` if the span is disabled and has been printed as-is. + #[inline] + #[must_use] + fn print_span_if_disabled(&mut self, span: Span) -> bool { + let disabled = self.inline_config.is_disabled(span); + if disabled { + self.print_span_cold(span); + } + disabled + } + + #[cold] + fn print_span_cold(&mut self, span: Span) { + self.print_span(span); + } + + fn print_span(&mut self, span: Span) { + match self.sm.span_to_snippet(span) { + Ok(s) => self.word(s), + Err(e) => panic!("failed to print {span:?}: {e:#?}"), + } + } } /// Language-specific pretty printing. @@ -293,7 +390,7 @@ impl State<'_> { ast::ImportItems::Aliases(aliases) => { self.cbox(INDENT); self.word("{"); - self.zerobreak(); // TODO(dani): braces space + self.braces_break(); for (pos, (ident, alias)) in aliases.iter().delimited() { self.print_ident(*ident); if let Some(alias) = alias { @@ -305,7 +402,7 @@ impl State<'_> { self.space(); } } - self.zerobreak(); // TODO(dani): braces space + self.braces_break(); self.offset(-INDENT); self.word("}"); self.end(); @@ -322,8 +419,46 @@ impl State<'_> { self.word(";"); self.hardbreak(); } - ast::ItemKind::Using(_using) => todo!("Using"), - ast::ItemKind::Contract(_contract) => todo!("Contract"), + ast::ItemKind::Using(ast::UsingDirective { list, ty, global }) => { + self.word("using "); + match list { + ast::UsingList::Single(path) => self.print_path(path), + ast::UsingList::Multiple(items) => { + self.cbox(INDENT); + self.word("{"); + self.braces_break(); + for (pos, (path, op)) in items.iter().delimited() { + self.print_path(path); + if let Some(op) = op { + self.word(" as "); + // TODO(dani): op.to_str() + let _ = op; + self.word("?"); + } + if !pos.is_last { + self.word(","); + self.space(); + } + } + self.braces_break(); + self.offset(-INDENT); + self.word("}"); + self.end(); + } + } + self.word(" for "); + if let Some(ty) = ty { + self.print_ty(ty); + } else { + self.word("*"); + } + if *global { + self.word(" global"); + } + self.word(";"); + self.hardbreak(); + } + ast::ItemKind::Contract(_contract) => todo!(), ast::ItemKind::Function(_func) => todo!("Function"), ast::ItemKind::Variable(_var) => todo!("Variable"), ast::ItemKind::Struct(_strukt) => todo!("Struct"), @@ -357,67 +492,146 @@ impl State<'_> { } fn print_tokens(&mut self, tokens: &[token::Token]) { - let s = tokens.iter().map(|t| self.token_to_string(t)).join(" "); - self.word(s); + // Leave unchanged. + let span = Span::join_first_last(tokens.iter().map(|t| t.span)); + self.print_span(span); } - #[allow(clippy::single_match)] - fn token_to_string<'a>(&self, token: &'a token::Token) -> Cow<'a, str> { - match token.kind { - token::TokenKind::Literal(kind, sym) => match kind { - token::TokenLitKind::Str | - token::TokenLitKind::UnicodeStr | - token::TokenLitKind::HexStr => { - let kind = match kind { - token::TokenLitKind::Str => ast::StrKind::Str, - token::TokenLitKind::UnicodeStr => ast::StrKind::Unicode, - token::TokenLitKind::HexStr => ast::StrKind::Hex, - _ => unreachable!(), - }; - return Cow::Owned(self.str_lit_to_string(token.span, sym.as_str(), kind)); - } - token::TokenLitKind::Integer | - token::TokenLitKind::Rational | - token::TokenLitKind::Err(_) => {} - }, - _ => {} + fn print_ident(&mut self, ident: ast::Ident) { + self.maybe_print_comment(ident.span.lo()); + self.word(ident.to_string()); + } + + fn print_path(&mut self, path: &ast::PathSlice) { + for (pos, ident) in path.segments().iter().delimited() { + self.print_ident(*ident); + if !pos.is_last { + self.word("."); + } } - Cow::Borrowed(token.as_str()) } - fn print_ident(&mut self, ident: ast::Ident) { - // TODO(dani): is this right? - self.maybe_print_comment(ident.span.lo()); + fn print_lit(&mut self, lit: &ast::Lit) { + let &ast::Lit { span, symbol, ref kind } = lit; + if self.handle_span(span) { + return; + } + let s = symbol.as_str(); + + match *kind { + ast::LitKind::Str(kind, _) => { + let prefix_len = match kind { + ast::StrKind::Str => 0, + ast::StrKind::Unicode => 7, + ast::StrKind::Hex => 3, + }; + let quote_pos = span.lo() + prefix_len as u32; + let s = &s[prefix_len + 1..s.len() - 1]; + self.print_str_lit(kind, quote_pos, s); + return; + } + ast::LitKind::Number(_) | ast::LitKind::Rational(_) + if !self.config.number_underscore.is_preserve() => + { + self.print_num_literal(s); + return; + } + _ => {} + }; + self.word(s.to_string()); + } - self.word(ident.to_string()); + fn print_num_literal(&mut self, source: &str) { + fn strip_underscores(s: &str) -> Cow<'_, str> { + if s.contains('_') { + Cow::Owned(s.replace('_', "")) + } else { + Cow::Borrowed(s) + } + } + + fn add_underscores( + out: &mut String, + config: config::NumberUnderscore, + string: &str, + reversed: bool, + ) { + if !config.is_thousands() || string.len() < 5 { + out.push_str(string); + return; + } + + let chunks = if reversed { + Either::Left(string.as_bytes().chunks(3)) + } else { + Either::Right(string.as_bytes().rchunks(3).rev()) + } + .map(|chunk| std::str::from_utf8(chunk).unwrap()); + for chunk in Itertools::intersperse(chunks, "_") { + out.push_str(chunk); + } + } + + debug_assert!(source.is_ascii(), "{source:?}"); + + let config = self.config.number_underscore; + debug_assert!(!config.is_preserve()); + + let (val, exp) = source.split_once(['e', 'E']).unwrap_or((source, "")); + let (val, fract) = val.split_once('.').unwrap_or((val, "")); + + let val = strip_underscores(val); + let exp = strip_underscores(exp); + let fract = strip_underscores(fract); + + // strip any padded 0's + let val = val.trim_start_matches('0'); + let fract = fract.trim_end_matches('0'); + let (exp_sign, exp) = + if let Some(exp) = exp.strip_prefix('-') { ("-", exp) } else { ("", &exp[..]) }; + let exp = exp.trim_start_matches('0'); + + let mut out = String::with_capacity(source.len() * 2); + if val.is_empty() { + out.push('0'); + } else { + add_underscores(&mut out, config, val, false); + } + if !fract.is_empty() { + out.push('.'); + add_underscores(&mut out, config, fract, true); + } + if !exp.is_empty() { + // TODO: preserve the `E`? + /* + out.push(if source.contains('e') { + 'e' + } else { + debug_assert!(source.contains('E')); + 'E' + }); + */ + out.push('e'); + out.push_str(exp_sign); + add_underscores(&mut out, config, exp, false); + } + + self.word(out); } /// Prints a raw AST string literal, which is unescaped. fn print_ast_str_lit(&mut self, strlit: &ast::StrLit) { - self.print_str_lit_unescaped(strlit.span, strlit.value.as_str(), ast::StrKind::Str); + self.print_str_lit(ast::StrKind::Str, strlit.span.lo(), strlit.value.as_str()); } - fn print_str_lit(&mut self, span: Span, s: &str, kind: ast::StrKind) { - let s = self.str_lit_to_string(span, s, kind); - self.word(s); - } - fn print_str_lit_unescaped(&mut self, span: Span, s: &str, kind: ast::StrKind) { - let s = self.str_lit_to_string_unescaped(span, s, kind); + /// `s` should be the *unescaped contents of the string literal*. + fn print_str_lit(&mut self, kind: ast::StrKind, quote_pos: BytePos, s: &str) { + let s = self.str_lit_to_string(kind, quote_pos, s); self.word(s); } - fn str_lit_to_string(&self, span: Span, s: &str, kind: ast::StrKind) -> String { - self.str_lit_to_string_inner(span, s.escape_debug(), kind) - } - fn str_lit_to_string_unescaped(&self, span: Span, s: &str, kind: ast::StrKind) -> String { - self.str_lit_to_string_inner(span, s, kind) - } - fn str_lit_to_string_inner( - &self, - span: Span, - s: impl std::fmt::Display, - kind: ast::StrKind, - ) -> String { + /// `s` should be the *unescaped contents of the string literal*. + fn str_lit_to_string(&self, kind: ast::StrKind, quote_pos: BytePos, s: &str) -> String { let prefix = match kind { ast::StrKind::Str => "", ast::StrKind::Unicode => "unicode", @@ -426,8 +640,89 @@ impl State<'_> { let quote = match self.config.quote_style { config::QuoteStyle::Double => '\"', config::QuoteStyle::Single => '\'', - config::QuoteStyle::Preserve => self.char_at(span.lo() + prefix.len() as u32), + config::QuoteStyle::Preserve => self.char_at(quote_pos), }; + let s = solar_parse::interface::data_structures::fmt::from_fn(move |f| { + if matches!(kind, ast::StrKind::Hex) { + match self.config.hex_underscore { + config::HexUnderscore::Preserve => {} + config::HexUnderscore::Remove | config::HexUnderscore::Bytes => { + let mut clean = s.to_string().replace('_', ""); + if matches!(self.config.hex_underscore, config::HexUnderscore::Bytes) { + clean = + clean.chars().chunks(2).into_iter().map(|c| c.format("")).join("_"); + } + return f.write_str(&clean); + } + }; + } + f.write_str(s) + }); format!("{prefix}{quote}{s}{quote}") } + + fn print_ty(&mut self, ty: &ast::Type<'_>) { + if self.handle_span(ty.span) { + return; + } + + match &ty.kind { + &ast::TypeKind::Elementary(ty) => 'b: { + match ty { + // `address payable` is normalized to `address`. + ast::ElementaryType::Address(true) => self.word("address payable"), + // Integers are normalized to long form. + ast::ElementaryType::Int(size) | ast::ElementaryType::UInt(size) => { + match (self.config.int_types, size.bits_raw()) { + (config::IntTypes::Short, 0 | 256) | + (config::IntTypes::Preserve, 0) => { + let short = match ty { + ast::ElementaryType::Int(_) => "int", + ast::ElementaryType::UInt(_) => "uint", + _ => unreachable!(), + }; + self.word(short); + break 'b; + } + _ => {} + } + } + _ => {} + } + self.word(ty.to_abi_str()); + } + ast::TypeKind::Array(ast::TypeArray { element, size }) => { + self.print_ty(element); + if let Some(size) = size { + self.word("["); + self.print_expr(size); + self.word("]"); + } else { + self.word("[]"); + } + } + ast::TypeKind::Function(_func) => todo!(), + ast::TypeKind::Mapping(ast::TypeMapping { key, key_name, value, value_name }) => { + self.word("mapping("); + self.print_ty(key); + if let Some(ident) = key_name { + self.word(" "); + self.print_ident(*ident); + } + self.word(" => "); + self.print_ty(value); + if let Some(ident) = value_name { + self.word(" "); + self.print_ident(*ident); + } + } + ast::TypeKind::Custom(path) => self.print_path(path), + } + } + + fn print_expr(&mut self, expr: &ast::Expr<'_>) { + // TODO + let _ = expr; + self.word(""); + } } diff --git a/crates/fmt/testdata/NumberLiteralUnderscore/original.sol b/crates/fmt/testdata/NumberLiteralUnderscore/original.sol index 8e88fc6d29472..2912a031cbef8 100644 --- a/crates/fmt/testdata/NumberLiteralUnderscore/original.sol +++ b/crates/fmt/testdata/NumberLiteralUnderscore/original.sol @@ -2,23 +2,23 @@ contract NumberLiteral { function test() external { 1; 123_000; - 1_2e345_678; + // 1_2e345_678; -1; 2e-10; .1; 1.3; 2.5e1; 1.23454e0; - 1.2e34_5_678; - 134411.2e34_5_678; - 13431.134112e34_135_678; + // 1.2e34_5_678; + // 134411.2e34_5_678; + // 13431.134112e34_135_678; 13431.0134112; - 13431.134112e-139_3141340; - 00134411.200e0034_5_6780; - 013431.13411200e34_135_6780; - 00.1341120000; + // 13431.134112e-139_3141340; + // 00134411.200e0034_5_6780; + // 013431.13411200e34_135_6780; + // 00.1341120000; 1.0000; - 0013431.13411200e-00139_3141340; + // 0013431.13411200e-00139_3141340; 123E456; 1_000; } diff --git a/crates/forge/src/cmd/fmt.rs b/crates/forge/src/cmd/fmt.rs index 26f601d7bbac1..44828d82760b9 100644 --- a/crates/forge/src/cmd/fmt.rs +++ b/crates/forge/src/cmd/fmt.rs @@ -70,7 +70,7 @@ impl FmtArgs { } [one] if one == Path::new("-") => { let mut s = String::new(); - io::stdin().read_to_string(&mut s).expect("Failed to read from stdin"); + io::stdin().read_to_string(&mut s).wrap_err("failed to read from stdin")?; Input::Stdin(s) } paths => { diff --git a/env.sh b/env.sh new file mode 100644 index 0000000000000..af178ffe78de8 --- /dev/null +++ b/env.sh @@ -0,0 +1,25 @@ +alias forge-fmt="cargo r --quiet -p forge-fmt-2 --" +forge-fmt-cmp() { + cargo b --quiet -p forge-fmt-2 + forge_fmt_new="$(pwd)/target/debug/forge-fmt-2" + + tmp="$(mktemp -d)" + in_f="$tmp/in.sol" + cat < "/dev/stdin" > "$in_f" + config=$1 + if [ -f "$config" ]; then + cp "$config" "$tmp" + else + printf "[fmt]\n%s\n" "$config" > "$tmp/foundry.toml" + fi + + pushd "$tmp" > /dev/null || return 1 + trap 'popd > /dev/null && rm -rf $tmp' EXIT + + forge fmt - --raw < "$in_f" > "$tmp/old.sol" || return 1 + "$forge_fmt_new" "$in_f" > "$tmp/new.sol" || return 1 + + bat --paging=never "$tmp/old.sol" "$tmp/new.sol" || return 1 + + difft "$tmp/old.sol" "$tmp/new.sol" || return 1 +} From b99ee19b1db2fb83d152d5180a70dc4383e2296f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:23:21 +0200 Subject: [PATCH 08/54] feat: contract --- a.sol | 14 ++++- crates/fmt-2/src/lib.rs | 1 + crates/fmt-2/src/state.rs | 120 ++++++++++++++++++++++++++------------ env.sh | 3 +- 4 files changed, 96 insertions(+), 42 deletions(-) diff --git a/a.sol b/a.sol index a9923086f8b82..1cb3c899cd002 100644 --- a/a.sol +++ b/a.sol @@ -1,3 +1,11 @@ -using {test, test as +} for uint; -using {test, test as +} for uint256 global; -using {test, test as +, insanelylongstringinsanelylongstringinsanelylongstringinsanelylongstringinsanelylongstringinsanelylongstringinsanelylongstring} for uint global; +contract ContractDefinition1 is Contract1, Contract2{} + +contract ContractDefinition2 is Contract1, Contract2, Contract3Contract3Contract3Contract3Contract3Contract3Contract3, Contract4, Contract5{} + +contract ContractDefinition1 is Contract1, Contract2{ + using A for uint; +} + +contract ContractDefinition2 is Contract1, Contract2, Contract3Contract3Contract3Contract3Contract3Contract3Contract3, Contract4, Contract5{ + using A for uint; +} diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 4d43caee870f3..4246a8180e490 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -64,6 +64,7 @@ pub fn format_source(source: &str, path: Option<&Path>, config: FormatterConfig) state.print_source_unit(&ast); Ok(state.s.eof()) }); + // TODO(dani): add a non-fatal error that returns the formatted source with the errors sess.emitted_errors().unwrap()?; Ok(res.unwrap()) } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 40972918257c7..d51262c2bd1d2 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -1,7 +1,7 @@ use super::{ comment::{Comment, CommentStyle}, comments::Comments, - pp::{self, Breaks, Token}, + pp::{self, Token}, }; use crate::{iter::IterDelimited, FormatterConfig, InlineConfig}; use foundry_config::fmt as config; @@ -84,12 +84,14 @@ pub(super) struct State<'a> { impl std::ops::Deref for State<'_> { type Target = pp::Printer; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.s } } impl std::ops::DerefMut for State<'_> { + #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.s } @@ -114,41 +116,6 @@ impl<'a> State<'a> { &mut self.comments } - fn strsep<'x, T: 'x, F, I>( - &mut self, - sep: &'static str, - space_before: bool, - b: Breaks, - elts: I, - mut op: F, - ) where - F: FnMut(&mut Self, &T), - I: IntoIterator, - { - let mut it = elts.into_iter(); - - self.rbox(0, b); - if let Some(first) = it.next() { - op(self, first); - for elt in it { - if space_before { - self.space(); - } - self.word_space(sep); - op(self, elt); - } - } - self.end(); - } - - fn commasep<'x, T: 'x, F, I>(&mut self, b: Breaks, elts: I, op: F) - where - F: FnMut(&mut Self, &T), - I: IntoIterator, - { - self.strsep(",", false, b, elts, op) - } - // TODO(dani): remove bool? fn maybe_print_comment(&mut self, pos: BytePos) -> bool { let mut has_comment = false; @@ -351,7 +318,6 @@ impl State<'_> { fn print_item(&mut self, item: &ast::Item<'_>) { let ast::Item { docs, span, kind } = item; - self.hardbreak_if_not_bol(); self.print_docs(docs); self.maybe_print_comment(span.lo()); match kind { @@ -458,7 +424,42 @@ impl State<'_> { self.word(";"); self.hardbreak(); } - ast::ItemKind::Contract(_contract) => todo!(), + ast::ItemKind::Contract(ast::ItemContract { kind, name, layout, bases, body }) => { + self.cbox(INDENT); + self.cbox(0); + self.word_nbsp(kind.to_str()); + self.print_ident(*name); + self.nbsp(); + if !bases.is_empty() { + self.word("is"); + self.space(); + for (pos, base) in bases.iter().delimited() { + self.print_modifier_call(base); + if !pos.is_last { + self.word(","); + self.space(); + } + } + self.space(); + self.offset(-INDENT); + } + self.end(); + if let Some(layout) = layout { + self.word("layout at "); + self.print_expr(layout.slot); + self.space(); + } + + self.word("{"); + self.hardbreak_if_nonempty(); + for item in body.iter() { + self.print_item(item); + } + self.offset(-INDENT); + self.end(); + self.word("}"); + self.hardbreak(); + } ast::ItemKind::Function(_func) => todo!("Function"), ast::ItemKind::Variable(_var) => todo!("Variable"), ast::ItemKind::Struct(_strukt) => todo!("Struct"), @@ -725,4 +726,47 @@ impl State<'_> { let _ = expr; self.word(""); } + + fn print_modifier_call(&mut self, modifier: &ast::Modifier<'_>) { + let ast::Modifier { name, arguments } = modifier; + self.print_path(name); + if !arguments.is_empty() { + self.print_call_args(arguments); + } + } + + fn print_call_args(&mut self, args: &ast::CallArgs<'_>) { + self.cbox(INDENT); + self.word("("); + match args { + ast::CallArgs::Unnamed(exprs) => { + self.zerobreak(); + for (pos, expr) in exprs.iter().delimited() { + self.print_expr(expr); + if !pos.is_last { + self.word(","); + self.space(); + } + } + } + ast::CallArgs::Named(named_args) => { + self.word("{"); + self.braces_break(); + for (pos, ast::NamedArg { name, value }) in named_args.iter().delimited() { + self.print_ident(*name); + self.word(": "); + self.print_expr(value); + if !pos.is_last { + self.word(","); + self.space(); + } + } + self.braces_break(); + self.word("}"); + } + } + self.offset(-INDENT); + self.end(); + self.word(")"); + } } diff --git a/env.sh b/env.sh index af178ffe78de8..55b1014b0d9b5 100644 --- a/env.sh +++ b/env.sh @@ -18,8 +18,9 @@ forge-fmt-cmp() { forge fmt - --raw < "$in_f" > "$tmp/old.sol" || return 1 "$forge_fmt_new" "$in_f" > "$tmp/new.sol" || return 1 + echo -n "$(perl -pe 'chomp if eof' "$tmp/new.sol")" > "$tmp/new.sol" # chop last nl bat --paging=never "$tmp/old.sol" "$tmp/new.sol" || return 1 - difft "$tmp/old.sol" "$tmp/new.sol" || return 1 + difft --override='*:text' "$tmp/old.sol" "$tmp/new.sol" || return 1 } From 5e21a4c53c3049d95eef2dbbcca84bd7d8612e33 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:40:13 +0200 Subject: [PATCH 09/54] rm duplicate testdata --- crates/fmt-2/testdata/Annotation/fmt.sol | 15 - crates/fmt-2/testdata/Annotation/original.sol | 15 - .../fmt-2/testdata/ArrayExpressions/fmt.sol | 69 -- .../testdata/ArrayExpressions/original.sol | 47 -- crates/fmt-2/testdata/BlockComments/fmt.sol | 25 - .../fmt-2/testdata/BlockComments/original.sol | 26 - .../testdata/BlockCommentsFunction/fmt.sol | 20 - .../BlockCommentsFunction/original.sol | 20 - .../ConditionalOperatorExpression/fmt.sol | 37 - .../original.sol | 33 - .../testdata/ConstructorDefinition/fmt.sol | 42 - .../ConstructorDefinition/original.sol | 17 - .../testdata/ConstructorModifierStyle/fmt.sol | 13 - .../ConstructorModifierStyle/original.sol | 13 - .../bracket-spacing.fmt.sol | 37 - .../contract-new-lines.fmt.sol | 52 -- .../fmt-2/testdata/ContractDefinition/fmt.sol | 47 -- .../testdata/ContractDefinition/original.sol | 40 - .../fmt-2/testdata/DoWhileStatement/fmt.sol | 30 - .../testdata/DoWhileStatement/original.sol | 24 - crates/fmt-2/testdata/DocComments/fmt.sol | 100 --- .../fmt-2/testdata/DocComments/original.sol | 95 --- .../DocComments/wrap-comments.fmt.sol | 128 --- crates/fmt-2/testdata/EmitStatement/fmt.sol | 31 - .../fmt-2/testdata/EmitStatement/original.sol | 24 - .../EnumDefinition/bracket-spacing.fmt.sol | 21 - crates/fmt-2/testdata/EnumDefinition/fmt.sol | 20 - .../testdata/EnumDefinition/original.sol | 7 - crates/fmt-2/testdata/EnumVariants/fmt.sol | 19 - .../fmt-2/testdata/EnumVariants/original.sol | 23 - crates/fmt-2/testdata/ErrorDefinition/fmt.sol | 14 - .../testdata/ErrorDefinition/original.sol | 14 - crates/fmt-2/testdata/EventDefinition/fmt.sol | 144 ---- .../testdata/EventDefinition/original.sol | 36 - crates/fmt-2/testdata/ForStatement/fmt.sol | 37 - .../fmt-2/testdata/ForStatement/original.sol | 33 - .../FunctionCall/bracket-spacing.fmt.sol | 42 - crates/fmt-2/testdata/FunctionCall/fmt.sol | 41 - .../fmt-2/testdata/FunctionCall/original.sol | 34 - .../bracket-spacing.fmt.sol | 55 -- .../FunctionCallArgsStatement/fmt.sol | 54 -- .../FunctionCallArgsStatement/original.sol | 50 -- .../FunctionDefinition/all-params.fmt.sol | 732 ------------------ .../testdata/FunctionDefinition/all.fmt.sol | 730 ----------------- .../fmt-2/testdata/FunctionDefinition/fmt.sol | 709 ----------------- .../testdata/FunctionDefinition/original.sol | 218 ------ .../override-spacing.fmt.sol | 710 ----------------- .../FunctionDefinition/params-first.fmt.sol | 716 ----------------- .../FunctionDefinition/params-multi.fmt.sol | 710 ----------------- .../fmt.sol | 21 - .../original.sol | 21 - crates/fmt-2/testdata/FunctionType/fmt.sol | 31 - .../fmt-2/testdata/FunctionType/original.sol | 31 - .../testdata/HexUnderscore/bytes.fmt.sol | 10 - crates/fmt-2/testdata/HexUnderscore/fmt.sol | 9 - .../fmt-2/testdata/HexUnderscore/original.sol | 9 - .../testdata/HexUnderscore/preserve.fmt.sol | 10 - .../testdata/HexUnderscore/remove.fmt.sol | 10 - .../testdata/IfStatement/block-multi.fmt.sol | 171 ---- .../testdata/IfStatement/block-single.fmt.sol | 123 --- crates/fmt-2/testdata/IfStatement/fmt.sol | 145 ---- .../fmt-2/testdata/IfStatement/original.sol | 119 --- crates/fmt-2/testdata/IfStatement2/fmt.sol | 7 - .../fmt-2/testdata/IfStatement2/original.sol | 10 - .../ImportDirective/bracket-spacing.fmt.sol | 21 - crates/fmt-2/testdata/ImportDirective/fmt.sol | 20 - .../testdata/ImportDirective/original.sol | 10 - .../ImportDirective/preserve-quote.fmt.sol | 21 - .../ImportDirective/single-quote.fmt.sol | 21 - crates/fmt-2/testdata/InlineDisable/fmt.sol | 507 ------------ .../fmt-2/testdata/InlineDisable/original.sol | 486 ------------ crates/fmt-2/testdata/IntTypes/fmt.sol | 24 - crates/fmt-2/testdata/IntTypes/original.sol | 24 - .../fmt-2/testdata/IntTypes/preserve.fmt.sol | 25 - crates/fmt-2/testdata/IntTypes/short.fmt.sol | 25 - .../fmt-2/testdata/LiteralExpression/fmt.sol | 59 -- .../testdata/LiteralExpression/original.sol | 58 -- .../LiteralExpression/preserve-quote.fmt.sol | 60 -- .../LiteralExpression/single-quote.fmt.sol | 60 -- crates/fmt-2/testdata/MappingType/fmt.sol | 35 - .../fmt-2/testdata/MappingType/original.sol | 23 - .../fmt-2/testdata/ModifierDefinition/fmt.sol | 14 - .../testdata/ModifierDefinition/original.sol | 9 - .../override-spacing.fmt.sol | 15 - .../NamedFunctionCallExpression/fmt.sol | 47 -- .../NamedFunctionCallExpression/original.sol | 28 - .../testdata/NumberLiteralUnderscore/fmt.sol | 25 - .../NumberLiteralUnderscore/original.sol | 25 - .../NumberLiteralUnderscore/preserve.fmt.sol | 26 - .../NumberLiteralUnderscore/remove.fmt.sol | 26 - .../NumberLiteralUnderscore/thousands.fmt.sol | 26 - .../testdata/OperatorExpressions/fmt.sol | 43 - .../testdata/OperatorExpressions/original.sol | 30 - crates/fmt-2/testdata/PragmaDirective/fmt.sol | 9 - .../testdata/PragmaDirective/original.sol | 9 - crates/fmt-2/testdata/Repros/fmt.sol | 161 ---- crates/fmt-2/testdata/Repros/original.sol | 160 ---- crates/fmt-2/testdata/ReturnStatement/fmt.sol | 66 -- .../testdata/ReturnStatement/original.sol | 60 -- .../testdata/RevertNamedArgsStatement/fmt.sol | 35 - .../RevertNamedArgsStatement/original.sol | 32 - crates/fmt-2/testdata/RevertStatement/fmt.sol | 56 -- .../testdata/RevertStatement/original.sol | 44 -- crates/fmt-2/testdata/SimpleComments/fmt.sol | 80 -- .../testdata/SimpleComments/original.sol | 83 -- .../SimpleComments/wrap-comments.fmt.sol | 92 --- crates/fmt-2/testdata/SortedImports/fmt.sol | 34 - .../fmt-2/testdata/SortedImports/original.sol | 23 - .../StatementBlock/bracket-spacing.fmt.sol | 20 - crates/fmt-2/testdata/StatementBlock/fmt.sol | 19 - .../testdata/StatementBlock/original.sol | 17 - .../StructDefinition/bracket-spacing.fmt.sol | 15 - .../fmt-2/testdata/StructDefinition/fmt.sol | 14 - .../testdata/StructDefinition/original.sol | 10 - crates/fmt-2/testdata/ThisExpression/fmt.sol | 20 - .../testdata/ThisExpression/original.sol | 17 - crates/fmt-2/testdata/TrailingComma/fmt.sol | 12 - .../fmt-2/testdata/TrailingComma/original.sol | 12 - crates/fmt-2/testdata/TryStatement/fmt.sol | 74 -- .../fmt-2/testdata/TryStatement/original.sol | 66 -- crates/fmt-2/testdata/TypeDefinition/fmt.sol | 12 - .../testdata/TypeDefinition/original.sol | 12 - crates/fmt-2/testdata/UnitExpression/fmt.sol | 24 - .../testdata/UnitExpression/original.sol | 23 - crates/fmt-2/testdata/UsingDirective/fmt.sol | 36 - .../testdata/UsingDirective/original.sol | 11 - .../bracket-spacing.fmt.sol | 27 - .../fmt-2/testdata/VariableAssignment/fmt.sol | 26 - .../testdata/VariableAssignment/original.sol | 26 - .../fmt-2/testdata/VariableDefinition/fmt.sol | 65 -- .../testdata/VariableDefinition/original.sol | 27 - .../override-spacing.fmt.sol | 66 -- .../WhileStatement/block-multi.fmt.sol | 80 -- .../WhileStatement/block-single.fmt.sol | 52 -- crates/fmt-2/testdata/WhileStatement/fmt.sol | 59 -- .../testdata/WhileStatement/original.sol | 51 -- crates/fmt-2/testdata/Yul/fmt.sol | 188 ----- crates/fmt-2/testdata/Yul/original.sol | 141 ---- crates/fmt-2/testdata/YulStrings/fmt.sol | 16 - crates/fmt-2/testdata/YulStrings/original.sol | 16 - .../YulStrings/preserve-quote.fmt.sol | 17 - .../testdata/YulStrings/single-quote.fmt.sol | 17 - 142 files changed, 10921 deletions(-) delete mode 100644 crates/fmt-2/testdata/Annotation/fmt.sol delete mode 100644 crates/fmt-2/testdata/Annotation/original.sol delete mode 100644 crates/fmt-2/testdata/ArrayExpressions/fmt.sol delete mode 100644 crates/fmt-2/testdata/ArrayExpressions/original.sol delete mode 100644 crates/fmt-2/testdata/BlockComments/fmt.sol delete mode 100644 crates/fmt-2/testdata/BlockComments/original.sol delete mode 100644 crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol delete mode 100644 crates/fmt-2/testdata/BlockCommentsFunction/original.sol delete mode 100644 crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol delete mode 100644 crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol delete mode 100644 crates/fmt-2/testdata/ConstructorDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/ConstructorDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol delete mode 100644 crates/fmt-2/testdata/ConstructorModifierStyle/original.sol delete mode 100644 crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol delete mode 100644 crates/fmt-2/testdata/ContractDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/ContractDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/DoWhileStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/DoWhileStatement/original.sol delete mode 100644 crates/fmt-2/testdata/DocComments/fmt.sol delete mode 100644 crates/fmt-2/testdata/DocComments/original.sol delete mode 100644 crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol delete mode 100644 crates/fmt-2/testdata/EmitStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/EmitStatement/original.sol delete mode 100644 crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/EnumDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/EnumDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/EnumVariants/fmt.sol delete mode 100644 crates/fmt-2/testdata/EnumVariants/original.sol delete mode 100644 crates/fmt-2/testdata/ErrorDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/ErrorDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/EventDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/EventDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/ForStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/ForStatement/original.sol delete mode 100644 crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionCall/fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionCall/original.sol delete mode 100644 crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol delete mode 100644 crates/fmt-2/testdata/FunctionType/fmt.sol delete mode 100644 crates/fmt-2/testdata/FunctionType/original.sol delete mode 100644 crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol delete mode 100644 crates/fmt-2/testdata/HexUnderscore/fmt.sol delete mode 100644 crates/fmt-2/testdata/HexUnderscore/original.sol delete mode 100644 crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol delete mode 100644 crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol delete mode 100644 crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol delete mode 100644 crates/fmt-2/testdata/IfStatement/block-single.fmt.sol delete mode 100644 crates/fmt-2/testdata/IfStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/IfStatement/original.sol delete mode 100644 crates/fmt-2/testdata/IfStatement2/fmt.sol delete mode 100644 crates/fmt-2/testdata/IfStatement2/original.sol delete mode 100644 crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/ImportDirective/fmt.sol delete mode 100644 crates/fmt-2/testdata/ImportDirective/original.sol delete mode 100644 crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol delete mode 100644 crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol delete mode 100644 crates/fmt-2/testdata/InlineDisable/fmt.sol delete mode 100644 crates/fmt-2/testdata/InlineDisable/original.sol delete mode 100644 crates/fmt-2/testdata/IntTypes/fmt.sol delete mode 100644 crates/fmt-2/testdata/IntTypes/original.sol delete mode 100644 crates/fmt-2/testdata/IntTypes/preserve.fmt.sol delete mode 100644 crates/fmt-2/testdata/IntTypes/short.fmt.sol delete mode 100644 crates/fmt-2/testdata/LiteralExpression/fmt.sol delete mode 100644 crates/fmt-2/testdata/LiteralExpression/original.sol delete mode 100644 crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol delete mode 100644 crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol delete mode 100644 crates/fmt-2/testdata/MappingType/fmt.sol delete mode 100644 crates/fmt-2/testdata/MappingType/original.sol delete mode 100644 crates/fmt-2/testdata/ModifierDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/ModifierDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol delete mode 100644 crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol delete mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol delete mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol delete mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol delete mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol delete mode 100644 crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol delete mode 100644 crates/fmt-2/testdata/OperatorExpressions/fmt.sol delete mode 100644 crates/fmt-2/testdata/OperatorExpressions/original.sol delete mode 100644 crates/fmt-2/testdata/PragmaDirective/fmt.sol delete mode 100644 crates/fmt-2/testdata/PragmaDirective/original.sol delete mode 100644 crates/fmt-2/testdata/Repros/fmt.sol delete mode 100644 crates/fmt-2/testdata/Repros/original.sol delete mode 100644 crates/fmt-2/testdata/ReturnStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/ReturnStatement/original.sol delete mode 100644 crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol delete mode 100644 crates/fmt-2/testdata/RevertStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/RevertStatement/original.sol delete mode 100644 crates/fmt-2/testdata/SimpleComments/fmt.sol delete mode 100644 crates/fmt-2/testdata/SimpleComments/original.sol delete mode 100644 crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol delete mode 100644 crates/fmt-2/testdata/SortedImports/fmt.sol delete mode 100644 crates/fmt-2/testdata/SortedImports/original.sol delete mode 100644 crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/StatementBlock/fmt.sol delete mode 100644 crates/fmt-2/testdata/StatementBlock/original.sol delete mode 100644 crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/StructDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/StructDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/ThisExpression/fmt.sol delete mode 100644 crates/fmt-2/testdata/ThisExpression/original.sol delete mode 100644 crates/fmt-2/testdata/TrailingComma/fmt.sol delete mode 100644 crates/fmt-2/testdata/TrailingComma/original.sol delete mode 100644 crates/fmt-2/testdata/TryStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/TryStatement/original.sol delete mode 100644 crates/fmt-2/testdata/TypeDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/TypeDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/UnitExpression/fmt.sol delete mode 100644 crates/fmt-2/testdata/UnitExpression/original.sol delete mode 100644 crates/fmt-2/testdata/UsingDirective/fmt.sol delete mode 100644 crates/fmt-2/testdata/UsingDirective/original.sol delete mode 100644 crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/VariableAssignment/fmt.sol delete mode 100644 crates/fmt-2/testdata/VariableAssignment/original.sol delete mode 100644 crates/fmt-2/testdata/VariableDefinition/fmt.sol delete mode 100644 crates/fmt-2/testdata/VariableDefinition/original.sol delete mode 100644 crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol delete mode 100644 crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol delete mode 100644 crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol delete mode 100644 crates/fmt-2/testdata/WhileStatement/fmt.sol delete mode 100644 crates/fmt-2/testdata/WhileStatement/original.sol delete mode 100644 crates/fmt-2/testdata/Yul/fmt.sol delete mode 100644 crates/fmt-2/testdata/Yul/original.sol delete mode 100644 crates/fmt-2/testdata/YulStrings/fmt.sol delete mode 100644 crates/fmt-2/testdata/YulStrings/original.sol delete mode 100644 crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol delete mode 100644 crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol diff --git a/crates/fmt-2/testdata/Annotation/fmt.sol b/crates/fmt-2/testdata/Annotation/fmt.sol deleted file mode 100644 index 75bbcf2dd3521..0000000000000 --- a/crates/fmt-2/testdata/Annotation/fmt.sol +++ /dev/null @@ -1,15 +0,0 @@ -// Support for Solana/Substrate annotations -contract A { - @selector([1, 2, 3, 4]) - function foo() public {} - - @selector("another one") - function bar() public {} - - @first("") - @second("") - function foobar() public {} -} - -@topselector(2) -contract B {} diff --git a/crates/fmt-2/testdata/Annotation/original.sol b/crates/fmt-2/testdata/Annotation/original.sol deleted file mode 100644 index 4551f7d1e6bec..0000000000000 --- a/crates/fmt-2/testdata/Annotation/original.sol +++ /dev/null @@ -1,15 +0,0 @@ -// Support for Solana/Substrate annotations -contract A { - @selector([1,2,3,4]) - function foo() public {} - - @selector("another one") - function bar() public {} - - @first("") - @second("") - function foobar() public {} -} - -@topselector(2) -contract B {} diff --git a/crates/fmt-2/testdata/ArrayExpressions/fmt.sol b/crates/fmt-2/testdata/ArrayExpressions/fmt.sol deleted file mode 100644 index adda7a30e098d..0000000000000 --- a/crates/fmt-2/testdata/ArrayExpressions/fmt.sol +++ /dev/null @@ -1,69 +0,0 @@ -contract ArrayExpressions { - function test() external { - /* ARRAY SUBSCRIPT */ - uint256[10] memory sample; - - uint256 length = 10; - uint256[] memory sample2 = new uint256[](length); - - uint256[] /* comment1 */ memory /* comment2 */ sample3; // comment3 - - /* ARRAY SLICE */ - msg.data[4:]; - msg.data[:msg.data.length]; - msg.data[4:msg.data.length]; - - msg.data[ - // comment1 - 4: - ]; - msg.data[ - : /* comment2 */ msg.data.length // comment3 - ]; - msg.data[ - // comment4 - 4: // comment5 - msg.data.length /* comment6 */ - ]; - - uint256 - someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice = - 4; - uint256 - someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice = - msg.data.length; - msg.data[ - someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice: - ]; - msg.data[ - :someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice - ]; - msg.data[ - someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice: - someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice - ]; - - /* ARRAY LITERAL */ - [1, 2, 3]; - - uint256 someVeryVeryLongVariableName = 0; - [ - someVeryVeryLongVariableName, - someVeryVeryLongVariableName, - someVeryVeryLongVariableName - ]; - uint256[3] memory literal = [ - someVeryVeryLongVariableName, - someVeryVeryLongVariableName, - someVeryVeryLongVariableName - ]; - - uint8[3] memory literal2 = /* comment7 */ [ // comment8 - 1, - 2, /* comment9 */ - 3 // comment10 - ]; - uint256[1] memory literal3 = - [ /* comment11 */ someVeryVeryLongVariableName /* comment13 */ ]; - } -} diff --git a/crates/fmt-2/testdata/ArrayExpressions/original.sol b/crates/fmt-2/testdata/ArrayExpressions/original.sol deleted file mode 100644 index 919cd241fdaed..0000000000000 --- a/crates/fmt-2/testdata/ArrayExpressions/original.sol +++ /dev/null @@ -1,47 +0,0 @@ -contract ArrayExpressions { - function test() external { - /* ARRAY SUBSCRIPT */ - uint[10] memory sample; - - uint256 length = 10; - uint[] memory sample2 = new uint[]( - length); - - uint /* comment1 */ [] memory /* comment2 */ sample3 // comment3 - ; - - /* ARRAY SLICE */ - msg.data[4:]; - msg.data[:msg.data.length]; - msg.data[4:msg.data.length]; - - msg.data[ - // comment1 - 4:]; - msg.data[ - : /* comment2 */ msg.data.length // comment3 - ]; - msg.data[ - // comment4 - 4 // comment5 - :msg.data.length /* comment6 */]; - - uint256 someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice = 4; - uint256 someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice = msg.data.length; - msg.data[someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice:]; - msg.data[:someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice]; - msg.data[someVeryVeryVeryLongVariableNameThatDenotesTheStartOfTheMessageDataSlice:someVeryVeryVeryLongVariableNameThatDenotesTheEndOfTheMessageDataSlice]; - - /* ARRAY LITERAL */ - [1, 2, 3]; - - uint256 someVeryVeryLongVariableName = 0; - [someVeryVeryLongVariableName, someVeryVeryLongVariableName, someVeryVeryLongVariableName]; - uint256[3] memory literal = [someVeryVeryLongVariableName,someVeryVeryLongVariableName,someVeryVeryLongVariableName]; - - uint8[3] memory literal2 = /* comment7 */ [ // comment8 - 1, 2, /* comment9 */ 3 // comment10 - ]; - uint256[1] memory literal3 = [ /* comment11 */ someVeryVeryLongVariableName /* comment13 */]; - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/BlockComments/fmt.sol b/crates/fmt-2/testdata/BlockComments/fmt.sol deleted file mode 100644 index 1d7025f2ad591..0000000000000 --- a/crates/fmt-2/testdata/BlockComments/fmt.sol +++ /dev/null @@ -1,25 +0,0 @@ -contract CounterTest is Test { - /** - * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev See {IERC721-balanceOf}. - */ - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } -} diff --git a/crates/fmt-2/testdata/BlockComments/original.sol b/crates/fmt-2/testdata/BlockComments/original.sol deleted file mode 100644 index b91934bf7d12b..0000000000000 --- a/crates/fmt-2/testdata/BlockComments/original.sol +++ /dev/null @@ -1,26 +0,0 @@ -contract CounterTest is Test { - /** - * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. - */ - - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev See {IERC721-balanceOf}. - */ - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - /** - * @dev See {IERC165-supportsInterface}. - */ function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); -} - -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol b/crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol deleted file mode 100644 index 368749bf4fdaf..0000000000000 --- a/crates/fmt-2/testdata/BlockCommentsFunction/fmt.sol +++ /dev/null @@ -1,20 +0,0 @@ -contract A { - Counter public counter; - /** - * TODO: this fuzz use too much time to execute - * function testGetFuzz(bytes[2][] memory kvs) public { - * for (uint256 i = 0; i < kvs.length; i++) { - * bytes32 root = trie.update(kvs[i][0], kvs[i][1]); - * console.logBytes32(root); - * } - * - * for (uint256 i = 0; i < kvs.length; i++) { - * (bool exist, bytes memory value) = trie.get(kvs[i][0]); - * console.logBool(exist); - * console.logBytes(value); - * require(exist); - * require(BytesSlice.equal(value, trie.getRaw(kvs[i][0]))); - * } - * } - */ -} diff --git a/crates/fmt-2/testdata/BlockCommentsFunction/original.sol b/crates/fmt-2/testdata/BlockCommentsFunction/original.sol deleted file mode 100644 index 089f1bac430cd..0000000000000 --- a/crates/fmt-2/testdata/BlockCommentsFunction/original.sol +++ /dev/null @@ -1,20 +0,0 @@ -contract A { - Counter public counter; - /** - * TODO: this fuzz use too much time to execute - function testGetFuzz(bytes[2][] memory kvs) public { - for (uint256 i = 0; i < kvs.length; i++) { - bytes32 root = trie.update(kvs[i][0], kvs[i][1]); - console.logBytes32(root); - } - - for (uint256 i = 0; i < kvs.length; i++) { - (bool exist, bytes memory value) = trie.get(kvs[i][0]); - console.logBool(exist); - console.logBytes(value); - require(exist); - require(BytesSlice.equal(value, trie.getRaw(kvs[i][0]))); - } - } - */ -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol b/crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol deleted file mode 100644 index 733ae2d8ca6a1..0000000000000 --- a/crates/fmt-2/testdata/ConditionalOperatorExpression/fmt.sol +++ /dev/null @@ -1,37 +0,0 @@ -contract TernaryExpression { - function test() external { - bool condition; - bool someVeryVeryLongConditionUsedInTheTernaryExpression; - - condition ? 0 : 1; - - someVeryVeryLongConditionUsedInTheTernaryExpression - ? 1234567890 - : 987654321; - - condition /* comment1 */ /* comment2 */ - ? 1001 /* comment3 */ /* comment4 */ - : 2002; - - // comment5 - someVeryVeryLongConditionUsedInTheTernaryExpression - ? 1 - // comment6 - // comment7 - : 0; // comment8 - - uint256 amount = msg.value > 0 - ? msg.value - : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); - - uint256 amount = msg.value > 0 - ? msg.value - // comment9 - : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); - - uint256 amount = msg.value > 0 - // comment10 - ? msg.value - : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); - } -} diff --git a/crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol b/crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol deleted file mode 100644 index f03328873d6c4..0000000000000 --- a/crates/fmt-2/testdata/ConditionalOperatorExpression/original.sol +++ /dev/null @@ -1,33 +0,0 @@ -contract TernaryExpression { - function test() external { - bool condition; - bool someVeryVeryLongConditionUsedInTheTernaryExpression; - - condition ? 0 : 1; - - someVeryVeryLongConditionUsedInTheTernaryExpression ? 1234567890 : 987654321; - - condition /* comment1 */ ? /* comment2 */ 1001 /* comment3 */ : /* comment4 */ 2002; - - // comment5 - someVeryVeryLongConditionUsedInTheTernaryExpression ? 1 - // comment6 - : - // comment7 - 0; // comment8 - - uint256 amount = msg.value > 0 - ? msg.value - : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); - - uint256 amount = msg.value > 0 - ? msg.value - // comment9 - : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); - - uint amount = msg.value > 0 - // comment10 - ? msg.value - : parseAmount(IERC20(asset).balanceOf(msg.sender), msg.data); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ConstructorDefinition/fmt.sol b/crates/fmt-2/testdata/ConstructorDefinition/fmt.sol deleted file mode 100644 index dd62a0cf43611..0000000000000 --- a/crates/fmt-2/testdata/ConstructorDefinition/fmt.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.5.2; - -// comment block starts here -// comment block continues -// - -// comment block 2 starts here -// comment block 2 continues - -contract Constructors is Ownable, Changeable { - function Constructors(variable1) - public - Changeable(variable1) - Ownable() - onlyOwner - {} - - constructor( - variable1, - variable2, - variable3, - variable4, - variable5, - variable6, - variable7 - ) - public - Changeable( - variable1, - variable2, - variable3, - variable4, - variable5, - variable6, - variable7 - ) - Ownable() - onlyOwner - {} -} diff --git a/crates/fmt-2/testdata/ConstructorDefinition/original.sol b/crates/fmt-2/testdata/ConstructorDefinition/original.sol deleted file mode 100644 index f69205196ffd1..0000000000000 --- a/crates/fmt-2/testdata/ConstructorDefinition/original.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.5.2; - -// comment block starts here -// comment block continues -// - -// comment block 2 starts here -// comment block 2 continues - -contract Constructors is Ownable, Changeable { - function Constructors(variable1) public Changeable(variable1) Ownable() onlyOwner { - } - - constructor(variable1, variable2, variable3, variable4, variable5, variable6, variable7) public Changeable(variable1, variable2, variable3, variable4, variable5, variable6, variable7) Ownable() onlyOwner {} -} diff --git a/crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol b/crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol deleted file mode 100644 index 88694860aded2..0000000000000 --- a/crates/fmt-2/testdata/ConstructorModifierStyle/fmt.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.5.2; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {ERC1155} from "solmate/tokens/ERC1155.sol"; - -import {IAchievements} from "./interfaces/IAchievements.sol"; -import {SoulBound1155} from "./abstracts/SoulBound1155.sol"; - -contract Achievements is IAchievements, SoulBound1155, Ownable { - constructor(address owner) Ownable() ERC1155() {} -} diff --git a/crates/fmt-2/testdata/ConstructorModifierStyle/original.sol b/crates/fmt-2/testdata/ConstructorModifierStyle/original.sol deleted file mode 100644 index 88694860aded2..0000000000000 --- a/crates/fmt-2/testdata/ConstructorModifierStyle/original.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.5.2; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {ERC1155} from "solmate/tokens/ERC1155.sol"; - -import {IAchievements} from "./interfaces/IAchievements.sol"; -import {SoulBound1155} from "./abstracts/SoulBound1155.sol"; - -contract Achievements is IAchievements, SoulBound1155, Ownable { - constructor(address owner) Ownable() ERC1155() {} -} diff --git a/crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol deleted file mode 100644 index dca4e325d39c9..0000000000000 --- a/crates/fmt-2/testdata/ContractDefinition/bracket-spacing.fmt.sol +++ /dev/null @@ -1,37 +0,0 @@ -// config: line_length = 160 -// config: bracket_spacing = true -contract ContractDefinition is Contract1, Contract2, Contract3, Contract4, Contract5 { } - -// comment 7 -contract SampleContract { - // spaced comment 1 - - // spaced comment 2 - // that spans multiple lines - - // comment 8 - constructor() { /* comment 9 */ } // comment 10 - - // comment 11 - function max( /* comment 13 */ uint256 arg1, uint256 /* comment 14 */ arg2, uint256 /* comment 15 */ ) - // comment 16 - external /* comment 17 */ - pure - returns (uint256) - // comment 18 - { - // comment 19 - return arg1 > arg2 ? arg1 : arg2; - } -} - -// comment 20 -contract /* comment 21 */ ExampleContract is /* comment 22 */ SampleContract { } - -contract ERC20DecimalsMock is ERC20 { - uint8 private immutable _decimals; - - constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { - _decimals = decimals_; - } -} diff --git a/crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol b/crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol deleted file mode 100644 index 2e9661f956dcf..0000000000000 --- a/crates/fmt-2/testdata/ContractDefinition/contract-new-lines.fmt.sol +++ /dev/null @@ -1,52 +0,0 @@ -// config: contract_new_lines = true -contract ContractDefinition is - Contract1, - Contract2, - Contract3, - Contract4, - Contract5 -{} - -// comment 7 -contract SampleContract { - - // spaced comment 1 - - // spaced comment 2 - // that spans multiple lines - - // comment 8 - constructor() { /* comment 9 */ } // comment 10 - - // comment 11 - function max( /* comment 13 */ - uint256 arg1, - uint256 /* comment 14 */ arg2, - uint256 /* comment 15 */ - ) - // comment 16 - external /* comment 17 */ - pure - returns (uint256) - // comment 18 - { - // comment 19 - return arg1 > arg2 ? arg1 : arg2; - } - -} - -// comment 20 -contract /* comment 21 */ ExampleContract is /* comment 22 */ SampleContract {} - -contract ERC20DecimalsMock is ERC20 { - - uint8 private immutable _decimals; - - constructor(string memory name_, string memory symbol_, uint8 decimals_) - ERC20(name_, symbol_) - { - _decimals = decimals_; - } - -} diff --git a/crates/fmt-2/testdata/ContractDefinition/fmt.sol b/crates/fmt-2/testdata/ContractDefinition/fmt.sol deleted file mode 100644 index 551e84decfc5b..0000000000000 --- a/crates/fmt-2/testdata/ContractDefinition/fmt.sol +++ /dev/null @@ -1,47 +0,0 @@ -contract ContractDefinition is - Contract1, - Contract2, - Contract3, - Contract4, - Contract5 -{} - -// comment 7 -contract SampleContract { - // spaced comment 1 - - // spaced comment 2 - // that spans multiple lines - - // comment 8 - constructor() { /* comment 9 */ } // comment 10 - - // comment 11 - function max( /* comment 13 */ - uint256 arg1, - uint256 /* comment 14 */ arg2, - uint256 /* comment 15 */ - ) - // comment 16 - external /* comment 17 */ - pure - returns (uint256) - // comment 18 - { - // comment 19 - return arg1 > arg2 ? arg1 : arg2; - } -} - -// comment 20 -contract /* comment 21 */ ExampleContract is /* comment 22 */ SampleContract {} - -contract ERC20DecimalsMock is ERC20 { - uint8 private immutable _decimals; - - constructor(string memory name_, string memory symbol_, uint8 decimals_) - ERC20(name_, symbol_) - { - _decimals = decimals_; - } -} diff --git a/crates/fmt-2/testdata/ContractDefinition/original.sol b/crates/fmt-2/testdata/ContractDefinition/original.sol deleted file mode 100644 index 4c671985bda7b..0000000000000 --- a/crates/fmt-2/testdata/ContractDefinition/original.sol +++ /dev/null @@ -1,40 +0,0 @@ -contract ContractDefinition is Contract1, Contract2, Contract3, Contract4, Contract5 { -} - -// comment 7 -contract SampleContract { - - // spaced comment 1 - - // spaced comment 2 - // that spans multiple lines - - // comment 8 - constructor() { /* comment 9 */ } // comment 10 - - // comment 11 - function max(/* comment 13 */ uint256 arg1, uint256 /* comment 14 */ arg2, uint256 /* comment 15 */) - // comment 16 - external /* comment 17 */ - pure - returns(uint256) - // comment 18 - { // comment 19 - return arg1 > arg2 ? arg1 : arg2; - } -} - -// comment 20 -contract /* comment 21 */ ExampleContract /* comment 22 */ is SampleContract {} - -contract ERC20DecimalsMock is ERC20 { - uint8 private immutable _decimals; - - constructor( - string memory name_, - string memory symbol_, - uint8 decimals_ - ) ERC20(name_, symbol_) { - _decimals = decimals_; - } -} diff --git a/crates/fmt-2/testdata/DoWhileStatement/fmt.sol b/crates/fmt-2/testdata/DoWhileStatement/fmt.sol deleted file mode 100644 index c3c8c71c5da91..0000000000000 --- a/crates/fmt-2/testdata/DoWhileStatement/fmt.sol +++ /dev/null @@ -1,30 +0,0 @@ -pragma solidity ^0.8.8; - -contract DoWhileStatement { - function test() external { - uint256 i; - do { - "test"; - } while (i != 0); - - do {} while (i != 0); - - bool someVeryVeryLongCondition; - do { - "test"; - } while ( - someVeryVeryLongCondition && !someVeryVeryLongCondition - && !someVeryVeryLongCondition && someVeryVeryLongCondition - ); - - do { - i++; - } while (i < 10); - - do { - do { - i++; - } while (i < 30); - } while (i < 20); - } -} diff --git a/crates/fmt-2/testdata/DoWhileStatement/original.sol b/crates/fmt-2/testdata/DoWhileStatement/original.sol deleted file mode 100644 index 51063c8784e10..0000000000000 --- a/crates/fmt-2/testdata/DoWhileStatement/original.sol +++ /dev/null @@ -1,24 +0,0 @@ -pragma solidity ^0.8.8; - - contract DoWhileStatement { - function test() external { - uint256 i; - do { "test"; } while (i != 0); - - do - {} - while - ( - i != 0); - - bool someVeryVeryLongCondition; - do { "test"; } while( - someVeryVeryLongCondition && !someVeryVeryLongCondition && -!someVeryVeryLongCondition && - someVeryVeryLongCondition); - - do i++; while(i < 10); - - do do i++; while (i < 30); while(i < 20); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/DocComments/fmt.sol b/crates/fmt-2/testdata/DocComments/fmt.sol deleted file mode 100644 index 4248f0fe587da..0000000000000 --- a/crates/fmt-2/testdata/DocComments/fmt.sol +++ /dev/null @@ -1,100 +0,0 @@ -pragma solidity ^0.8.13; - -/// @title A Hello world example -contract HelloWorld { - /// Some example struct - struct Person { - uint256 age; - address wallet; - } - - /** - * Here's a more double asterix comment - */ - Person public theDude; - - /// Constructs the dude - /// @param age The dude's age - constructor(uint256 age) { - theDude = Person({age: age, wallet: msg.sender}); - } - - /** - * @dev does nothing - */ - function example() public { - /** - * Does this add a whitespace error? - * - * Let's find out. - */ - } - - /** - * @dev Calculates a rectangle's surface and perimeter. - * @param w Width of the rectangle. - * @param h Height of the rectangle. - * @return s The calculated surface. - * @return p The calculated perimeter. - */ - function rectangle(uint256 w, uint256 h) - public - pure - returns (uint256 s, uint256 p) - { - s = w * h; - p = 2 * (w + h); - } - - /// A long doc line comment that will be wrapped - function docLineOverflow() external {} - - function docLinePostfixOverflow() external {} - - /// A long doc line comment that will be wrapped - - /** - * @notice Here is my comment - * - item 1 - * - item 2 - * Some equations: - * y = mx + b - */ - function anotherExample() external {} - - /** - * contract A { - * function foo() public { - * // does nothing. - * } - * } - */ - function multilineIndent() external {} - - /** - * contract A { - * function foo() public { - * // does nothing. - * } - * } - */ - function multilineMalformedIndent() external {} - - /** - * contract A { - * function withALongNameThatWillCauseCommentWrap() public { - * // does nothing. - * } - * } - */ - function malformedIndentOverflow() external {} -} - -/** - * contract A { - * function foo() public { - * // does nothing. - * } - * } - */ -function freeFloatingMultilineIndent() {} diff --git a/crates/fmt-2/testdata/DocComments/original.sol b/crates/fmt-2/testdata/DocComments/original.sol deleted file mode 100644 index 28f654b57903d..0000000000000 --- a/crates/fmt-2/testdata/DocComments/original.sol +++ /dev/null @@ -1,95 +0,0 @@ -pragma solidity ^0.8.13; - -/// @title A Hello world example -contract HelloWorld { - - /// Some example struct - struct Person { - uint age; - address wallet; - } - - /** - Here's a more double asterix comment - */ - Person public theDude; - - /// Constructs the dude - /// @param age The dude's age - constructor(uint256 age) { - theDude = Person({ - age: age, - wallet: msg.sender - }); - } - - /** @dev does nothing */ - function example() public { - /** - * Does this add a whitespace error? - * - * Let's find out. - */ - } - - /** @dev Calculates a rectangle's surface and perimeter. - * @param w Width of the rectangle. - * @param h Height of the rectangle. - * @return s The calculated surface. -* @return p The calculated perimeter. - */ - function rectangle(uint256 w, uint256 h) public pure returns (uint256 s, uint256 p) { - s = w * h; - p = 2 * (w + h); - } - - /// A long doc line comment that will be wrapped - function docLineOverflow() external {} - - function docLinePostfixOverflow() external {} /// A long doc line comment that will be wrapped - - /** - * @notice Here is my comment - * - item 1 - * - item 2 - * Some equations: - * y = mx + b - */ - function anotherExample() external {} - - /** - contract A { - function foo() public { - // does nothing. - } - } - */ - function multilineIndent() external {} - - /** - contract A { -function foo() public { - // does nothing. - } - } - */ - function multilineMalformedIndent() external {} - - /** - contract A { -function withALongNameThatWillCauseCommentWrap() public { - // does nothing. - } - } - */ - function malformedIndentOverflow() external {} -} - -/** -contract A { - function foo() public { - // does nothing. - } -} -*/ -function freeFloatingMultilineIndent() {} diff --git a/crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol b/crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol deleted file mode 100644 index c3c7fe00c9180..0000000000000 --- a/crates/fmt-2/testdata/DocComments/wrap-comments.fmt.sol +++ /dev/null @@ -1,128 +0,0 @@ -// config: line_length = 40 -// config: wrap_comments = true -pragma solidity ^0.8.13; - -/// @title A Hello world example -contract HelloWorld { - /// Some example struct - struct Person { - uint256 age; - address wallet; - } - - /** - * Here's a more double asterix - * comment - */ - Person public theDude; - - /// Constructs the dude - /// @param age The dude's age - constructor(uint256 age) { - theDude = Person({ - age: age, - wallet: msg.sender - }); - } - - /** - * @dev does nothing - */ - function example() public { - /** - * Does this add a whitespace - * error? - * - * Let's find out. - */ - } - - /** - * @dev Calculates a rectangle's - * surface and perimeter. - * @param w Width of the rectangle. - * @param h Height of the rectangle. - * @return s The calculated surface. - * @return p The calculated - * perimeter. - */ - function rectangle( - uint256 w, - uint256 h - ) - public - pure - returns (uint256 s, uint256 p) - { - s = w * h; - p = 2 * (w + h); - } - - /// A long doc line comment that - /// will be wrapped - function docLineOverflow() - external - {} - - function docLinePostfixOverflow() - external - {} - - /// A long doc line comment that - /// will be wrapped - - /** - * @notice Here is my comment - * - item 1 - * - item 2 - * Some equations: - * y = mx + b - */ - function anotherExample() - external - {} - - /** - * contract A { - * function foo() public { - * // does nothing. - * } - * } - */ - function multilineIndent() - external - {} - - /** - * contract A { - * function foo() public { - * // does nothing. - * } - * } - */ - function multilineMalformedIndent() - external - {} - - /** - * contract A { - * function - * withALongNameThatWillCauseCommentWrap() - * public { - * // does nothing. - * } - * } - */ - function malformedIndentOverflow() - external - {} -} - -/** - * contract A { - * function foo() public { - * // does nothing. - * } - * } - */ -function freeFloatingMultilineIndent() {} diff --git a/crates/fmt-2/testdata/EmitStatement/fmt.sol b/crates/fmt-2/testdata/EmitStatement/fmt.sol deleted file mode 100644 index 0fac66b9b2b80..0000000000000 --- a/crates/fmt-2/testdata/EmitStatement/fmt.sol +++ /dev/null @@ -1,31 +0,0 @@ -// config: line_length = 80 -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 - ); -} diff --git a/crates/fmt-2/testdata/EmitStatement/original.sol b/crates/fmt-2/testdata/EmitStatement/original.sol deleted file mode 100644 index 661abb78261d7..0000000000000 --- a/crates/fmt-2/testdata/EmitStatement/original.sol +++ /dev/null @@ -1,24 +0,0 @@ -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 - ); -} diff --git a/crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol deleted file mode 100644 index a4ae0f0192051..0000000000000 --- a/crates/fmt-2/testdata/EnumDefinition/bracket-spacing.fmt.sol +++ /dev/null @@ -1,21 +0,0 @@ -// config: bracket_spacing = true -contract EnumDefinitions { - enum Empty { } - enum ActionChoices { - GoLeft, - GoRight, - GoStraight, - SitStill - } - enum States { - State1, - State2, - State3, - State4, - State5, - State6, - State7, - State8, - State9 - } -} diff --git a/crates/fmt-2/testdata/EnumDefinition/fmt.sol b/crates/fmt-2/testdata/EnumDefinition/fmt.sol deleted file mode 100644 index 437268aff6423..0000000000000 --- a/crates/fmt-2/testdata/EnumDefinition/fmt.sol +++ /dev/null @@ -1,20 +0,0 @@ -contract EnumDefinitions { - enum Empty {} - enum ActionChoices { - GoLeft, - GoRight, - GoStraight, - SitStill - } - enum States { - State1, - State2, - State3, - State4, - State5, - State6, - State7, - State8, - State9 - } -} diff --git a/crates/fmt-2/testdata/EnumDefinition/original.sol b/crates/fmt-2/testdata/EnumDefinition/original.sol deleted file mode 100644 index 69aadf884aec2..0000000000000 --- a/crates/fmt-2/testdata/EnumDefinition/original.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract EnumDefinitions { - enum Empty { - - } - enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } - enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/EnumVariants/fmt.sol b/crates/fmt-2/testdata/EnumVariants/fmt.sol deleted file mode 100644 index b33b8846984d2..0000000000000 --- a/crates/fmt-2/testdata/EnumVariants/fmt.sol +++ /dev/null @@ -1,19 +0,0 @@ -interface I { - enum Empty {} - - /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. - enum CallerMode { - /// No caller modification is currently active. - None - } - - /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. - enum CallerMode2 { - /// No caller modification is currently active. - None, - /// No caller modification is currently active2. - Some - } - - function bar() public {} -} diff --git a/crates/fmt-2/testdata/EnumVariants/original.sol b/crates/fmt-2/testdata/EnumVariants/original.sol deleted file mode 100644 index 8e146ae0fb574..0000000000000 --- a/crates/fmt-2/testdata/EnumVariants/original.sol +++ /dev/null @@ -1,23 +0,0 @@ -interface I { - enum Empty { - - } - - /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. - enum CallerMode - {/// No caller modification is currently active. - None - } - - /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. - enum CallerMode2 - {/// No caller modification is currently active. - None,/// No caller modification is currently active2. - - Some - } - - function bar() public { - - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ErrorDefinition/fmt.sol b/crates/fmt-2/testdata/ErrorDefinition/fmt.sol deleted file mode 100644 index b94bbe45d52b0..0000000000000 --- a/crates/fmt-2/testdata/ErrorDefinition/fmt.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma solidity ^0.8.4; - -error TopLevelCustomError(); -error TopLevelCustomErrorWithArg(uint256 x); -error TopLevelCustomErrorArgWithoutName(string); -error Error1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 -); - -contract Errors { - error ContractCustomError(); - error ContractCustomErrorWithArg(uint256 x); - error ContractCustomErrorArgWithoutName(string); -} diff --git a/crates/fmt-2/testdata/ErrorDefinition/original.sol b/crates/fmt-2/testdata/ErrorDefinition/original.sol deleted file mode 100644 index f9524c22a9ddf..0000000000000 --- a/crates/fmt-2/testdata/ErrorDefinition/original.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma solidity ^0.8.4; - -error - TopLevelCustomError(); - error TopLevelCustomErrorWithArg(uint x) ; -error TopLevelCustomErrorArgWithoutName (string); -error Error1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); - -contract Errors { - error - ContractCustomError(); - error ContractCustomErrorWithArg(uint x) ; - error ContractCustomErrorArgWithoutName (string); -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/EventDefinition/fmt.sol b/crates/fmt-2/testdata/EventDefinition/fmt.sol deleted file mode 100644 index 11d3d8256865a..0000000000000 --- a/crates/fmt-2/testdata/EventDefinition/fmt.sol +++ /dev/null @@ -1,144 +0,0 @@ -pragma solidity ^0.5.2; - -contract Events { - event Event1(); - event Event1() anonymous; - - event Event1(uint256); - event Event1(uint256) anonymous; - - event Event1(uint256 a); - event Event1(uint256 a) anonymous; - - event Event1(uint256 indexed); - event Event1(uint256 indexed) anonymous; - - event Event1(uint256 indexed a); - event Event1(uint256 indexed a) anonymous; - - event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256); - event Event1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256 - ) anonymous; - - event Event1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 - ); - event Event1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 - ) anonymous; - - event Event1( - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256 - ); - event Event1( - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256, - uint256 - ) anonymous; - - event Event1( - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a - ); - event Event1( - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a, - uint256 a - ) anonymous; - - event Event1( - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed - ); - event Event1( - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed, - uint256 indexed - ) anonymous; - - event Event1( - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a - ); - event Event1( - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a, - uint256 indexed a - ) anonymous; -} diff --git a/crates/fmt-2/testdata/EventDefinition/original.sol b/crates/fmt-2/testdata/EventDefinition/original.sol deleted file mode 100644 index d2a61516204d0..0000000000000 --- a/crates/fmt-2/testdata/EventDefinition/original.sol +++ /dev/null @@ -1,36 +0,0 @@ -pragma solidity ^0.5.2; - -contract Events { - event Event1(); - event Event1() anonymous; - - event Event1(uint256); - event Event1(uint256) anonymous; - - event Event1(uint256 a); - event Event1(uint256 a) anonymous; - - event Event1(uint256 indexed); - event Event1(uint256 indexed) anonymous; - - event Event1(uint256 indexed a); - event Event1(uint256 indexed a) anonymous; - - event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256); - event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256) anonymous; - - event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); - event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) anonymous; - - event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); - event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) anonymous; - - event Event1(uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a); - event Event1(uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a, uint256 a) anonymous; - - event Event1(uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed); - event Event1(uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed, uint256 indexed) anonymous; - - event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); - event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a) anonymous; -} diff --git a/crates/fmt-2/testdata/ForStatement/fmt.sol b/crates/fmt-2/testdata/ForStatement/fmt.sol deleted file mode 100644 index a1bb4b2e6a28c..0000000000000 --- a/crates/fmt-2/testdata/ForStatement/fmt.sol +++ /dev/null @@ -1,37 +0,0 @@ -pragma solidity ^0.8.8; - -contract ForStatement { - function test() external { - for (uint256 i1; i1 < 10; i1++) { - i1++; - } - - uint256 i2; - for (++i2; i2 < 10; i2++) {} - - uint256 veryLongVariableName = 1000; - for ( - uint256 i3; - i3 < 10 && veryLongVariableName > 999 && veryLongVariableName < 1001; - i3++ - ) { - i3++; - } - - for (type(uint256).min;;) {} - - for (;;) { - "test"; - } - - for (uint256 i4; i4 < 10; i4++) { - i4++; - } - - for (uint256 i5;;) { - for (uint256 i6 = 10; i6 > i5; i6--) { - i5++; - } - } - } -} diff --git a/crates/fmt-2/testdata/ForStatement/original.sol b/crates/fmt-2/testdata/ForStatement/original.sol deleted file mode 100644 index e98288dd1cbce..0000000000000 --- a/crates/fmt-2/testdata/ForStatement/original.sol +++ /dev/null @@ -1,33 +0,0 @@ -pragma solidity ^0.8.8; - -contract ForStatement { - function test() external { - for - (uint256 i1 - ; i1 < 10; i1++) - { - i1++; - } - - uint256 i2; - for(++i2;i2<10;i2++) - - {} - - uint256 veryLongVariableName = 1000; - for ( uint256 i3; i3 < 10 - && veryLongVariableName>999 && veryLongVariableName< 1001 - ; i3++) - { i3 ++ ; } - - for (type(uint256).min;;) {} - - for (;;) { "test" ; } - - for (uint256 i4; i4< 10; i4++) i4++; - - for (uint256 i5; ;) - for (uint256 i6 = 10; i6 > i5; i6--) - i5++; - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol deleted file mode 100644 index 2bfe9798c5294..0000000000000 --- a/crates/fmt-2/testdata/FunctionCall/bracket-spacing.fmt.sol +++ /dev/null @@ -1,42 +0,0 @@ -// config: line_length = 120 -// config: bracket_spacing = true -contract FunctionCall { - function foo() public pure { - bar(1111111111111111111111111111111111111111111111111111, 111111111111111111111111111111111111111111111111111); - bar(1111111111111111111111111111111111111111111111111112, 1111111111111111111111111111111111111111111111111112); - bar(1111111111111111111111111111111111111111111111111113, 11111111111111111111111111111111111111111111111111113); // the semicolon is not considered when determining line break - bar( - 1111111111111111111111111111111111111111111111111114, 111111111111111111111111111111111111111111111111111114 - ); - bar( - 111111111111111111111111111111111115, - 11111111111111111111111111111111115, - 11111111111111111111111111111111115 - ); - bar( - 111111111111111111111111111111111111111111111111111116, - 111111111111111111111111111111111111111111111111111116 - ); - bar( - 111111111111111111111111111111111111111111111111111117, - 1111111111111111111111111111111111111111111111111111117 - ); - } - - function bar(uint256, uint256) private pure { - return; - } -} - -function a(uint256 foo) { - foo; - MyContract c = new MyContract(address(0), hex"beef"); -} - -function b() { - a({ foo: 5 }); -} - -contract MyContract { - constructor(address arg, bytes memory data) { } -} diff --git a/crates/fmt-2/testdata/FunctionCall/fmt.sol b/crates/fmt-2/testdata/FunctionCall/fmt.sol deleted file mode 100644 index ff22f63aaa5ce..0000000000000 --- a/crates/fmt-2/testdata/FunctionCall/fmt.sol +++ /dev/null @@ -1,41 +0,0 @@ -// config: line_length = 120 -contract FunctionCall { - function foo() public pure { - bar(1111111111111111111111111111111111111111111111111111, 111111111111111111111111111111111111111111111111111); - bar(1111111111111111111111111111111111111111111111111112, 1111111111111111111111111111111111111111111111111112); - bar(1111111111111111111111111111111111111111111111111113, 11111111111111111111111111111111111111111111111111113); // the semicolon is not considered when determining line break - bar( - 1111111111111111111111111111111111111111111111111114, 111111111111111111111111111111111111111111111111111114 - ); - bar( - 111111111111111111111111111111111115, - 11111111111111111111111111111111115, - 11111111111111111111111111111111115 - ); - bar( - 111111111111111111111111111111111111111111111111111116, - 111111111111111111111111111111111111111111111111111116 - ); - bar( - 111111111111111111111111111111111111111111111111111117, - 1111111111111111111111111111111111111111111111111111117 - ); - } - - function bar(uint256, uint256) private pure { - return; - } -} - -function a(uint256 foo) { - foo; - MyContract c = new MyContract(address(0), hex"beef"); -} - -function b() { - a({foo: 5}); -} - -contract MyContract { - constructor(address arg, bytes memory data) {} -} diff --git a/crates/fmt-2/testdata/FunctionCall/original.sol b/crates/fmt-2/testdata/FunctionCall/original.sol deleted file mode 100644 index ea03850554e25..0000000000000 --- a/crates/fmt-2/testdata/FunctionCall/original.sol +++ /dev/null @@ -1,34 +0,0 @@ -contract FunctionCall { - function foo() public pure { - bar(1111111111111111111111111111111111111111111111111111, 111111111111111111111111111111111111111111111111111); - bar(1111111111111111111111111111111111111111111111111112, 1111111111111111111111111111111111111111111111111112); - bar(1111111111111111111111111111111111111111111111111113, 11111111111111111111111111111111111111111111111111113); // the semicolon is not considered when determining line break - bar(1111111111111111111111111111111111111111111111111114, 111111111111111111111111111111111111111111111111111114); - bar( - 111111111111111111111111111111111115, 11111111111111111111111111111111115, 11111111111111111111111111111111115 - ); - bar( - 111111111111111111111111111111111111111111111111111116, 111111111111111111111111111111111111111111111111111116 - ); - bar( - 111111111111111111111111111111111111111111111111111117, 1111111111111111111111111111111111111111111111111111117 - ); - } - - function bar(uint256, uint256) private pure { - return; - } -} - -function a(uint256 foo) { - foo; - MyContract c = new MyContract(address( 0),hex"beef"); -} - -function b() { - a( {foo: 5} ); -} - -contract MyContract { - constructor(address arg, bytes memory data) {} -} diff --git a/crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol deleted file mode 100644 index 93e5eb1a2e793..0000000000000 --- a/crates/fmt-2/testdata/FunctionCallArgsStatement/bracket-spacing.fmt.sol +++ /dev/null @@ -1,55 +0,0 @@ -// config: bracket_spacing = true -interface ITarget { - function run() external payable; - function veryAndVeryLongNameOfSomeRunFunction() external payable; -} - -contract FunctionCallArgsStatement { - ITarget public target; - - function estimate() public returns (uint256 gas) { - gas = 1 gwei; - } - - function veryAndVeryLongNameOfSomeGasEstimateFunction() - public - returns (uint256) - { - return gasleft(); - } - - function value(uint256 val) public returns (uint256) { - return val; - } - - function test() external { - target.run{ gas: gasleft(), value: 1 wei }; - - target.run{ gas: 1, value: 0x00 }(); - - target.run{ gas: 1000, value: 1 ether }(); - - target.run{ gas: estimate(), value: value(1) }(); - - target.run{ - value: value(1 ether), - gas: veryAndVeryLongNameOfSomeGasEstimateFunction() - }(); - - target.run{ /* comment 1 */ value: /* comment2 */ 1 }; - - target.run{ /* comment3 */ - value: 1, // comment4 - gas: gasleft() - }; - - target.run{ - // comment5 - value: 1, - // comment6 - gas: gasleft() - }; - - vm.expectEmit({ checkTopic1: false, checkTopic2: false }); - } -} diff --git a/crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol b/crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol deleted file mode 100644 index 5a5cc5f634281..0000000000000 --- a/crates/fmt-2/testdata/FunctionCallArgsStatement/fmt.sol +++ /dev/null @@ -1,54 +0,0 @@ -interface ITarget { - function run() external payable; - function veryAndVeryLongNameOfSomeRunFunction() external payable; -} - -contract FunctionCallArgsStatement { - ITarget public target; - - function estimate() public returns (uint256 gas) { - gas = 1 gwei; - } - - function veryAndVeryLongNameOfSomeGasEstimateFunction() - public - returns (uint256) - { - return gasleft(); - } - - function value(uint256 val) public returns (uint256) { - return val; - } - - function test() external { - target.run{gas: gasleft(), value: 1 wei}; - - target.run{gas: 1, value: 0x00}(); - - target.run{gas: 1000, value: 1 ether}(); - - target.run{gas: estimate(), value: value(1)}(); - - target.run{ - value: value(1 ether), - gas: veryAndVeryLongNameOfSomeGasEstimateFunction() - }(); - - target.run{ /* comment 1 */ value: /* comment2 */ 1}; - - target.run{ /* comment3 */ - value: 1, // comment4 - gas: gasleft() - }; - - target.run{ - // comment5 - value: 1, - // comment6 - gas: gasleft() - }; - - vm.expectEmit({checkTopic1: false, checkTopic2: false}); - } -} diff --git a/crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol b/crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol deleted file mode 100644 index b2cfaa2f28f9a..0000000000000 --- a/crates/fmt-2/testdata/FunctionCallArgsStatement/original.sol +++ /dev/null @@ -1,50 +0,0 @@ -interface ITarget { - function run() external payable; - function veryAndVeryLongNameOfSomeRunFunction() external payable; -} - -contract FunctionCallArgsStatement { - ITarget public target; - - function estimate() public returns (uint256 gas) { - gas = 1 gwei; - } - - function veryAndVeryLongNameOfSomeGasEstimateFunction() public returns (uint256) { - return gasleft(); - } - - function value(uint256 val) public returns (uint256) { - return val; - } - - function test() external { - target.run{ gas: gasleft(), value: 1 wei }; - - target.run{gas:1,value:0x00}(); - - target.run{ - gas : 1000, - value: 1 ether - } (); - - target.run{ gas: estimate(), - value: value(1) }(); - - target.run { value: - value(1 ether), gas: veryAndVeryLongNameOfSomeGasEstimateFunction() } (); - - target.run /* comment 1 */ { value: /* comment2 */ 1 }; - - target.run { /* comment3 */ value: 1, // comment4 - gas: gasleft()}; - - target.run { - // comment5 - value: 1, - // comment6 - gas: gasleft()}; - - vm.expectEmit({ checkTopic1: false, checkTopic2: false }); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol deleted file mode 100644 index db7164d284a54..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinition/all-params.fmt.sol +++ /dev/null @@ -1,732 +0,0 @@ -// config: line_length = 60 -// config: multiline_func_header = "all_params" -interface FunctionInterfaces { - function noParamsNoModifiersNoReturns(); - - function oneParam(uint256 x); - - function oneModifier() modifier1; - - function oneReturn() returns (uint256 y1); - - // function prefix - function withComments( // function name postfix - // x1 prefix - uint256 x1, // x1 postfix - // x2 prefix - uint256 x2, // x2 postfix - // x2 postfix2 - /* - multi-line x3 prefix - */ - uint256 x3 // x3 postfix - ) - // public prefix - public // public postfix - // pure prefix - pure // pure postfix - // modifier1 prefix - modifier1 // modifier1 postfix - // modifier2 prefix - modifier2 /* - mutliline modifier2 postfix - */ - // modifier3 prefix - modifier3 // modifier3 postfix - returns ( - // y1 prefix - uint256 y1, // y1 postfix - // y2 prefix - uint256 y2, // y2 postfix - // y3 prefix - uint256 y3 - ); // y3 postfix - // function postfix - - /*////////////////////////////////////////////////////////////////////////// - TEST - //////////////////////////////////////////////////////////////////////////*/ - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ); - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3; - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns (uint256 y1, uint256 y2, uint256 y3); - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3; - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns (uint256 y1, uint256 y2, uint256 y3); - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string); - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address); - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256); -} - -contract FunctionDefinitions { - function() external {} - fallback() external {} - - function() external payable {} - fallback() external payable {} - receive() external payable {} - - function noParamsNoModifiersNoReturns() { - a = 1; - } - - function oneParam(uint256 x) { - a = 1; - } - - function oneModifier() modifier1 { - a = 1; - } - - function oneReturn() returns (uint256 y1) { - a = 1; - } - - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) { - a = 1; - } - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - { - a = 1; - } - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - { - a = 1; - } - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - public - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string) - { - a = 1; - } - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address) - { - a = 1; - } - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - fallback() external payable virtual {} - receive() external payable virtual {} -} - -contract FunctionOverrides is - FunctionInterfaces, - FunctionDefinitions -{ - function noParamsNoModifiersNoReturns() override { - a = 1; - } - - function oneParam( - uint256 x - ) - override( - FunctionInterfaces, - FunctionDefinitions, - SomeOtherFunctionContract, - SomeImport.AndAnotherFunctionContract - ) - { - a = 1; - } -} diff --git a/crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol deleted file mode 100644 index 6d90880679199..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinition/all.fmt.sol +++ /dev/null @@ -1,730 +0,0 @@ -// config: line_length = 60 -// config: multiline_func_header = "all" -interface FunctionInterfaces { - function noParamsNoModifiersNoReturns(); - - function oneParam(uint256 x); - - function oneModifier() modifier1; - - function oneReturn() returns (uint256 y1); - - // function prefix - function withComments( // function name postfix - // x1 prefix - uint256 x1, // x1 postfix - // x2 prefix - uint256 x2, // x2 postfix - // x2 postfix2 - /* - multi-line x3 prefix - */ - uint256 x3 // x3 postfix - ) - // public prefix - public // public postfix - // pure prefix - pure // pure postfix - // modifier1 prefix - modifier1 // modifier1 postfix - // modifier2 prefix - modifier2 /* - mutliline modifier2 postfix - */ - // modifier3 prefix - modifier3 // modifier3 postfix - returns ( - // y1 prefix - uint256 y1, // y1 postfix - // y2 prefix - uint256 y2, // y2 postfix - // y3 prefix - uint256 y3 - ); // y3 postfix - // function postfix - - /*////////////////////////////////////////////////////////////////////////// - TEST - //////////////////////////////////////////////////////////////////////////*/ - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ); - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3; - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns (uint256 y1, uint256 y2, uint256 y3); - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3; - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns (uint256 y1, uint256 y2, uint256 y3); - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string); - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address); - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256); -} - -contract FunctionDefinitions { - function() external {} - fallback() external {} - - function() external payable {} - fallback() external payable {} - receive() external payable {} - - function noParamsNoModifiersNoReturns() { - a = 1; - } - - function oneParam(uint256 x) { - a = 1; - } - - function oneModifier() modifier1 { - a = 1; - } - - function oneReturn() returns (uint256 y1) { - a = 1; - } - - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) { - a = 1; - } - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - { - a = 1; - } - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - { - a = 1; - } - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - public - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string) - { - a = 1; - } - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address) - { - a = 1; - } - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - fallback() external payable virtual {} - receive() external payable virtual {} -} - -contract FunctionOverrides is - FunctionInterfaces, - FunctionDefinitions -{ - function noParamsNoModifiersNoReturns() override { - a = 1; - } - - function oneParam(uint256 x) - override( - FunctionInterfaces, - FunctionDefinitions, - SomeOtherFunctionContract, - SomeImport.AndAnotherFunctionContract - ) - { - a = 1; - } -} diff --git a/crates/fmt-2/testdata/FunctionDefinition/fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/fmt.sol deleted file mode 100644 index 9e34a8bea2682..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinition/fmt.sol +++ /dev/null @@ -1,709 +0,0 @@ -// config: line_length = 60 -interface FunctionInterfaces { - function noParamsNoModifiersNoReturns(); - - function oneParam(uint256 x); - - function oneModifier() modifier1; - - function oneReturn() returns (uint256 y1); - - // function prefix - function withComments( // function name postfix - // x1 prefix - uint256 x1, // x1 postfix - // x2 prefix - uint256 x2, // x2 postfix - // x2 postfix2 - /* - multi-line x3 prefix - */ - uint256 x3 // x3 postfix - ) - // public prefix - public // public postfix - // pure prefix - pure // pure postfix - // modifier1 prefix - modifier1 // modifier1 postfix - // modifier2 prefix - modifier2 /* - mutliline modifier2 postfix - */ - // modifier3 prefix - modifier3 // modifier3 postfix - returns ( - // y1 prefix - uint256 y1, // y1 postfix - // y2 prefix - uint256 y2, // y2 postfix - // y3 prefix - uint256 y3 - ); // y3 postfix - // function postfix - - /*////////////////////////////////////////////////////////////////////////// - TEST - //////////////////////////////////////////////////////////////////////////*/ - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ); - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3; - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3; - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string); - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address); - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256); -} - -contract FunctionDefinitions { - function() external {} - fallback() external {} - - function() external payable {} - fallback() external payable {} - receive() external payable {} - - function noParamsNoModifiersNoReturns() { - a = 1; - } - - function oneParam(uint256 x) { - a = 1; - } - - function oneModifier() modifier1 { - a = 1; - } - - function oneReturn() returns (uint256 y1) { - a = 1; - } - - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) { - a = 1; - } - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - public - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string) - { - a = 1; - } - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address) - { - a = 1; - } - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - fallback() external payable virtual {} - receive() external payable virtual {} -} - -contract FunctionOverrides is - FunctionInterfaces, - FunctionDefinitions -{ - function noParamsNoModifiersNoReturns() override { - a = 1; - } - - function oneParam(uint256 x) - override( - FunctionInterfaces, - FunctionDefinitions, - SomeOtherFunctionContract, - SomeImport.AndAnotherFunctionContract - ) - { - a = 1; - } -} diff --git a/crates/fmt-2/testdata/FunctionDefinition/original.sol b/crates/fmt-2/testdata/FunctionDefinition/original.sol deleted file mode 100644 index 97db649d55660..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinition/original.sol +++ /dev/null @@ -1,218 +0,0 @@ -interface FunctionInterfaces { - function noParamsNoModifiersNoReturns(); - - function oneParam(uint x); - - function oneModifier() modifier1; - - function oneReturn() returns(uint y1); - - // function prefix - function withComments( // function name postfix - // x1 prefix - uint256 x1, // x1 postfix - - // x2 prefix - uint256 x2, // x2 postfix - // x2 postfix2 - /* - multi-line x3 prefix - */ - uint256 x3 // x3 postfix - - ) - // pure prefix - pure // pure postfix - // modifier1 prefix - modifier1 // modifier1 postfix - // public prefix - public // public postfix - // modifier2 prefix - modifier2 /* - mutliline modifier2 postfix - */ - // modifier3 prefix - modifier3 // modifier3 postfix - returns ( - // y1 prefix - uint256 y1, // y1 postfix - // y2 prefix - uint256 y2, // y2 postfix - // y3 prefix - uint256 y3 // y3 postfix - ); // function postfix - - /*////////////////////////////////////////////////////////////////////////// - TEST - //////////////////////////////////////////////////////////////////////////*/ - function manyParams(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10); - - function manyModifiers() modifier1() modifier2() modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10; - - function manyReturns() returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); - - function someParamsSomeModifiers(uint x1, uint x2, uint x3) modifier1() modifier2 modifier3; - - function someParamsSomeReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3); - - function someModifiersSomeReturns() modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3); - - function someParamSomeModifiersSomeReturns(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3); - - function someParamsManyModifiers(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10; - - function someParamsManyReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); - - function manyParamsSomeModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3; - - function manyParamsSomeReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3); - - function manyParamsManyModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10; - - function manyParamsManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); - - function manyParamsManyModifiersManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10); - - function modifierOrderCorrect01() public view virtual override modifier1 modifier2 returns(uint); - - function modifierOrderCorrect02() private pure virtual modifier1 modifier2 returns(string); - - function modifierOrderCorrect03() external payable override modifier1 modifier2 returns(address); - - function modifierOrderCorrect04() internal virtual override modifier1 modifier2 returns(uint); - - function modifierOrderIncorrect01() public modifier1 modifier2 override virtual view returns(uint); - - function modifierOrderIncorrect02() virtual modifier1 external modifier2 override returns(uint); - - function modifierOrderIncorrect03() modifier1 pure internal virtual modifier2 returns(uint); - - function modifierOrderIncorrect04() override modifier1 payable external modifier2 returns(uint); -} - -contract FunctionDefinitions { - function () external {} - fallback () external {} - - function () external payable {} - fallback () external payable {} - receive () external payable {} - - function noParamsNoModifiersNoReturns() { - a = 1; - } - - function oneParam(uint x) { - a = 1; - } - - function oneModifier() modifier1 { - a = 1; - } - - function oneReturn() returns(uint y1) { - a = 1; - } - - function manyParams(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) { - a = 1; - } - - function manyModifiers() modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 { - a = 1; - } - - function manyReturns() returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { - a = 1; - } - - function someParamsSomeModifiers(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 { - a = 1; - } - - function someParamsSomeReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3) { - a = 1; - } - - function someModifiersSomeReturns() modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3) { - a = 1; - } - - function someParamSomeModifiersSomeReturns(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 returns(uint y1, uint y2, uint y3) { - a = 1; - } - - function someParamsManyModifiers(uint x1, uint x2, uint x3) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 { - a = 1; - } - - function someParamsManyReturns(uint x1, uint x2, uint x3) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { - a = 1; - } - - function manyParamsSomeModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 { - a = 1; - } - - function manyParamsSomeReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3) { - a = 1; - } - - function manyParamsManyModifiers(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 public { - a = 1; - } - - function manyParamsManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { - a = 1; - } - - function manyParamsManyModifiersManyReturns(uint x1, uint x2, uint x3, uint x4, uint x5, uint x6, uint x7, uint x8, uint x9, uint x10) modifier1 modifier2 modifier3 modifier4 modifier5 modifier6 modifier7 modifier8 modifier9 modifier10 returns(uint y1, uint y2, uint y3, uint y4, uint y5, uint y6, uint y7, uint y8, uint y9, uint y10) { - a = 1; - } - - function modifierOrderCorrect01() public view virtual override modifier1 modifier2 returns(uint) { - a = 1; - } - - function modifierOrderCorrect02() private pure virtual modifier1 modifier2 returns(string) { - a = 1; - } - - function modifierOrderCorrect03() external payable override modifier1 modifier2 returns(address) { - a = 1; - } - - function modifierOrderCorrect04() internal virtual override modifier1 modifier2 returns(uint) { - a = 1; - } - - function modifierOrderIncorrect01() public modifier1 modifier2 override virtual view returns(uint) { - a = 1; - } - - function modifierOrderIncorrect02() virtual modifier1 external modifier2 override returns(uint) { - a = 1; - } - - function modifierOrderIncorrect03() modifier1 pure internal virtual modifier2 returns(uint) { - a = 1; - } - - function modifierOrderIncorrect04() override modifier1 payable external modifier2 returns(uint) { - a = 1; - } - - fallback() external payable virtual {} - receive() external payable virtual {} -} - -contract FunctionOverrides is FunctionInterfaces, FunctionDefinitions { - function noParamsNoModifiersNoReturns() override { - a = 1; - } - - function oneParam(uint256 x) override(FunctionInterfaces, FunctionDefinitions, SomeOtherFunctionContract, SomeImport.AndAnotherFunctionContract) { - a = 1; - } -} - diff --git a/crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol deleted file mode 100644 index 516e5c2fd42ed..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinition/override-spacing.fmt.sol +++ /dev/null @@ -1,710 +0,0 @@ -// config: line_length = 60 -// config: override_spacing = true -interface FunctionInterfaces { - function noParamsNoModifiersNoReturns(); - - function oneParam(uint256 x); - - function oneModifier() modifier1; - - function oneReturn() returns (uint256 y1); - - // function prefix - function withComments( // function name postfix - // x1 prefix - uint256 x1, // x1 postfix - // x2 prefix - uint256 x2, // x2 postfix - // x2 postfix2 - /* - multi-line x3 prefix - */ - uint256 x3 // x3 postfix - ) - // public prefix - public // public postfix - // pure prefix - pure // pure postfix - // modifier1 prefix - modifier1 // modifier1 postfix - // modifier2 prefix - modifier2 /* - mutliline modifier2 postfix - */ - // modifier3 prefix - modifier3 // modifier3 postfix - returns ( - // y1 prefix - uint256 y1, // y1 postfix - // y2 prefix - uint256 y2, // y2 postfix - // y3 prefix - uint256 y3 - ); // y3 postfix - // function postfix - - /*////////////////////////////////////////////////////////////////////////// - TEST - //////////////////////////////////////////////////////////////////////////*/ - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ); - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3; - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3; - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string); - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address); - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256); -} - -contract FunctionDefinitions { - function() external {} - fallback() external {} - - function() external payable {} - fallback() external payable {} - receive() external payable {} - - function noParamsNoModifiersNoReturns() { - a = 1; - } - - function oneParam(uint256 x) { - a = 1; - } - - function oneModifier() modifier1 { - a = 1; - } - - function oneReturn() returns (uint256 y1) { - a = 1; - } - - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) { - a = 1; - } - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - public - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string) - { - a = 1; - } - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address) - { - a = 1; - } - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - fallback() external payable virtual {} - receive() external payable virtual {} -} - -contract FunctionOverrides is - FunctionInterfaces, - FunctionDefinitions -{ - function noParamsNoModifiersNoReturns() override { - a = 1; - } - - function oneParam(uint256 x) - override ( - FunctionInterfaces, - FunctionDefinitions, - SomeOtherFunctionContract, - SomeImport.AndAnotherFunctionContract - ) - { - a = 1; - } -} diff --git a/crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol deleted file mode 100644 index 3e7ebfff6b3aa..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinition/params-first.fmt.sol +++ /dev/null @@ -1,716 +0,0 @@ -// config: line_length = 60 -// config: multiline_func_header = "params_first" -interface FunctionInterfaces { - function noParamsNoModifiersNoReturns(); - - function oneParam( - uint256 x - ); - - function oneModifier() modifier1; - - function oneReturn() returns (uint256 y1); - - // function prefix - function withComments( // function name postfix - // x1 prefix - uint256 x1, // x1 postfix - // x2 prefix - uint256 x2, // x2 postfix - // x2 postfix2 - /* - multi-line x3 prefix - */ - uint256 x3 // x3 postfix - ) - // public prefix - public // public postfix - // pure prefix - pure // pure postfix - // modifier1 prefix - modifier1 // modifier1 postfix - // modifier2 prefix - modifier2 /* - mutliline modifier2 postfix - */ - // modifier3 prefix - modifier3 // modifier3 postfix - returns ( - // y1 prefix - uint256 y1, // y1 postfix - // y2 prefix - uint256 y2, // y2 postfix - // y3 prefix - uint256 y3 - ); // y3 postfix - // function postfix - - /*////////////////////////////////////////////////////////////////////////// - TEST - //////////////////////////////////////////////////////////////////////////*/ - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ); - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3; - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3; - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string); - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address); - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256); -} - -contract FunctionDefinitions { - function() external {} - fallback() external {} - - function() external payable {} - fallback() external payable {} - receive() external payable {} - - function noParamsNoModifiersNoReturns() { - a = 1; - } - - function oneParam( - uint256 x - ) { - a = 1; - } - - function oneModifier() modifier1 { - a = 1; - } - - function oneReturn() returns (uint256 y1) { - a = 1; - } - - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) { - a = 1; - } - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - public - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string) - { - a = 1; - } - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address) - { - a = 1; - } - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - fallback() external payable virtual {} - receive() external payable virtual {} -} - -contract FunctionOverrides is - FunctionInterfaces, - FunctionDefinitions -{ - function noParamsNoModifiersNoReturns() override { - a = 1; - } - - function oneParam( - uint256 x - ) - override( - FunctionInterfaces, - FunctionDefinitions, - SomeOtherFunctionContract, - SomeImport.AndAnotherFunctionContract - ) - { - a = 1; - } -} diff --git a/crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol b/crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol deleted file mode 100644 index cd2015c9e050e..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinition/params-multi.fmt.sol +++ /dev/null @@ -1,710 +0,0 @@ -// config: line_length = 60 -// config: multiline_func_header = "params_first_multi" -interface FunctionInterfaces { - function noParamsNoModifiersNoReturns(); - - function oneParam(uint256 x); - - function oneModifier() modifier1; - - function oneReturn() returns (uint256 y1); - - // function prefix - function withComments( // function name postfix - // x1 prefix - uint256 x1, // x1 postfix - // x2 prefix - uint256 x2, // x2 postfix - // x2 postfix2 - /* - multi-line x3 prefix - */ - uint256 x3 // x3 postfix - ) - // public prefix - public // public postfix - // pure prefix - pure // pure postfix - // modifier1 prefix - modifier1 // modifier1 postfix - // modifier2 prefix - modifier2 /* - mutliline modifier2 postfix - */ - // modifier3 prefix - modifier3 // modifier3 postfix - returns ( - // y1 prefix - uint256 y1, // y1 postfix - // y2 prefix - uint256 y2, // y2 postfix - // y3 prefix - uint256 y3 - ); // y3 postfix - // function postfix - - /*////////////////////////////////////////////////////////////////////////// - TEST - //////////////////////////////////////////////////////////////////////////*/ - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ); - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3; - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3); - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3; - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3); - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10; - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ); - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string); - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address); - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256); - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256); -} - -contract FunctionDefinitions { - function() external {} - fallback() external {} - - function() external payable {} - fallback() external payable {} - receive() external payable {} - - function noParamsNoModifiersNoReturns() { - a = 1; - } - - function oneParam(uint256 x) { - a = 1; - } - - function oneModifier() modifier1 { - a = 1; - } - - function oneReturn() returns (uint256 y1) { - a = 1; - } - - function manyParams( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) { - a = 1; - } - - function manyModifiers() - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyReturns() - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function someParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function someParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function someModifiersSomeReturns() - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamSomeModifiersSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - returns (uint256 y1, uint256 y2, uint256 y3) - { - a = 1; - } - - function someParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function someParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsSomeModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) modifier1 modifier2 modifier3 { - a = 1; - } - - function manyParamsSomeReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) returns (uint256 y1, uint256 y2, uint256 y3) { - a = 1; - } - - function manyParamsManyModifiers( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - public - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - { - a = 1; - } - - function manyParamsManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function manyParamsManyModifiersManyReturns( - uint256 x1, - uint256 x2, - uint256 x3, - uint256 x4, - uint256 x5, - uint256 x6, - uint256 x7, - uint256 x8, - uint256 x9, - uint256 x10 - ) - modifier1 - modifier2 - modifier3 - modifier4 - modifier5 - modifier6 - modifier7 - modifier8 - modifier9 - modifier10 - returns ( - uint256 y1, - uint256 y2, - uint256 y3, - uint256 y4, - uint256 y5, - uint256 y6, - uint256 y7, - uint256 y8, - uint256 y9, - uint256 y10 - ) - { - a = 1; - } - - function modifierOrderCorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderCorrect02() - private - pure - virtual - modifier1 - modifier2 - returns (string) - { - a = 1; - } - - function modifierOrderCorrect03() - external - payable - override - modifier1 - modifier2 - returns (address) - { - a = 1; - } - - function modifierOrderCorrect04() - internal - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect01() - public - view - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect02() - external - virtual - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect03() - internal - pure - virtual - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - function modifierOrderIncorrect04() - external - payable - override - modifier1 - modifier2 - returns (uint256) - { - a = 1; - } - - fallback() external payable virtual {} - receive() external payable virtual {} -} - -contract FunctionOverrides is - FunctionInterfaces, - FunctionDefinitions -{ - function noParamsNoModifiersNoReturns() override { - a = 1; - } - - function oneParam(uint256 x) - override( - FunctionInterfaces, - FunctionDefinitions, - SomeOtherFunctionContract, - SomeImport.AndAnotherFunctionContract - ) - { - a = 1; - } -} diff --git a/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol b/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol deleted file mode 100644 index 7b751e22ec26a..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/fmt.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -contract ReturnFnFormat { - function returnsFunction() - internal - pure - returns ( - function() - internal pure returns (uint256) - ) - {} -} - -// https://github.com/foundry-rs/foundry/issues/7920 -contract ReturnFnDisableFormat { - // forgefmt: disable-next-line - function disableFnFormat() external returns (uint256) { - return 0; - } -} diff --git a/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol b/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol deleted file mode 100644 index 0c785cde81b14..0000000000000 --- a/crates/fmt-2/testdata/FunctionDefinitionWithFunctionReturns/original.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -contract ReturnFnFormat { - function returnsFunction() - internal - pure - returns ( - function() - internal pure returns (uint256) - ) - {} -} - -// https://github.com/foundry-rs/foundry/issues/7920 -contract ReturnFnDisableFormat { - // forgefmt: disable-next-line - function disableFnFormat() external returns (uint256) { - return 0; - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/FunctionType/fmt.sol b/crates/fmt-2/testdata/FunctionType/fmt.sol deleted file mode 100644 index 39053d816058f..0000000000000 --- a/crates/fmt-2/testdata/FunctionType/fmt.sol +++ /dev/null @@ -1,31 +0,0 @@ -// config: line_length = 90 -library ArrayUtils { - function map(uint256[] memory self, function (uint) pure returns (uint) f) - internal - pure - returns (uint256[] memory r) - { - r = new uint256[](self.length); - for (uint256 i = 0; i < self.length; i++) { - r[i] = f(self[i]); - } - } - - function reduce(uint256[] memory self, function (uint, uint) pure returns (uint) f) - internal - pure - returns (uint256 r) - { - r = self[0]; - for (uint256 i = 1; i < self.length; i++) { - r = f(r, self[i]); - } - } - - function range(uint256 length) internal pure returns (uint256[] memory r) { - r = new uint256[](length); - for (uint256 i = 0; i < r.length; i++) { - r[i] = i; - } - } -} diff --git a/crates/fmt-2/testdata/FunctionType/original.sol b/crates/fmt-2/testdata/FunctionType/original.sol deleted file mode 100644 index 27b402d85ef5f..0000000000000 --- a/crates/fmt-2/testdata/FunctionType/original.sol +++ /dev/null @@ -1,31 +0,0 @@ -library ArrayUtils { - function map(uint[] memory self, function (uint) pure returns (uint) f) - internal - pure - returns ( - uint[] memory r - ) - { - r = new uint[]( self.length); - for (uint i = 0; i < self.length; i++) { - r[i] = f(self[i]); - } - } - - function reduce( - uint[] memory self, - function (uint, uint) pure returns (uint) f - ) internal pure returns (uint256 r) { - r = self[0]; - for (uint i = 1; i < self.length; i++) { - r = f(r, self[i]); - } - } - - function range(uint256 length) internal pure returns (uint[] memory r) { - r = new uint256[](length ); - for (uint i = 0; i < r.length; i++) { - r[i] = i; - } - } -} diff --git a/crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol b/crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol deleted file mode 100644 index b3be2a8657c18..0000000000000 --- a/crates/fmt-2/testdata/HexUnderscore/bytes.fmt.sol +++ /dev/null @@ -1,10 +0,0 @@ -// config: hex_underscore = "bytes" -contract HexLiteral { - function test() external { - hex"01_23_00_00"; - hex"01_23_00_00"; - hex"01_23_00_00"; - hex""; - hex"60_01_60_02_53"; - } -} diff --git a/crates/fmt-2/testdata/HexUnderscore/fmt.sol b/crates/fmt-2/testdata/HexUnderscore/fmt.sol deleted file mode 100644 index 0c8710a924758..0000000000000 --- a/crates/fmt-2/testdata/HexUnderscore/fmt.sol +++ /dev/null @@ -1,9 +0,0 @@ -contract HexLiteral { - function test() external { - hex"01230000"; - hex"01230000"; - hex"01230000"; - hex""; - hex"6001600253"; - } -} diff --git a/crates/fmt-2/testdata/HexUnderscore/original.sol b/crates/fmt-2/testdata/HexUnderscore/original.sol deleted file mode 100644 index 5c29187475489..0000000000000 --- a/crates/fmt-2/testdata/HexUnderscore/original.sol +++ /dev/null @@ -1,9 +0,0 @@ -contract HexLiteral { - function test() external { - hex"0123_0000"; - hex"01230000"; - hex"0123_00_00"; - hex""; - hex"6001_6002_53"; - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol b/crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol deleted file mode 100644 index 0f5db52e3c3f3..0000000000000 --- a/crates/fmt-2/testdata/HexUnderscore/preserve.fmt.sol +++ /dev/null @@ -1,10 +0,0 @@ -// config: hex_underscore = "preserve" -contract HexLiteral { - function test() external { - hex"0123_0000"; - hex"01230000"; - hex"0123_00_00"; - hex""; - hex"6001_6002_53"; - } -} diff --git a/crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol b/crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol deleted file mode 100644 index 39aae1465cf7d..0000000000000 --- a/crates/fmt-2/testdata/HexUnderscore/remove.fmt.sol +++ /dev/null @@ -1,10 +0,0 @@ -// config: hex_underscore = "remove" -contract HexLiteral { - function test() external { - hex"01230000"; - hex"01230000"; - hex"01230000"; - hex""; - hex"6001600253"; - } -} diff --git a/crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol b/crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol deleted file mode 100644 index dcd8bb83eaa8f..0000000000000 --- a/crates/fmt-2/testdata/IfStatement/block-multi.fmt.sol +++ /dev/null @@ -1,171 +0,0 @@ -// config: single_line_statement_blocks = "multi" -function execute() returns (bool) { - if (true) { - // always returns true - return true; - } - return false; -} - -function executeElse() {} - -function executeWithMultipleParameters(bool parameter1, bool parameter2) {} - -function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} - -contract IfStatement { - function test() external { - if (true) { - execute(); - } - - bool condition; - bool anotherLongCondition; - bool andAnotherVeryVeryLongCondition; - if ( - condition && anotherLongCondition || andAnotherVeryVeryLongCondition - ) { - execute(); - } - - // comment - if (condition) { - execute(); - } else if (anotherLongCondition) { - execute(); // differently - } - - /* comment1 */ - if ( /* comment2 */ /* comment3 */ - condition // comment4 - ) { - // comment5 - execute(); - } // comment6 - - if (condition) { - execute(); - } // comment7 - /* comment8 */ - /* comment9 */ - else if ( /* comment10 */ - anotherLongCondition // comment11 - ) { - /* comment12 */ - execute(); - } // comment13 - /* comment14 */ - else {} // comment15 - - if ( - // comment16 - condition /* comment17 */ - ) { - execute(); - } - - if (condition) { - execute(); - } else { - executeElse(); - } - - if (condition) { - if (anotherLongCondition) { - execute(); - } - } - - if (condition) { - execute(); - } - - if ( - condition && anotherLongCondition || andAnotherVeryVeryLongCondition - ) { - execute(); - } - - if (condition) { - if (anotherLongCondition) { - execute(); - } - } - - if (condition) { - execute(); - } // comment18 - - if (condition) { - executeWithMultipleParameters(condition, anotherLongCondition); - } - - if (condition) { - executeWithVeryVeryVeryLongNameAndSomeParameter(condition); - } - - if (condition) { - execute(); - } else { - execute(); - } - - if (condition) {} - - if (condition) { - executeWithMultipleParameters(condition, anotherLongCondition); - } else if (anotherLongCondition) { - execute(); - } - - if (condition && ((condition || anotherLongCondition))) { - execute(); - } - - // if statement - if (condition) { - execute(); - } - // else statement - else { - execute(); - } - - // if statement - if (condition) { - execute(); - } - // else statement - else { - executeWithMultipleParameters( - anotherLongCondition, andAnotherVeryVeryLongCondition - ); - } - - if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } - - if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else { - executeElse(); - } - } -} diff --git a/crates/fmt-2/testdata/IfStatement/block-single.fmt.sol b/crates/fmt-2/testdata/IfStatement/block-single.fmt.sol deleted file mode 100644 index ba2b9998b184c..0000000000000 --- a/crates/fmt-2/testdata/IfStatement/block-single.fmt.sol +++ /dev/null @@ -1,123 +0,0 @@ -// config: single_line_statement_blocks = "single" -function execute() returns (bool) { - if (true) { - // always returns true - return true; - } - return false; -} - -function executeElse() {} - -function executeWithMultipleParameters(bool parameter1, bool parameter2) {} - -function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} - -contract IfStatement { - function test() external { - if (true) execute(); - - bool condition; - bool anotherLongCondition; - bool andAnotherVeryVeryLongCondition; - if ( - condition && anotherLongCondition || andAnotherVeryVeryLongCondition - ) execute(); - - // comment - if (condition) execute(); - else if (anotherLongCondition) execute(); // differently - - /* comment1 */ - if ( /* comment2 */ /* comment3 */ - condition // comment4 - ) { - // comment5 - execute(); - } // comment6 - - if (condition) { - execute(); - } // comment7 - /* comment8 */ - /* comment9 */ - else if ( /* comment10 */ - anotherLongCondition // comment11 - ) { - /* comment12 */ - execute(); - } // comment13 - /* comment14 */ - else {} // comment15 - - if ( - // comment16 - condition /* comment17 */ - ) execute(); - - if (condition) execute(); - else executeElse(); - - if (condition) if (anotherLongCondition) execute(); - - if (condition) execute(); - - if ( - condition && anotherLongCondition || andAnotherVeryVeryLongCondition - ) execute(); - - if (condition) if (anotherLongCondition) execute(); - - if (condition) execute(); // comment18 - - if (condition) { - executeWithMultipleParameters(condition, anotherLongCondition); - } - - if (condition) { - executeWithVeryVeryVeryLongNameAndSomeParameter(condition); - } - - if (condition) execute(); - else execute(); - - if (condition) {} - - if (condition) { - executeWithMultipleParameters(condition, anotherLongCondition); - } else if (anotherLongCondition) { - execute(); - } - - if (condition && ((condition || anotherLongCondition))) execute(); - - // if statement - if (condition) execute(); - // else statement - else execute(); - - // if statement - if (condition) { - execute(); - } - // else statement - else { - executeWithMultipleParameters( - anotherLongCondition, andAnotherVeryVeryLongCondition - ); - } - - if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - - if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else executeElse(); - } -} diff --git a/crates/fmt-2/testdata/IfStatement/fmt.sol b/crates/fmt-2/testdata/IfStatement/fmt.sol deleted file mode 100644 index cb2f8874f83d5..0000000000000 --- a/crates/fmt-2/testdata/IfStatement/fmt.sol +++ /dev/null @@ -1,145 +0,0 @@ -function execute() returns (bool) { - if (true) { - // always returns true - return true; - } - return false; -} - -function executeElse() {} - -function executeWithMultipleParameters(bool parameter1, bool parameter2) {} - -function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} - -contract IfStatement { - function test() external { - if (true) { - execute(); - } - - bool condition; - bool anotherLongCondition; - bool andAnotherVeryVeryLongCondition; - if ( - condition && anotherLongCondition || andAnotherVeryVeryLongCondition - ) { - execute(); - } - - // comment - if (condition) { - execute(); - } else if (anotherLongCondition) { - execute(); // differently - } - - /* comment1 */ - if ( /* comment2 */ /* comment3 */ - condition // comment4 - ) { - // comment5 - execute(); - } // comment6 - - if (condition) { - execute(); - } // comment7 - /* comment8 */ - /* comment9 */ - else if ( /* comment10 */ - anotherLongCondition // comment11 - ) { - /* comment12 */ - execute(); - } // comment13 - /* comment14 */ - else {} // comment15 - - if ( - // comment16 - condition /* comment17 */ - ) { - execute(); - } - - if (condition) { - execute(); - } else { - executeElse(); - } - - if (condition) { - if (anotherLongCondition) { - execute(); - } - } - - if (condition) execute(); - - if ( - condition && anotherLongCondition || andAnotherVeryVeryLongCondition - ) execute(); - - if (condition) if (anotherLongCondition) execute(); - - if (condition) execute(); // comment18 - - if (condition) { - executeWithMultipleParameters(condition, anotherLongCondition); - } - - if (condition) { - executeWithVeryVeryVeryLongNameAndSomeParameter(condition); - } - - if (condition) execute(); - else execute(); - - if (condition) {} - - if (condition) { - executeWithMultipleParameters(condition, anotherLongCondition); - } else if (anotherLongCondition) { - execute(); - } - - if (condition && ((condition || anotherLongCondition))) execute(); - - // if statement - if (condition) execute(); - // else statement - else execute(); - - // if statement - if (condition) { - execute(); - } - // else statement - else { - executeWithMultipleParameters( - anotherLongCondition, andAnotherVeryVeryLongCondition - ); - } - - if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - - if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else if (condition) { - execute(); - } else { - executeElse(); - } - } -} diff --git a/crates/fmt-2/testdata/IfStatement/original.sol b/crates/fmt-2/testdata/IfStatement/original.sol deleted file mode 100644 index b36829bbbf6bf..0000000000000 --- a/crates/fmt-2/testdata/IfStatement/original.sol +++ /dev/null @@ -1,119 +0,0 @@ -function execute() returns (bool) { - if (true) { - // always returns true - return true; - } - return false; -} - -function executeElse() {} - -function executeWithMultipleParameters(bool parameter1, bool parameter2) {} - -function executeWithVeryVeryVeryLongNameAndSomeParameter(bool parameter) {} - -contract IfStatement { - - function test() external { - if( true) - { - execute() ; - } - - bool condition; bool anotherLongCondition; bool andAnotherVeryVeryLongCondition ; - if - ( condition && anotherLongCondition || - andAnotherVeryVeryLongCondition - ) - { execute(); } - - // comment - if (condition) { execute(); } - else - if (anotherLongCondition) { - execute(); // differently - } - - /* comment1 */ if /* comment2 */ ( /* comment3 */ condition ) // comment4 - { - // comment5 - execute(); - } // comment6 - - if (condition ) { - execute(); - } // comment7 - /* comment8 */ - /* comment9 */ else if /* comment10 */ (anotherLongCondition) // comment11 - /* comment12 */ { - execute() ; - } // comment13 - /* comment14 */ else { } // comment15 - - if ( - // comment16 - condition /* comment17 */ - ) - { - execute(); - } - - if (condition) - execute(); - else - executeElse(); - - if (condition) - if (anotherLongCondition) - execute(); - - if (condition) execute(); - - if (condition && anotherLongCondition || - andAnotherVeryVeryLongCondition ) execute(); - - if (condition) if (anotherLongCondition) execute(); - - if (condition) execute(); // comment18 - - if (condition) executeWithMultipleParameters(condition, anotherLongCondition); - - if (condition) executeWithVeryVeryVeryLongNameAndSomeParameter(condition); - - if (condition) execute(); else execute(); - - if (condition) {} - - if (condition) executeWithMultipleParameters(condition, anotherLongCondition); else if (anotherLongCondition) execute(); - - if (condition && ((condition || anotherLongCondition) - ) - ) execute(); - - // if statement - if (condition) execute(); - // else statement - else execute(); - - // if statement - if (condition) execute(); - // else statement - else executeWithMultipleParameters(anotherLongCondition, andAnotherVeryVeryLongCondition); - - if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - else if (condition) execute(); - - if (condition) execute(); - else if (condition) - execute(); - else if (condition) execute(); - else if (condition) - execute(); - else if (condition) execute(); - else - executeElse(); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/IfStatement2/fmt.sol b/crates/fmt-2/testdata/IfStatement2/fmt.sol deleted file mode 100644 index 10ae43601d4ad..0000000000000 --- a/crates/fmt-2/testdata/IfStatement2/fmt.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract IfStatement { - function test() external { - bool anotherLongCondition; - - if (condition && ((condition || anotherLongCondition))) execute(); - } -} diff --git a/crates/fmt-2/testdata/IfStatement2/original.sol b/crates/fmt-2/testdata/IfStatement2/original.sol deleted file mode 100644 index df020c04bbe33..0000000000000 --- a/crates/fmt-2/testdata/IfStatement2/original.sol +++ /dev/null @@ -1,10 +0,0 @@ -contract IfStatement { - - function test() external { - bool anotherLongCondition; - - if (condition && ((condition || anotherLongCondition) - ) - ) execute(); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol deleted file mode 100644 index 1db94929ab7c7..0000000000000 --- a/crates/fmt-2/testdata/ImportDirective/bracket-spacing.fmt.sol +++ /dev/null @@ -1,21 +0,0 @@ -// config: bracket_spacing = true -import "SomeFile.sol"; -import "SomeFile.sol"; -import "SomeFile.sol" as SomeOtherFile; -import "SomeFile.sol" as SomeOtherFile; -import "AnotherFile.sol" as SomeSymbol; -import "AnotherFile.sol" as SomeSymbol; -import { symbol1 as alias, symbol2 } from "File.sol"; -import { symbol1 as alias, symbol2 } from "File.sol"; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from "File2.sol"; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from "File2.sol"; diff --git a/crates/fmt-2/testdata/ImportDirective/fmt.sol b/crates/fmt-2/testdata/ImportDirective/fmt.sol deleted file mode 100644 index 4915b8ab203c8..0000000000000 --- a/crates/fmt-2/testdata/ImportDirective/fmt.sol +++ /dev/null @@ -1,20 +0,0 @@ -import "SomeFile.sol"; -import "SomeFile.sol"; -import "SomeFile.sol" as SomeOtherFile; -import "SomeFile.sol" as SomeOtherFile; -import "AnotherFile.sol" as SomeSymbol; -import "AnotherFile.sol" as SomeSymbol; -import {symbol1 as alias, symbol2} from "File.sol"; -import {symbol1 as alias, symbol2} from "File.sol"; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from "File2.sol"; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from "File2.sol"; diff --git a/crates/fmt-2/testdata/ImportDirective/original.sol b/crates/fmt-2/testdata/ImportDirective/original.sol deleted file mode 100644 index 0e18e10c14dab..0000000000000 --- a/crates/fmt-2/testdata/ImportDirective/original.sol +++ /dev/null @@ -1,10 +0,0 @@ -import "SomeFile.sol"; -import 'SomeFile.sol'; -import "SomeFile.sol" as SomeOtherFile; -import 'SomeFile.sol' as SomeOtherFile; -import * as SomeSymbol from "AnotherFile.sol"; -import * as SomeSymbol from 'AnotherFile.sol'; -import {symbol1 as alias, symbol2} from "File.sol"; -import {symbol1 as alias, symbol2} from 'File.sol'; -import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from "File2.sol"; -import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; diff --git a/crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol b/crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol deleted file mode 100644 index d1bf9852c02e5..0000000000000 --- a/crates/fmt-2/testdata/ImportDirective/preserve-quote.fmt.sol +++ /dev/null @@ -1,21 +0,0 @@ -// config: quote_style = "preserve" -import "SomeFile.sol"; -import 'SomeFile.sol'; -import "SomeFile.sol" as SomeOtherFile; -import 'SomeFile.sol' as SomeOtherFile; -import "AnotherFile.sol" as SomeSymbol; -import 'AnotherFile.sol' as SomeSymbol; -import {symbol1 as alias, symbol2} from "File.sol"; -import {symbol1 as alias, symbol2} from 'File.sol'; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from "File2.sol"; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from 'File2.sol'; diff --git a/crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol b/crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol deleted file mode 100644 index 10449e079ae81..0000000000000 --- a/crates/fmt-2/testdata/ImportDirective/single-quote.fmt.sol +++ /dev/null @@ -1,21 +0,0 @@ -// config: quote_style = "single" -import 'SomeFile.sol'; -import 'SomeFile.sol'; -import 'SomeFile.sol' as SomeOtherFile; -import 'SomeFile.sol' as SomeOtherFile; -import 'AnotherFile.sol' as SomeSymbol; -import 'AnotherFile.sol' as SomeSymbol; -import {symbol1 as alias, symbol2} from 'File.sol'; -import {symbol1 as alias, symbol2} from 'File.sol'; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from 'File2.sol'; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from 'File2.sol'; diff --git a/crates/fmt-2/testdata/InlineDisable/fmt.sol b/crates/fmt-2/testdata/InlineDisable/fmt.sol deleted file mode 100644 index d7adea60b32da..0000000000000 --- a/crates/fmt-2/testdata/InlineDisable/fmt.sol +++ /dev/null @@ -1,507 +0,0 @@ -pragma solidity ^0.5.2; - -// forgefmt: disable-next-line -pragma solidity ^0.5.2; - -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from "File2.sol"; - -// forgefmt: disable-next-line -import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; - -enum States { - State1, - State2, - State3, - State4, - State5, - State6, - State7, - State8, - State9 -} - -// forgefmt: disable-next-line -enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } - -// forgefmt: disable-next-line -bytes32 constant private BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - -// forgefmt: disable-start - -// comment1 - - -// comment2 -/* comment 3 */ /* - comment4 - */ // comment 5 - - -/// Doccomment 1 - /// Doccomment 2 - -/** - * docccoment 3 - */ - - -// forgefmt: disable-end - -// forgefmt: disable-start - -function test1() {} - -function test2() {} - -// forgefmt: disable-end - -contract Constructors is Ownable, Changeable { - //forgefmt: disable-next-item - function Constructors(variable1) public Changeable(variable1) Ownable() onlyOwner { - } - - //forgefmt: disable-next-item - constructor(variable1, variable2, variable3, variable4, variable5, variable6, variable7) public Changeable(variable1, variable2, variable3, variable4, variable5, variable6, variable7) Ownable() onlyOwner {} -} - -function test() { - uint256 pi_approx = 666 / 212; - uint256 pi_approx = /* forgefmt: disable-start */ 666 / 212; /* forgefmt: disable-end */ - - // forgefmt: disable-next-item - uint256 pi_approx = 666 / - 212; - - uint256 test_postfix = 1; // forgefmt: disable-start - // comment1 - // comment2 - // comment3 - // forgefmt: disable-end -} - -// forgefmt: disable-next-item -function testFunc(uint256 num, bytes32 data , address receiver) - public payable attr1 Cool( "hello" ) {} - -function testAttrs(uint256 num, bytes32 data, address receiver) - // forgefmt: disable-next-line - public payable attr1 Cool( "hello" ) {} - -// forgefmt: disable-next-line -function testParams(uint256 num, bytes32 data , address receiver) - public - payable - attr1 - Cool("hello") -{} - -function testDoWhile() external { - //forgefmt: disable-start - uint256 i; - do { "test"; } while (i != 0); - - do - {} - while - ( -i != 0); - - bool someVeryVeryLongCondition; - do { "test"; } while( - someVeryVeryLongCondition && !someVeryVeryLongCondition && -!someVeryVeryLongCondition && -someVeryVeryLongCondition); - - do i++; while(i < 10); - - do do i++; while (i < 30); while(i < 20); - //forgefmt: disable-end -} - -function forStatement() { - //forgefmt: disable-start - for - (uint256 i1 - ; i1 < 10; i1++) - { - i1++; - } - - uint256 i2; - for(++i2;i2<10;i2++) - - {} - - uint256 veryLongVariableName = 1000; - for ( uint256 i3; i3 < 10 - && veryLongVariableName>999 && veryLongVariableName< 1001 - ; i3++) - { i3 ++ ; } - - for (type(uint256).min;;) {} - - for (;;) { "test" ; } - - for (uint256 i4; i4< 10; i4++) i4++; - - for (uint256 i5; ;) - for (uint256 i6 = 10; i6 > i5; i6--) - i5++; - //forgefmt: disable-end -} - -function callArgTest() { - //forgefmt: disable-start - target.run{ gas: gasleft(), value: 1 wei }; - - target.run{gas:1,value:0x00}(); - - target.run{ - gas : 1000, - value: 1 ether - } (); - - target.run{ gas: estimate(), - value: value(1) }(); - - target.run { value: - value(1 ether), gas: veryAndVeryLongNameOfSomeGasEstimateFunction() } (); - - target.run /* comment 1 */ { value: /* comment2 */ 1 }; - - target.run { /* comment3 */ value: 1, // comment4 - gas: gasleft()}; - - target.run { - // comment5 - value: 1, - // comment6 - gas: gasleft()}; - //forgefmt: disable-end -} - -function ifTest() { - // forgefmt: disable-start - if (condition) - execute(); - else - executeElse(); - // forgefmt: disable-end - - /* forgefmt: disable-next-line */ - if (condition && anotherLongCondition ) { - execute(); - } -} - -function yulTest() { - // forgefmt: disable-start - assembly { - let payloadSize := sub(calldatasize(), 4) - calldatacopy(0, 4, payloadSize) - mstore(payloadSize, shl(96, caller())) - - let result := - delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) - - returndatacopy(0, 0, returndatasize()) - - switch result - case 0 { revert(0, returndatasize()) } - default { return(0, returndatasize()) } - } - // forgefmt: disable-end -} - -function literalTest() { - // forgefmt: disable-start - - true; - 0x123_456; - .1; - "foobar"; - hex"001122FF"; - 0xc02aaa39b223Fe8D0A0e5C4F27ead9083c756Cc2; - // forgefmt: disable-end - - // forgefmt: disable-next-line - bytes memory bytecode = hex"ff"; -} - -function returnTest() { - // forgefmt: disable-start - if (val == 0) { - return // return single 1 - 0x00; - } - - if (val == 1) { return - 1; } - - if (val == 2) { - return 3 - - - 1; - } - - if (val == 4) { - /* return single 2 */ return 2** // return single 3 - 3 // return single 4 - ; - } - - return value(); // return single 5 - return ; - return /* return mul 4 */ - ( - 987654321, 1234567890,/* return mul 5 */ false); - // forgefmt: disable-end -} - -function namedFuncCall() { - // forgefmt: disable-start - SimpleStruct memory simple = SimpleStruct({ val: 0 }); - - ComplexStruct memory complex = ComplexStruct({ val: 1, anotherVal: 2, flag: true, timestamp: block.timestamp }); - - StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting memory long = StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ whyNameSoLong: "dunno" }); - - SimpleStruct memory simple2 = SimpleStruct( - { // comment1 - /* comment2 */ val : /* comment3 */ 0 - - } - ); - // forgefmt: disable-end -} - -function revertTest() { - // forgefmt: disable-start - revert ({ }); - - revert EmptyError({}); - - revert SimpleError({ val: 0 }); - - revert ComplexError( - { - val: 0, - ts: block.timestamp, - message: "some reason" - }); - - revert SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ val: 0, ts: 0x00, message: "something unpredictable happened that caused execution to revert"}); - - revert // comment1 - ({}); - // forgefmt: disable-end -} - -function testTernary() { - // forgefmt: disable-start - bool condition; - bool someVeryVeryLongConditionUsedInTheTernaryExpression; - - condition ? 0 : 1; - - someVeryVeryLongConditionUsedInTheTernaryExpression ? 1234567890 : 987654321; - - condition /* comment1 */ ? /* comment2 */ 1001 /* comment3 */ : /* comment4 */ 2002; - - // comment5 - someVeryVeryLongConditionUsedInTheTernaryExpression ? 1 - // comment6 - : - // comment7 - 0; // comment8 - // forgefmt: disable-end -} - -function thisTest() { - // forgefmt: disable-start - this.someFunc(); - this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - this // comment1 - .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - address(this).balance; - - address thisAddress = address( - // comment2 - /* comment3 */ this // comment 4 - ); - // forgefmt: disable-end -} - -function tryTest() { - // forgefmt: disable-start - try unknown.empty() {} catch {} - - try unknown.lookup() returns (uint256) {} catch Error(string memory) {} - - try unknown.lookup() returns (uint256) {} catch Error(string memory) {} catch (bytes memory) {} - - try unknown - .lookup() returns (uint256 - ) { - } catch ( bytes memory ){} - - try unknown.empty() { - unknown.doSomething(); - } catch { - unknown.handleError(); - } - - try unknown.empty() { - unknown.doSomething(); - } catch Error(string memory) {} - catch Panic(uint) {} - catch { - unknown.handleError(); - } - - try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) {} catch Error(string memory) {} catch {} - - try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) { - unknown.doSomething(); - } - catch Error(string memory) { - unknown.handleError(); - } - catch {} - // forgefmt: disable-end -} - -function testArray() { - // forgefmt: disable-start - msg.data[ - // comment1 - 4:]; - msg.data[ - : /* comment2 */ msg.data.length // comment3 - ]; - msg.data[ - // comment4 - 4 // comment5 - :msg.data.length /* comment6 */]; - // forgefmt: disable-end -} - -function testUnit() { - // forgefmt: disable-start - uint256 timestamp; - timestamp = 1 seconds; - timestamp = 1 minutes; - timestamp = 1 hours; - timestamp = 1 days; - timestamp = 1 weeks; - - uint256 value; - value = 1 wei; - value = 1 gwei; - value = 1 ether; - - uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; - - value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue * 1 /* comment1 */ ether; // comment2 - - value = 1 // comment3 - // comment4 - ether; // comment5 - // forgefmt: disable-end -} - -contract UsingExampleContract { - // forgefmt: disable-start - using UsingExampleLibrary for * ; - using UsingExampleLibrary for uint; - using Example.UsingExampleLibrary for uint; - using { M.g, M.f} for uint; - using UsingExampleLibrary for uint global; - using { These, Are, MultipleLibraries, ThatNeedToBePut, OnSeparateLines } for uint; - using { This.isareally.longmember.access.expression.that.needs.to.besplit.into.lines } for uint; - // forgefmt: disable-end -} - -function testAssignment() { - // forgefmt: disable-start - (, uint256 second) = (1, 2); - (uint256 listItem001) = 1; - (uint256 listItem002, uint256 listItem003) = (10, 20); - (uint256 listItem004, uint256 listItem005, uint256 listItem006) = - (10, 20, 30); - // forgefmt: disable-end -} - -function testWhile() { - // forgefmt: disable-start - uint256 i1; - while ( i1 < 10 ) { - i1++; - } - - while (i1<10) i1++; - - while (i1<10) - while (i1<10) - i1++; - - uint256 i2; - while ( i2 < 10) { i2++; } - - uint256 i3; while ( - i3 < 10 - ) { i3++; } - - uint256 i4; while (i4 < 10) - - { i4 ++ ;} - - uint256 someLongVariableName; - while ( - someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12 - ) { someLongVariableName ++; } someLongVariableName++; - // forgefmt: disable-end -} - -function testLine() {} - -function /* forgefmt: disable-line */ testLine( ) { } - -function testLine() {} - -function testLine( ) { } // forgefmt: disable-line - -// forgefmt: disable-start - - type Hello is uint256; - -error - TopLevelCustomError(); - error TopLevelCustomErrorWithArg(uint x) ; -error TopLevelCustomErrorArgWithoutName (string); - - event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); - -// forgefmt: disable-stop - -function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line - number = newNumber; - return (true, true); -} - -function setNumber1(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line -} - -// forgefmt: disable-next-line -function setNumber1(uint256 newNumber , uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public view returns (bool,bool) { number1 = newNumber1; -} - -function setNumber(uint256 newNumber, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public { // forgefmt: disable-line - number = newNumber; - number1 = newNumber1; // forgefmt: disable-line -} diff --git a/crates/fmt-2/testdata/InlineDisable/original.sol b/crates/fmt-2/testdata/InlineDisable/original.sol deleted file mode 100644 index 7731678940cbf..0000000000000 --- a/crates/fmt-2/testdata/InlineDisable/original.sol +++ /dev/null @@ -1,486 +0,0 @@ -pragma solidity ^0.5.2; - -// forgefmt: disable-next-line -pragma solidity ^0.5.2; - -import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; - -// forgefmt: disable-next-line -import {symbol1 as alias1, symbol2 as alias2, symbol3 as alias3, symbol4} from 'File2.sol'; - -enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } - -// forgefmt: disable-next-line -enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } - -// forgefmt: disable-next-line -bytes32 constant private BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - -// forgefmt: disable-start - -// comment1 - - -// comment2 -/* comment 3 */ /* - comment4 - */ // comment 5 - - -/// Doccomment 1 - /// Doccomment 2 - -/** - * docccoment 3 - */ - - -// forgefmt: disable-end - -// forgefmt: disable-start - -function test1() {} - -function test2() {} - -// forgefmt: disable-end - -contract Constructors is Ownable, Changeable { - //forgefmt: disable-next-item - function Constructors(variable1) public Changeable(variable1) Ownable() onlyOwner { - } - - //forgefmt: disable-next-item - constructor(variable1, variable2, variable3, variable4, variable5, variable6, variable7) public Changeable(variable1, variable2, variable3, variable4, variable5, variable6, variable7) Ownable() onlyOwner {} -} - -function test() { - uint256 pi_approx = 666 / 212; - uint256 pi_approx = /* forgefmt: disable-start */ 666 / 212; /* forgefmt: disable-end */ - - // forgefmt: disable-next-item - uint256 pi_approx = 666 / - 212; - - uint256 test_postfix = 1; // forgefmt: disable-start - // comment1 - // comment2 - // comment3 - // forgefmt: disable-end -} - -// forgefmt: disable-next-item -function testFunc(uint256 num, bytes32 data , address receiver) - public payable attr1 Cool( "hello" ) {} - -function testAttrs(uint256 num, bytes32 data , address receiver) - // forgefmt: disable-next-line - public payable attr1 Cool( "hello" ) {} - -// forgefmt: disable-next-line -function testParams(uint256 num, bytes32 data , address receiver) - public payable attr1 Cool( "hello" ) {} - - -function testDoWhile() external { - //forgefmt: disable-start - uint256 i; - do { "test"; } while (i != 0); - - do - {} - while - ( -i != 0); - - bool someVeryVeryLongCondition; - do { "test"; } while( - someVeryVeryLongCondition && !someVeryVeryLongCondition && -!someVeryVeryLongCondition && -someVeryVeryLongCondition); - - do i++; while(i < 10); - - do do i++; while (i < 30); while(i < 20); - //forgefmt: disable-end -} - -function forStatement() { - //forgefmt: disable-start - for - (uint256 i1 - ; i1 < 10; i1++) - { - i1++; - } - - uint256 i2; - for(++i2;i2<10;i2++) - - {} - - uint256 veryLongVariableName = 1000; - for ( uint256 i3; i3 < 10 - && veryLongVariableName>999 && veryLongVariableName< 1001 - ; i3++) - { i3 ++ ; } - - for (type(uint256).min;;) {} - - for (;;) { "test" ; } - - for (uint256 i4; i4< 10; i4++) i4++; - - for (uint256 i5; ;) - for (uint256 i6 = 10; i6 > i5; i6--) - i5++; - //forgefmt: disable-end -} - -function callArgTest() { - //forgefmt: disable-start - target.run{ gas: gasleft(), value: 1 wei }; - - target.run{gas:1,value:0x00}(); - - target.run{ - gas : 1000, - value: 1 ether - } (); - - target.run{ gas: estimate(), - value: value(1) }(); - - target.run { value: - value(1 ether), gas: veryAndVeryLongNameOfSomeGasEstimateFunction() } (); - - target.run /* comment 1 */ { value: /* comment2 */ 1 }; - - target.run { /* comment3 */ value: 1, // comment4 - gas: gasleft()}; - - target.run { - // comment5 - value: 1, - // comment6 - gas: gasleft()}; - //forgefmt: disable-end -} - -function ifTest() { - // forgefmt: disable-start - if (condition) - execute(); - else - executeElse(); - // forgefmt: disable-end - - /* forgefmt: disable-next-line */ - if (condition && anotherLongCondition ) { - execute(); } -} - -function yulTest() { - // forgefmt: disable-start - assembly { - let payloadSize := sub(calldatasize(), 4) - calldatacopy(0, 4, payloadSize) - mstore(payloadSize, shl(96, caller())) - - let result := - delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) - - returndatacopy(0, 0, returndatasize()) - - switch result - case 0 { revert(0, returndatasize()) } - default { return(0, returndatasize()) } - } - // forgefmt: disable-end -} - -function literalTest() { - // forgefmt: disable-start - - true; - 0x123_456; - .1; - "foobar"; - hex"001122FF"; - 0xc02aaa39b223Fe8D0A0e5C4F27ead9083c756Cc2; - // forgefmt: disable-end - - // forgefmt: disable-next-line - bytes memory bytecode = - hex"ff"; -} - -function returnTest() { - // forgefmt: disable-start - if (val == 0) { - return // return single 1 - 0x00; - } - - if (val == 1) { return - 1; } - - if (val == 2) { - return 3 - - - 1; - } - - if (val == 4) { - /* return single 2 */ return 2** // return single 3 - 3 // return single 4 - ; - } - - return value(); // return single 5 - return ; - return /* return mul 4 */ - ( - 987654321, 1234567890,/* return mul 5 */ false); - // forgefmt: disable-end -} - -function namedFuncCall() { - // forgefmt: disable-start - SimpleStruct memory simple = SimpleStruct({ val: 0 }); - - ComplexStruct memory complex = ComplexStruct({ val: 1, anotherVal: 2, flag: true, timestamp: block.timestamp }); - - StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting memory long = StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ whyNameSoLong: "dunno" }); - - SimpleStruct memory simple2 = SimpleStruct( - { // comment1 - /* comment2 */ val : /* comment3 */ 0 - - } - ); - // forgefmt: disable-end -} - -function revertTest() { - // forgefmt: disable-start - revert ({ }); - - revert EmptyError({}); - - revert SimpleError({ val: 0 }); - - revert ComplexError( - { - val: 0, - ts: block.timestamp, - message: "some reason" - }); - - revert SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ val: 0, ts: 0x00, message: "something unpredictable happened that caused execution to revert"}); - - revert // comment1 - ({}); - // forgefmt: disable-end -} - -function testTernary() { - // forgefmt: disable-start - bool condition; - bool someVeryVeryLongConditionUsedInTheTernaryExpression; - - condition ? 0 : 1; - - someVeryVeryLongConditionUsedInTheTernaryExpression ? 1234567890 : 987654321; - - condition /* comment1 */ ? /* comment2 */ 1001 /* comment3 */ : /* comment4 */ 2002; - - // comment5 - someVeryVeryLongConditionUsedInTheTernaryExpression ? 1 - // comment6 - : - // comment7 - 0; // comment8 - // forgefmt: disable-end -} - -function thisTest() { - // forgefmt: disable-start - this.someFunc(); - this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - this // comment1 - .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - address(this).balance; - - address thisAddress = address( - // comment2 - /* comment3 */ this // comment 4 - ); - // forgefmt: disable-end -} - -function tryTest() { - // forgefmt: disable-start - try unknown.empty() {} catch {} - - try unknown.lookup() returns (uint256) {} catch Error(string memory) {} - - try unknown.lookup() returns (uint256) {} catch Error(string memory) {} catch (bytes memory) {} - - try unknown - .lookup() returns (uint256 - ) { - } catch ( bytes memory ){} - - try unknown.empty() { - unknown.doSomething(); - } catch { - unknown.handleError(); - } - - try unknown.empty() { - unknown.doSomething(); - } catch Error(string memory) {} - catch Panic(uint) {} - catch { - unknown.handleError(); - } - - try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) {} catch Error(string memory) {} catch {} - - try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) { - unknown.doSomething(); - } - catch Error(string memory) { - unknown.handleError(); - } - catch {} - // forgefmt: disable-end -} - -function testArray() { - // forgefmt: disable-start - msg.data[ - // comment1 - 4:]; - msg.data[ - : /* comment2 */ msg.data.length // comment3 - ]; - msg.data[ - // comment4 - 4 // comment5 - :msg.data.length /* comment6 */]; - // forgefmt: disable-end -} - -function testUnit() { - // forgefmt: disable-start - uint256 timestamp; - timestamp = 1 seconds; - timestamp = 1 minutes; - timestamp = 1 hours; - timestamp = 1 days; - timestamp = 1 weeks; - - uint256 value; - value = 1 wei; - value = 1 gwei; - value = 1 ether; - - uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; - - value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue * 1 /* comment1 */ ether; // comment2 - - value = 1 // comment3 - // comment4 - ether; // comment5 - // forgefmt: disable-end -} - -contract UsingExampleContract { - // forgefmt: disable-start - using UsingExampleLibrary for * ; - using UsingExampleLibrary for uint; - using Example.UsingExampleLibrary for uint; - using { M.g, M.f} for uint; - using UsingExampleLibrary for uint global; - using { These, Are, MultipleLibraries, ThatNeedToBePut, OnSeparateLines } for uint; - using { This.isareally.longmember.access.expression.that.needs.to.besplit.into.lines } for uint; - // forgefmt: disable-end -} - -function testAssignment() { - // forgefmt: disable-start - (, uint256 second) = (1, 2); - (uint256 listItem001) = 1; - (uint256 listItem002, uint256 listItem003) = (10, 20); - (uint256 listItem004, uint256 listItem005, uint256 listItem006) = - (10, 20, 30); - // forgefmt: disable-end -} - -function testWhile() { - // forgefmt: disable-start - uint256 i1; - while ( i1 < 10 ) { - i1++; - } - - while (i1<10) i1++; - - while (i1<10) - while (i1<10) - i1++; - - uint256 i2; - while ( i2 < 10) { i2++; } - - uint256 i3; while ( - i3 < 10 - ) { i3++; } - - uint256 i4; while (i4 < 10) - - { i4 ++ ;} - - uint256 someLongVariableName; - while ( - someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12 - ) { someLongVariableName ++; } someLongVariableName++; - // forgefmt: disable-end -} - -function testLine( ) { } -function /* forgefmt: disable-line */ testLine( ) { } -function testLine( ) { } -function testLine( ) { } // forgefmt: disable-line - -// forgefmt: disable-start - - type Hello is uint256; - -error - TopLevelCustomError(); - error TopLevelCustomErrorWithArg(uint x) ; -error TopLevelCustomErrorArgWithoutName (string); - - event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); - -// forgefmt: disable-stop - -function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line - number = newNumber; - return (true, true); -} - -function setNumber1(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line -} - -// forgefmt: disable-next-line -function setNumber1(uint256 newNumber , uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public view returns (bool,bool) { number1 = newNumber1; -} - -function setNumber(uint256 newNumber, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf) public { // forgefmt: disable-line - number = newNumber; - number1 = newNumber1; // forgefmt: disable-line -} diff --git a/crates/fmt-2/testdata/IntTypes/fmt.sol b/crates/fmt-2/testdata/IntTypes/fmt.sol deleted file mode 100644 index b244d0281d2e7..0000000000000 --- a/crates/fmt-2/testdata/IntTypes/fmt.sol +++ /dev/null @@ -1,24 +0,0 @@ -contract Contract { - uint256 constant UINT256_IMPL = 0; - uint8 constant UINT8 = 1; - uint128 constant UINT128 = 2; - uint256 constant UINT256_EXPL = 3; - - int256 constant INT256_IMPL = 4; - int8 constant INT8 = 5; - int128 constant INT128 = 6; - int256 constant INT256_EXPL = 7; - - function test( - uint256 uint256_impl, - uint8 uint8_var, - uint128 uint128_var, - uint256 uint256_expl, - int256 int256_impl, - int8 int8_var, - int128 int128_var, - int256 int256_expl - ) public { - // do something - } -} diff --git a/crates/fmt-2/testdata/IntTypes/original.sol b/crates/fmt-2/testdata/IntTypes/original.sol deleted file mode 100644 index 2990aadbb979d..0000000000000 --- a/crates/fmt-2/testdata/IntTypes/original.sol +++ /dev/null @@ -1,24 +0,0 @@ -contract Contract { - uint constant UINT256_IMPL = 0; - uint8 constant UINT8 = 1; - uint128 constant UINT128 = 2; - uint256 constant UINT256_EXPL = 3; - - int constant INT256_IMPL = 4; - int8 constant INT8 = 5; - int128 constant INT128 = 6; - int256 constant INT256_EXPL = 7; - - function test( - uint uint256_impl, - uint8 uint8_var, - uint128 uint128_var, - uint256 uint256_expl, - int int256_impl, - int8 int8_var, - int128 int128_var, - int256 int256_expl - ) public { - // do something - } -} diff --git a/crates/fmt-2/testdata/IntTypes/preserve.fmt.sol b/crates/fmt-2/testdata/IntTypes/preserve.fmt.sol deleted file mode 100644 index 70d780d89e329..0000000000000 --- a/crates/fmt-2/testdata/IntTypes/preserve.fmt.sol +++ /dev/null @@ -1,25 +0,0 @@ -// config: int_types = "preserve" -contract Contract { - uint constant UINT256_IMPL = 0; - uint8 constant UINT8 = 1; - uint128 constant UINT128 = 2; - uint256 constant UINT256_EXPL = 3; - - int constant INT256_IMPL = 4; - int8 constant INT8 = 5; - int128 constant INT128 = 6; - int256 constant INT256_EXPL = 7; - - function test( - uint uint256_impl, - uint8 uint8_var, - uint128 uint128_var, - uint256 uint256_expl, - int int256_impl, - int8 int8_var, - int128 int128_var, - int256 int256_expl - ) public { - // do something - } -} diff --git a/crates/fmt-2/testdata/IntTypes/short.fmt.sol b/crates/fmt-2/testdata/IntTypes/short.fmt.sol deleted file mode 100644 index 45064d41e34ed..0000000000000 --- a/crates/fmt-2/testdata/IntTypes/short.fmt.sol +++ /dev/null @@ -1,25 +0,0 @@ -// config: int_types = "short" -contract Contract { - uint constant UINT256_IMPL = 0; - uint8 constant UINT8 = 1; - uint128 constant UINT128 = 2; - uint constant UINT256_EXPL = 3; - - int constant INT256_IMPL = 4; - int8 constant INT8 = 5; - int128 constant INT128 = 6; - int constant INT256_EXPL = 7; - - function test( - uint uint256_impl, - uint8 uint8_var, - uint128 uint128_var, - uint uint256_expl, - int int256_impl, - int8 int8_var, - int128 int128_var, - int int256_expl - ) public { - // do something - } -} diff --git a/crates/fmt-2/testdata/LiteralExpression/fmt.sol b/crates/fmt-2/testdata/LiteralExpression/fmt.sol deleted file mode 100644 index 7fa6878e5586d..0000000000000 --- a/crates/fmt-2/testdata/LiteralExpression/fmt.sol +++ /dev/null @@ -1,59 +0,0 @@ -contract LiteralExpressions { - function test() external { - // bool literals - true; - false; - /* comment1 */ - true; /* comment2 */ - // comment3 - false; // comment4 - - // number literals - 1; - 123_000; - 1_2e345_678; - -1; - 2e-10; - // comment5 - /* comment6 */ - -1; /* comment7 */ - - // hex number literals - 0x00; - 0x123_456; - 0x2eff_abde; - - // rational number literals - 0.1; - 1.3; - 2.5e1; - - // string literals - ""; - "foobar"; - "foo" // comment8 - " bar"; - // comment9 - "\ -some words"; /* comment10 */ - unicode"Hello 😃"; - - // quoted strings - 'hello "world"'; - "hello 'world'"; - "hello \'world\'"; - "hello \"world\""; - "hello \"world\""; - "hello \'world\'"; - - // hex literals - hex"001122FF"; - hex"001122FF"; - hex"00112233" hex"44556677"; - - // address literals - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - // non checksummed address - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - } -} diff --git a/crates/fmt-2/testdata/LiteralExpression/original.sol b/crates/fmt-2/testdata/LiteralExpression/original.sol deleted file mode 100644 index 5c559531de955..0000000000000 --- a/crates/fmt-2/testdata/LiteralExpression/original.sol +++ /dev/null @@ -1,58 +0,0 @@ -contract LiteralExpressions { - function test() external { - // bool literals - true; - false; - /* comment1 */ true /* comment2 */; - // comment3 - false; // comment4 - - // number literals - 1; - 123_000; - 1_2e345_678; - -1; - 2e-10; - // comment5 - /* comment6 */ -1 /* comment7 */; - - // hex number literals - 0x00; - 0x123_456; - 0x2eff_abde; - - // rational number literals - .1; - 1.3; - 2.5e1; - - // string literals - ""; - "foobar"; - "foo" // comment8 - " bar"; - // comment9 - "\ -some words" /* comment10 */; - unicode"Hello 😃"; - - // quoted strings - 'hello "world"'; - "hello 'world'"; - 'hello \'world\''; - "hello \"world\""; - 'hello \"world\"'; - "hello \'world\'"; - - - // hex literals - hex"001122FF"; - hex'0011_22_FF'; - hex"00112233" hex"44556677"; - - // address literals - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - // non checksummed address - 0xc02aaa39b223Fe8D0A0e5C4F27ead9083c756Cc2; - } -} diff --git a/crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol b/crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol deleted file mode 100644 index 3d9490804f231..0000000000000 --- a/crates/fmt-2/testdata/LiteralExpression/preserve-quote.fmt.sol +++ /dev/null @@ -1,60 +0,0 @@ -// config: quote_style = "preserve" -contract LiteralExpressions { - function test() external { - // bool literals - true; - false; - /* comment1 */ - true; /* comment2 */ - // comment3 - false; // comment4 - - // number literals - 1; - 123_000; - 1_2e345_678; - -1; - 2e-10; - // comment5 - /* comment6 */ - -1; /* comment7 */ - - // hex number literals - 0x00; - 0x123_456; - 0x2eff_abde; - - // rational number literals - 0.1; - 1.3; - 2.5e1; - - // string literals - ""; - "foobar"; - "foo" // comment8 - " bar"; - // comment9 - "\ -some words"; /* comment10 */ - unicode"Hello 😃"; - - // quoted strings - 'hello "world"'; - "hello 'world'"; - 'hello \'world\''; - "hello \"world\""; - 'hello \"world\"'; - "hello \'world\'"; - - // hex literals - hex"001122FF"; - hex'001122FF'; - hex"00112233" hex"44556677"; - - // address literals - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - // non checksummed address - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - } -} diff --git a/crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol b/crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol deleted file mode 100644 index cdc67a2c6c814..0000000000000 --- a/crates/fmt-2/testdata/LiteralExpression/single-quote.fmt.sol +++ /dev/null @@ -1,60 +0,0 @@ -// config: quote_style = "single" -contract LiteralExpressions { - function test() external { - // bool literals - true; - false; - /* comment1 */ - true; /* comment2 */ - // comment3 - false; // comment4 - - // number literals - 1; - 123_000; - 1_2e345_678; - -1; - 2e-10; - // comment5 - /* comment6 */ - -1; /* comment7 */ - - // hex number literals - 0x00; - 0x123_456; - 0x2eff_abde; - - // rational number literals - 0.1; - 1.3; - 2.5e1; - - // string literals - ''; - 'foobar'; - 'foo' // comment8 - ' bar'; - // comment9 - '\ -some words'; /* comment10 */ - unicode'Hello 😃'; - - // quoted strings - 'hello "world"'; - "hello 'world'"; - 'hello \'world\''; - 'hello \"world\"'; - 'hello \"world\"'; - 'hello \'world\''; - - // hex literals - hex'001122FF'; - hex'001122FF'; - hex'00112233' hex'44556677'; - - // address literals - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - // non checksummed address - 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - } -} diff --git a/crates/fmt-2/testdata/MappingType/fmt.sol b/crates/fmt-2/testdata/MappingType/fmt.sol deleted file mode 100644 index 7f6297cff9455..0000000000000 --- a/crates/fmt-2/testdata/MappingType/fmt.sol +++ /dev/null @@ -1,35 +0,0 @@ -// config: line_length = 40 -contract X { - type Y is bytes32; -} - -type SomeVeryLongTypeName is uint256; - -contract Mapping { - mapping(uint256 => X.Y) mapping1; - mapping( - uint256 key => uint256 value - ) mapping2; - mapping( - uint256 veryLongKeyName - => uint256 veryLongValueName - ) mapping3; - mapping( - string anotherVeryLongKeyName - => uint256 anotherVeryLongValueName - ) mapping4; - mapping( - SomeVeryLongTypeName anotherVeryLongKeyName - => uint256 anotherVeryLongValueName - ) mapping5; - - mapping( - // comment1 - uint256 key => uint256 value - // comment2 - ) mapping6; - mapping( /* comment3 */ - uint256 /* comment4 */ key /* comment5 */ - => /* comment6 */ uint256 /* comment7 */ value /* comment8 */ /* comment9 */ - ) /* comment10 */ mapping7; -} diff --git a/crates/fmt-2/testdata/MappingType/original.sol b/crates/fmt-2/testdata/MappingType/original.sol deleted file mode 100644 index 58ebb134d6684..0000000000000 --- a/crates/fmt-2/testdata/MappingType/original.sol +++ /dev/null @@ -1,23 +0,0 @@ -contract X { - type Y is bytes32; -} - -type SomeVeryLongTypeName is uint256; - -contract Mapping { - mapping(uint256 => X.Y) mapping1; - mapping(uint256 key => uint256 value) mapping2; - mapping(uint256 veryLongKeyName => uint256 veryLongValueName) mapping3; - mapping(string anotherVeryLongKeyName => uint256 anotherVeryLongValueName) mapping4; - mapping(SomeVeryLongTypeName anotherVeryLongKeyName => uint256 anotherVeryLongValueName) mapping5; - - mapping( - - // comment1 - uint256 key => uint256 value -// comment2 - ) mapping6; - mapping( /* comment3 */ - uint256 /* comment4 */ key /* comment5 */ => /* comment6 */ uint256 /* comment7 */ value /* comment8 */ /* comment9 */ - ) /* comment10 */ mapping7; -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/ModifierDefinition/fmt.sol b/crates/fmt-2/testdata/ModifierDefinition/fmt.sol deleted file mode 100644 index e735f25b4c51d..0000000000000 --- a/crates/fmt-2/testdata/ModifierDefinition/fmt.sol +++ /dev/null @@ -1,14 +0,0 @@ -// config: line_length = 60 -contract ModifierDefinitions { - modifier noParams() {} - modifier oneParam(uint256 a) {} - modifier twoParams(uint256 a, uint256 b) {} - modifier threeParams(uint256 a, uint256 b, uint256 c) {} - modifier fourParams( - uint256 a, - uint256 b, - uint256 c, - uint256 d - ) {} - modifier overridden() override(Base1, Base2) {} -} diff --git a/crates/fmt-2/testdata/ModifierDefinition/original.sol b/crates/fmt-2/testdata/ModifierDefinition/original.sol deleted file mode 100644 index f4a1c428429bb..0000000000000 --- a/crates/fmt-2/testdata/ModifierDefinition/original.sol +++ /dev/null @@ -1,9 +0,0 @@ -contract ModifierDefinitions { - modifier noParams() {} - modifier oneParam(uint a) {} - modifier twoParams(uint a,uint b) {} - modifier threeParams(uint a,uint b ,uint c) {} - modifier fourParams(uint a,uint b ,uint c, uint d) {} - modifier overridden ( - ) override ( Base1 , Base2) {} -} diff --git a/crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol b/crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol deleted file mode 100644 index 68906fcdc0f65..0000000000000 --- a/crates/fmt-2/testdata/ModifierDefinition/override-spacing.fmt.sol +++ /dev/null @@ -1,15 +0,0 @@ -// config: line_length = 60 -// config: override_spacing = true -contract ModifierDefinitions { - modifier noParams() {} - modifier oneParam(uint256 a) {} - modifier twoParams(uint256 a, uint256 b) {} - modifier threeParams(uint256 a, uint256 b, uint256 c) {} - modifier fourParams( - uint256 a, - uint256 b, - uint256 c, - uint256 d - ) {} - modifier overridden() override (Base1, Base2) {} -} diff --git a/crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol b/crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol deleted file mode 100644 index 14a24c9003888..0000000000000 --- a/crates/fmt-2/testdata/NamedFunctionCallExpression/fmt.sol +++ /dev/null @@ -1,47 +0,0 @@ -contract NamedFunctionCallExpression { - struct SimpleStruct { - uint256 val; - } - - struct ComplexStruct { - uint256 val; - uint256 anotherVal; - bool flag; - uint256 timestamp; - } - - struct - StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting { - string whyNameSoLong; - } - - function test() external { - SimpleStruct memory simple = SimpleStruct({val: 0}); - - ComplexStruct memory complex = ComplexStruct({ - val: 1, - anotherVal: 2, - flag: true, - timestamp: block.timestamp - }); - - StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting - memory long = - StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ - whyNameSoLong: "dunno" - }); - - SimpleStruct memory simple2 = SimpleStruct({ // comment1 - /* comment2 */ - val: /* comment3 */ 0 - }); - - SimpleStruct memory simple3 = SimpleStruct({ - /* comment4 */ - // comment5 - val: // comment6 - 0 // comment7 - // comment8 - }); - } -} diff --git a/crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol b/crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol deleted file mode 100644 index 8b34474a7134f..0000000000000 --- a/crates/fmt-2/testdata/NamedFunctionCallExpression/original.sol +++ /dev/null @@ -1,28 +0,0 @@ -contract NamedFunctionCallExpression { - struct SimpleStruct { uint256 val; } - struct ComplexStruct { uint256 val; uint256 anotherVal; bool flag; uint256 timestamp; } - struct StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting { string whyNameSoLong; } - - function test() external { - SimpleStruct memory simple = SimpleStruct({ val: 0 }); - - ComplexStruct memory complex = ComplexStruct({ val: 1, anotherVal: 2, flag: true, timestamp: block.timestamp }); - - StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting memory long = StructWithAVeryLongNameThatExceedsMaximumLengthThatIsAllowedForFormatting({ whyNameSoLong: "dunno" }); - - SimpleStruct memory simple2 = SimpleStruct( - { // comment1 - /* comment2 */ val : /* comment3 */ 0 - - } - ); - - SimpleStruct memory simple3 = SimpleStruct( - /* comment4 */ { - // comment5 - val: // comment6 - 0 // comment7 - // comment8 - }); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol deleted file mode 100644 index 7c9f5740de76a..0000000000000 --- a/crates/fmt-2/testdata/NumberLiteralUnderscore/fmt.sol +++ /dev/null @@ -1,25 +0,0 @@ -contract NumberLiteral { - function test() external { - 1; - 123_000; - 1_2e345_678; - -1; - 2e-10; - 0.1; - 1.3; - 2.5e1; - 1.23454; - 1.2e34_5_678; - 134411.2e34_5_678; - 13431.134112e34_135_678; - 13431.0134112; - 13431.134112e-139_3141340; - 134411.2e34_5_6780; - 13431.134112e34_135_6780; - 0.134112; - 1.0; - 13431.134112e-139_3141340; - 123e456; - 1_000; - } -} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol deleted file mode 100644 index 8e88fc6d29472..0000000000000 --- a/crates/fmt-2/testdata/NumberLiteralUnderscore/original.sol +++ /dev/null @@ -1,25 +0,0 @@ -contract NumberLiteral { - function test() external { - 1; - 123_000; - 1_2e345_678; - -1; - 2e-10; - .1; - 1.3; - 2.5e1; - 1.23454e0; - 1.2e34_5_678; - 134411.2e34_5_678; - 13431.134112e34_135_678; - 13431.0134112; - 13431.134112e-139_3141340; - 00134411.200e0034_5_6780; - 013431.13411200e34_135_6780; - 00.1341120000; - 1.0000; - 0013431.13411200e-00139_3141340; - 123E456; - 1_000; - } -} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol deleted file mode 100644 index d87dc99d9653d..0000000000000 --- a/crates/fmt-2/testdata/NumberLiteralUnderscore/preserve.fmt.sol +++ /dev/null @@ -1,26 +0,0 @@ -// config: number_underscore = "preserve" -contract NumberLiteral { - function test() external { - 1; - 123_000; - 1_2e345_678; - -1; - 2e-10; - 0.1; - 1.3; - 2.5e1; - 1.23454; - 1.2e34_5_678; - 134411.2e34_5_678; - 13431.134112e34_135_678; - 13431.0134112; - 13431.134112e-139_3141340; - 134411.2e34_5_6780; - 13431.134112e34_135_6780; - 0.134112; - 1.0; - 13431.134112e-139_3141340; - 123e456; - 1_000; - } -} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol deleted file mode 100644 index cbde2e9b9fe61..0000000000000 --- a/crates/fmt-2/testdata/NumberLiteralUnderscore/remove.fmt.sol +++ /dev/null @@ -1,26 +0,0 @@ -// config: number_underscore = "remove" -contract NumberLiteral { - function test() external { - 1; - 123000; - 12e345678; - -1; - 2e-10; - 0.1; - 1.3; - 2.5e1; - 1.23454; - 1.2e345678; - 134411.2e345678; - 13431.134112e34135678; - 13431.0134112; - 13431.134112e-1393141340; - 134411.2e3456780; - 13431.134112e341356780; - 0.134112; - 1.0; - 13431.134112e-1393141340; - 123e456; - 1000; - } -} diff --git a/crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol b/crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol deleted file mode 100644 index a9fc8a69ae6fa..0000000000000 --- a/crates/fmt-2/testdata/NumberLiteralUnderscore/thousands.fmt.sol +++ /dev/null @@ -1,26 +0,0 @@ -// config: number_underscore = "thousands" -contract NumberLiteral { - function test() external { - 1; - 123_000; - 12e345_678; - -1; - 2e-10; - 0.1; - 1.3; - 2.5e1; - 1.23454; - 1.2e345_678; - 134_411.2e345_678; - 13_431.134112e34_135_678; - 13_431.0134112; - 13_431.134112e-1_393_141_340; - 134_411.2e3_456_780; - 13_431.134112e341_356_780; - 0.134112; - 1.0; - 13_431.134112e-1_393_141_340; - 123e456; - 1000; - } -} diff --git a/crates/fmt-2/testdata/OperatorExpressions/fmt.sol b/crates/fmt-2/testdata/OperatorExpressions/fmt.sol deleted file mode 100644 index be0cec9173d55..0000000000000 --- a/crates/fmt-2/testdata/OperatorExpressions/fmt.sol +++ /dev/null @@ -1,43 +0,0 @@ -function test() { - uint256 expr001 = (1 + 2) + 3; - uint256 expr002 = 1 + (2 + 3); - uint256 expr003 = 1 * 2 + 3; - uint256 expr004 = (1 * 2) + 3; - uint256 expr005 = 1 * (2 + 3); - uint256 expr006 = 1 + 2 * 3; - uint256 expr007 = (1 + 2) * 3; - uint256 expr008 = 1 + (2 * 3); - uint256 expr009 = 1 ** 2 ** 3; - uint256 expr010 = 1 ** (2 ** 3); - uint256 expr011 = (1 ** 2) ** 3; - uint256 expr012 = ++expr011 + 1; - bool expr013 = ++expr012 == expr011 - 1; - bool expr014 = ++(++expr013)--; - if (++batch.movesPerformed == drivers.length) createNewBatch(); - sum += getPrice( - ACCELERATE_STARTING_PRICE, - ACCELERATE_PER_PERIOD_DECREASE, - idleTicks, - actionsSold[ActionType.ACCELERATE] + i, - ACCELERATE_SELL_PER_TICK - ) / 1e18; - other += 1e18 - / getPrice( - ACCELERATE_STARTING_PRICE, - ACCELERATE_PER_PERIOD_DECREASE, - idleTicks, - actionsSold[ActionType.ACCELERATE] + i, - ACCELERATE_SELL_PER_TICK - ); - if ( - op == 0x54 // SLOAD - || op == 0x55 // SSTORE - || op == 0xF0 // CREATE - || op == 0xF1 // CALL - || op == 0xF2 // CALLCODE - || op == 0xF4 // DELEGATECALL - || op == 0xF5 // CREATE2 - || op == 0xFA // STATICCALL - || op == 0xFF // SELFDESTRUCT - ) return false; -} diff --git a/crates/fmt-2/testdata/OperatorExpressions/original.sol b/crates/fmt-2/testdata/OperatorExpressions/original.sol deleted file mode 100644 index c3e788d0a9385..0000000000000 --- a/crates/fmt-2/testdata/OperatorExpressions/original.sol +++ /dev/null @@ -1,30 +0,0 @@ -function test() { - uint256 expr001 = (1 + 2) + 3; - uint256 expr002 = 1 + (2 + 3); - uint256 expr003 = 1 * 2 + 3; - uint256 expr004 = (1 * 2) + 3; - uint256 expr005 = 1 * (2 + 3); - uint256 expr006 = 1 + 2 * 3; - uint256 expr007 = (1 + 2) * 3; - uint256 expr008 = 1 + (2 * 3); - uint256 expr009 = 1 ** 2 ** 3; - uint256 expr010 = 1 ** (2 ** 3); - uint256 expr011 = (1 ** 2) ** 3; - uint256 expr012 = ++expr011 + 1; - bool expr013 = ++expr012 == expr011 - 1; - bool expr014 = ++(++expr013)--; - if (++batch.movesPerformed == drivers.length) createNewBatch(); - sum += getPrice(ACCELERATE_STARTING_PRICE, ACCELERATE_PER_PERIOD_DECREASE, idleTicks, actionsSold[ActionType.ACCELERATE] + i, ACCELERATE_SELL_PER_TICK) / 1e18; - other += 1e18 / getPrice(ACCELERATE_STARTING_PRICE, ACCELERATE_PER_PERIOD_DECREASE, idleTicks, actionsSold[ActionType.ACCELERATE] + i, ACCELERATE_SELL_PER_TICK); - if ( - op == 0x54 // SLOAD - || op == 0x55 // SSTORE - || op == 0xF0 // CREATE - || op == 0xF1 // CALL - || op == 0xF2 // CALLCODE - || op == 0xF4 // DELEGATECALL - || op == 0xF5 // CREATE2 - || op == 0xFA // STATICCALL - || op == 0xFF // SELFDESTRUCT - ) return false; -} diff --git a/crates/fmt-2/testdata/PragmaDirective/fmt.sol b/crates/fmt-2/testdata/PragmaDirective/fmt.sol deleted file mode 100644 index 645a4f4906c00..0000000000000 --- a/crates/fmt-2/testdata/PragmaDirective/fmt.sol +++ /dev/null @@ -1,9 +0,0 @@ -pragma solidity 0.8.17; -pragma experimental ABIEncoderV2; - -contract Contract {} - -// preserves lines -pragma solidity 0.8.17; - -pragma experimental ABIEncoderV2; diff --git a/crates/fmt-2/testdata/PragmaDirective/original.sol b/crates/fmt-2/testdata/PragmaDirective/original.sol deleted file mode 100644 index 535b709590908..0000000000000 --- a/crates/fmt-2/testdata/PragmaDirective/original.sol +++ /dev/null @@ -1,9 +0,0 @@ -pragma solidity 0.8.17; -pragma experimental ABIEncoderV2; - -contract Contract {} - -// preserves lines -pragma solidity 0.8.17; - -pragma experimental ABIEncoderV2; \ No newline at end of file diff --git a/crates/fmt-2/testdata/Repros/fmt.sol b/crates/fmt-2/testdata/Repros/fmt.sol deleted file mode 100644 index 0a480c0b02bdf..0000000000000 --- a/crates/fmt-2/testdata/Repros/fmt.sol +++ /dev/null @@ -1,161 +0,0 @@ -// Repros of fmt issues - -// https://github.com/foundry-rs/foundry/issues/4403 -function errorIdentifier() { - bytes memory error = bytes(""); - if (error.length > 0) {} -} - -// https://github.com/foundry-rs/foundry/issues/7549 -function one() external { - this.other({ - data: abi.encodeCall( - this.other, - ( - "bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla" - ) - ) - }); -} - -// https://github.com/foundry-rs/foundry/issues/3979 -contract Format { - bool public test; - - function testing(uint256 amount) public payable { - if ( - // This is a comment - msg.value == amount - ) { - test = true; - } else { - test = false; - } - - if ( - // Another one - block.timestamp >= amount - ) {} - } -} - -// https://github.com/foundry-rs/foundry/issues/3830 -contract TestContract { - function test(uint256 a) public { - if (a > 1) { - a = 2; - } // forgefmt: disable-line - } - - function test1() public { - assembly{ sstore( 1, 1) /* inline comment*/ // forgefmt: disable-line - sstore(2, 2) - } - } - - function test2() public { - assembly{ sstore( 1, 1) // forgefmt: disable-line - sstore(2, 2) - sstore(3, 3)// forgefmt: disable-line - sstore(4, 4) - } - } - - function test3() public { - // forgefmt: disable-next-line - assembly{ sstore( 1, 1) - sstore(2, 2) - sstore(3, 3)// forgefmt: disable-line - sstore(4, 4) - }// forgefmt: disable-line - } - - function test4() public { - // forgefmt: disable-next-line - assembly{ - sstore(1, 1) - sstore(2, 2) - sstore(3, 3)// forgefmt: disable-line - sstore(4, 4) - }// forgefmt: disable-line - if (condition) execute(); // comment7 - } - - function test5() public { - assembly { sstore(0, 0) }// forgefmt: disable-line - } - - function test6() returns (bool) { // forgefmt: disable-line - if ( true ) { // forgefmt: disable-line - } - return true ; } // forgefmt: disable-line - - function test7() returns (bool) { // forgefmt: disable-line - if (true) { // forgefmt: disable-line - uint256 a = 1; // forgefmt: disable-line - } - return true; - } - - function test8() returns (bool) { // forgefmt: disable-line - if ( true ) { // forgefmt: disable-line - uint256 a = 1; - } else { - uint256 b = 1; // forgefmt: disable-line - } - return true; - } -} - -// https://github.com/foundry-rs/foundry/issues/5825 -library MyLib { - bytes32 private constant TYPE_HASH = keccak256( - // forgefmt: disable-start - "MyStruct(" - "uint8 myEnum," - "address myAddress" - ")" - // forgefmt: disable-end - ); - - bytes32 private constant TYPE_HASH_1 = keccak256( - "MyStruct(" "uint8 myEnum," "address myAddress" ")" // forgefmt: disable-line - ); - - // forgefmt: disable-start - bytes32 private constant TYPE_HASH_2 = keccak256( - "MyStruct(" - "uint8 myEnum," - "address myAddress" - ")" - ); - // forgefmt: disable-end -} - -contract IfElseTest { - function setNumber(uint256 newNumber) public { - number = newNumber; - if (newNumber = 1) { - number = 1; - } else if (newNumber = 2) { - // number = 2; - } else { - newNumber = 3; - } - } -} - -contract DbgFmtTest is Test { - function test_argsList() public { - uint256 result1 = internalNoArgs({}); - result2 = add({a: 1, b: 2}); - } - - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - function internalNoArgs() internal pure returns (uint256) { - return 0; - } -} diff --git a/crates/fmt-2/testdata/Repros/original.sol b/crates/fmt-2/testdata/Repros/original.sol deleted file mode 100644 index 6f18784d39dea..0000000000000 --- a/crates/fmt-2/testdata/Repros/original.sol +++ /dev/null @@ -1,160 +0,0 @@ -// Repros of fmt issues - -// https://github.com/foundry-rs/foundry/issues/4403 -function errorIdentifier() { - bytes memory error = bytes(""); - if (error.length > 0) {} -} - -// https://github.com/foundry-rs/foundry/issues/7549 -function one() external { - this.other({ - data: abi.encodeCall( - this.other, - ( - "bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla" - ) - ) - }); -} - -// https://github.com/foundry-rs/foundry/issues/3979 -contract Format { - bool public test; - - function testing(uint256 amount) public payable { - if ( - // This is a comment - msg.value == amount - ) { - test = true; - } else { - test = false; - } - - if ( - // Another one - block.timestamp >= amount - ) {} - } -} - -// https://github.com/foundry-rs/foundry/issues/3830 -contract TestContract { - function test(uint256 a) public { - if (a > 1) { - a = 2; - } // forgefmt: disable-line - } - - function test1() public { - assembly { sstore( 1, 1) /* inline comment*/ // forgefmt: disable-line - sstore(2, 2) - } - } - - function test2() public { - assembly { sstore( 1, 1) // forgefmt: disable-line - sstore(2, 2) - sstore(3, 3) // forgefmt: disable-line - sstore(4, 4) - } - } - - function test3() public { - // forgefmt: disable-next-line - assembly{ sstore( 1, 1) - sstore(2, 2) - sstore(3, 3) // forgefmt: disable-line - sstore(4, 4) - }// forgefmt: disable-line - } - - function test4() public { - // forgefmt: disable-next-line - assembly { - sstore( 1, 1) - sstore(2, 2) - sstore(3, 3) // forgefmt: disable-line - sstore(4, 4) - }// forgefmt: disable-line - if (condition) execute(); // comment7 - } - - function test5() public { - assembly { sstore(0, 0) }// forgefmt: disable-line - } - - function test6() returns (bool) { // forgefmt: disable-line - if ( true ) { // forgefmt: disable-line - } - return true ; } // forgefmt: disable-line - - function test7() returns (bool) { // forgefmt: disable-line - if (true) { // forgefmt: disable-line - uint256 a = 1; // forgefmt: disable-line - } - return true ; } - - function test8() returns (bool) { // forgefmt: disable-line - if ( true ) { // forgefmt: disable-line - uint256 a = 1; - } else { - uint256 b = 1; // forgefmt: disable-line - } - return true ; } -} - -// https://github.com/foundry-rs/foundry/issues/5825 -library MyLib { - bytes32 private constant TYPE_HASH = keccak256( - // forgefmt: disable-start - "MyStruct(" - "uint8 myEnum," - "address myAddress" - ")" - // forgefmt: disable-end - ); - - bytes32 private constant TYPE_HASH_1 = keccak256( - "MyStruct(" "uint8 myEnum," "address myAddress" ")" // forgefmt: disable-line - ); - - // forgefmt: disable-start - bytes32 private constant TYPE_HASH_2 = keccak256( - "MyStruct(" - "uint8 myEnum," - "address myAddress" - ")" - ); - // forgefmt: disable-end -} - -contract IfElseTest { - function setNumber(uint256 newNumber) public { - number = newNumber; - if (newNumber = 1) { - number = 1; - } else if (newNumber = 2) { - // number = 2; - } - else { - newNumber = 3; - } - } -} - -contract DbgFmtTest is Test { - function test_argsList() public { - uint256 result1 = internalNoArgs({}); - result2 = add({a: 1, b: 2}); - } - - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - function internalNoArgs() internal pure returns (uint256) { - return 0; - } -} diff --git a/crates/fmt-2/testdata/ReturnStatement/fmt.sol b/crates/fmt-2/testdata/ReturnStatement/fmt.sol deleted file mode 100644 index d628d6097233d..0000000000000 --- a/crates/fmt-2/testdata/ReturnStatement/fmt.sol +++ /dev/null @@ -1,66 +0,0 @@ -contract ReturnStatement { - function value() internal returns (uint256) { - return type(uint256).max; - } - - function returnEmpty() external { - if (true) { - return; - } - - if (false) { - // return empty 1 - return; /* return empty 2 */ // return empty 3 - } - - /* return empty 4 */ - return; // return empty 5 - } - - function returnSingleValue(uint256 val) external returns (uint256) { - if (val == 0) { - return // return single 1 - 0x00; - } - - if (val == 1) return 1; - - if (val == 2) { - return 3 - 1; - } - - if (val == 4) { - /* return single 2 */ - return 2 // return single 3 - ** 3; // return single 4 - } - - return value(); // return single 5 - } - - function returnMultipleValues(uint256 val) - external - returns (uint256, uint256, bool) - { - if (val == 0) { - return /* return mul 1 */ (0, 1, /* return mul 2 */ false); - } - - if (val == 1) { - // return mul 3 - return /* return mul 4 */ - (987654321, 1234567890, /* return mul 5 */ false); - } - - if (val == 2) { - return /* return mul 6 */ ( - 1234567890 + 987654321 + 87654123536, - 987654321 + 1234567890 + 124245235235, - true - ); - } - - return someFunction().getValue().modifyValue().negate() - .scaleBySomeFactor(1000).transformToTuple(); - } -} diff --git a/crates/fmt-2/testdata/ReturnStatement/original.sol b/crates/fmt-2/testdata/ReturnStatement/original.sol deleted file mode 100644 index 9cfaa82d6c524..0000000000000 --- a/crates/fmt-2/testdata/ReturnStatement/original.sol +++ /dev/null @@ -1,60 +0,0 @@ -contract ReturnStatement { - function value() internal returns (uint256) { - return type(uint256).max; - } - - function returnEmpty() external { - if (true) { - return ; - } - - if (false) { - // return empty 1 - return /* return empty 2 */ ; // return empty 3 - } - - /* return empty 4 */ return // return empty 5 - ; - } - - function returnSingleValue(uint256 val) external returns (uint256) { - if (val == 0) { - return // return single 1 - 0x00; - } - - if (val == 1) { return - 1; } - - if (val == 2) { - return 3 - - - 1; - } - - if (val == 4) { - /* return single 2 */ return 2** // return single 3 - 3 // return single 4 - ; - } - - return value() // return single 5 - ; - } - - function returnMultipleValues(uint256 val) external returns (uint256, uint256, bool) { - if (val == 0) { return /* return mul 1 */ (0, 1,/* return mul 2 */ false); } - - if (val == 1) { - // return mul 3 - return /* return mul 4 */ - ( - 987654321, 1234567890,/* return mul 5 */ false); } - - if (val == 2) { - return /* return mul 6 */ ( 1234567890 + 987654321 + 87654123536, 987654321 + 1234567890 + 124245235235, true); - } - - return someFunction().getValue().modifyValue().negate().scaleBySomeFactor(1000).transformToTuple(); - } -} diff --git a/crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol b/crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol deleted file mode 100644 index 9ad6b042b731a..0000000000000 --- a/crates/fmt-2/testdata/RevertNamedArgsStatement/fmt.sol +++ /dev/null @@ -1,35 +0,0 @@ -contract RevertNamedArgsStatement { - error EmptyError(); - error SimpleError(uint256 val); - error ComplexError(uint256 val, uint256 ts, string message); - error SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength( - uint256 val, uint256 ts, string message - ); - - function test() external { - revert({}); - - revert EmptyError({}); - - revert SimpleError({val: 0}); - - revert ComplexError({ - val: 0, - ts: block.timestamp, - message: "some reason" - }); - - revert - SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ - val: 0, - ts: 0x00, - message: "something unpredictable happened that caused execution to revert" - }); - - revert({}); // comment1 - - revert /* comment2 */ SimpleError({ /* comment3 */ // comment4 - val: 0 // comment 5 - }); - } -} diff --git a/crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol b/crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol deleted file mode 100644 index 2c9e35ba3d16c..0000000000000 --- a/crates/fmt-2/testdata/RevertNamedArgsStatement/original.sol +++ /dev/null @@ -1,32 +0,0 @@ -contract RevertNamedArgsStatement { - error EmptyError(); - error SimpleError(uint256 val); - error ComplexError(uint256 val, uint256 ts, string message); - error SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength( - uint256 val, uint256 ts, string message - ); - - function test() external { - revert ({ }); - - revert EmptyError({}); - - revert SimpleError({ val: 0 }); - - revert ComplexError( - { - val: 0, - ts: block.timestamp, - message: "some reason" - }); - - revert SomeVeryVeryVeryLongErrorNameWithNamedArgumentsThatExceedsMaximumLength({ val: 0, ts: 0x00, message: "something unpredictable happened that caused execution to revert"}); - - revert // comment1 - ({}); - - revert /* comment2 */ SimpleError /* comment3 */ ({ // comment4 - val:0 // comment 5 - }); - } -} diff --git a/crates/fmt-2/testdata/RevertStatement/fmt.sol b/crates/fmt-2/testdata/RevertStatement/fmt.sol deleted file mode 100644 index 3a677a5cadfdb..0000000000000 --- a/crates/fmt-2/testdata/RevertStatement/fmt.sol +++ /dev/null @@ -1,56 +0,0 @@ -contract RevertStatement { - error TestError(uint256, bool, string); - - function someVeryLongFunctionNameToGetDynamicErrorMessageString() - public - returns (string memory) - { - return ""; - } - - function test(string memory message) external { - revert(); - - revert( /* comment1 */ ); - - revert(); - - // comment2 - revert( - // comment3 - ); - - revert(message); - - revert( - // comment4 - message // comment5 /* comment6 */ - ); - - revert( /* comment7 */ /* comment8 */ message /* comment9 */ ); /* comment10 */ // comment11 - - revert( - string.concat( - message, - someVeryLongFunctionNameToGetDynamicErrorMessageString( - /* comment12 */ - ) - ) - ); - - revert TestError(0, false, message); - revert TestError( - 0, false, someVeryLongFunctionNameToGetDynamicErrorMessageString() - ); - - revert /* comment13 */ /* comment14 */ TestError( /* comment15 */ - 1234567890, false, message - ); - - revert TestError( /* comment16 */ - 1, - true, - someVeryLongFunctionNameToGetDynamicErrorMessageString() /* comment17 */ - ); - } -} diff --git a/crates/fmt-2/testdata/RevertStatement/original.sol b/crates/fmt-2/testdata/RevertStatement/original.sol deleted file mode 100644 index 3426fdb1e5ad5..0000000000000 --- a/crates/fmt-2/testdata/RevertStatement/original.sol +++ /dev/null @@ -1,44 +0,0 @@ -contract RevertStatement { - error TestError(uint256,bool,string); - - function someVeryLongFunctionNameToGetDynamicErrorMessageString() public returns (string memory) { - return ""; - } - - function test(string memory message) external { - revert ( ) ; - - revert ( /* comment1 */ ); - - revert - ( - - ) - ; - - // comment2 - revert ( - // comment3 - ); - - - revert ( message ); - - revert ( - // comment4 - message // comment5 /* comment6 */ - ); - - revert /* comment7 */ ( /* comment8 */ message /* comment9 */ ) /* comment10 */; // comment11 - - revert ( string.concat( message , someVeryLongFunctionNameToGetDynamicErrorMessageString( /* comment12 */)) ); - - revert TestError(0, false, message); - revert TestError(0, false, someVeryLongFunctionNameToGetDynamicErrorMessageString()); - - revert /* comment13 */ /* comment14 */ TestError /* comment15 */(1234567890, false, message); - - - revert TestError ( /* comment16 */ 1, true, someVeryLongFunctionNameToGetDynamicErrorMessageString() /* comment17 */); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/SimpleComments/fmt.sol b/crates/fmt-2/testdata/SimpleComments/fmt.sol deleted file mode 100644 index 6e8d5195bd143..0000000000000 --- a/crates/fmt-2/testdata/SimpleComments/fmt.sol +++ /dev/null @@ -1,80 +0,0 @@ -contract SimpleComments { - mapping(address /* asset */ => address /* router */) public router; - - constructor() { - // TODO: do this and that - - uint256 a = 1; - - // TODO: do that and this - // or maybe - // smth else - } - - function test() public view { - // do smth here - - // then here - - // cleanup - } - - function test2() public pure { - uint256 a = 1; - // comment 1 - // comment 2 - uint256 b = 2; - } - - function test3() public view { - uint256 a = 1; // comment - - // line comment - } - - function test4() public view returns (uint256) { - uint256 abc; // long postfix comment that exceeds line width. the comment should be split and carried over to the next line - uint256 abc2; // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline - - // long prefix comment that exceeds line width. the comment should be split and carried over to the next line - // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline - uint256 c; - - /* a really really long prefix block comment that exceeds line width */ - uint256 d; /* a really really long postfix block comment that exceeds line width */ - - uint256 value; - return /* a long block comment that exceeds line width */ value; - return /* a block comment that exceeds line width */ value; - return // a line comment that exceeds line width - value; - } -} - -/* - -██████╗ ██████╗ ██████╗ ████████╗███████╗███████╗████████╗ -██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ -██████╔╝██████╔╝██████╔╝ ██║ █████╗ ███████╗ ██║ -██╔═══╝ ██╔══██╗██╔══██╗ ██║ ██╔══╝ ╚════██║ ██║ -██║ ██║ ██║██████╔╝ ██║ ███████╗███████║ ██║ -╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ -*/ -function asciiArt() {} - -/* - * @notice Here is my comment - * - item 1 - * - item 2 - * Some equations: - * y = mx + b - */ -function test() {} -// comment after function - -// comment with extra newlines - -// some comment -// another comment - -// eof comment diff --git a/crates/fmt-2/testdata/SimpleComments/original.sol b/crates/fmt-2/testdata/SimpleComments/original.sol deleted file mode 100644 index d41c686b2f783..0000000000000 --- a/crates/fmt-2/testdata/SimpleComments/original.sol +++ /dev/null @@ -1,83 +0,0 @@ -contract SimpleComments { - mapping(address /* asset */ => address /* router */) public router; - - - constructor() { - // TODO: do this and that - - uint256 a = 1; - - // TODO: do that and this - // or maybe - // smth else - } - - function test() public view { - // do smth here - - // then here - - // cleanup - } - - function test2() public pure { - uint a = 1; - // comment 1 - // comment 2 - uint b = 2; - } - - function test3() public view { - uint256 a = 1; // comment - - // line comment - } - - function test4() public view returns (uint256) { - uint256 abc; // long postfix comment that exceeds line width. the comment should be split and carried over to the next line - uint256 abc2; // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline - - // long prefix comment that exceeds line width. the comment should be split and carried over to the next line - // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline - uint256 c; - - /* a really really long prefix block comment that exceeds line width */ - uint256 d; /* a really really long postfix block comment that exceeds line width */ - - uint256 value; - return /* a long block comment that exceeds line width */ value; - return /* a block comment that exceeds line width */ value; - return // a line comment that exceeds line width - value; - } -} - -/* - -██████╗ ██████╗ ██████╗ ████████╗███████╗███████╗████████╗ -██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ -██████╔╝██████╔╝██████╔╝ ██║ █████╗ ███████╗ ██║ -██╔═══╝ ██╔══██╗██╔══██╗ ██║ ██╔══╝ ╚════██║ ██║ -██║ ██║ ██║██████╔╝ ██║ ███████╗███████║ ██║ -╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ -*/ -function asciiArt() {} - -/* - * @notice Here is my comment - * - item 1 - * - item 2 - * Some equations: - * y = mx + b - */ -function test() {} -// comment after function - - -// comment with extra newlines - - -// some comment -// another comment - -// eof comment \ No newline at end of file diff --git a/crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol b/crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol deleted file mode 100644 index 06ddc1cc19669..0000000000000 --- a/crates/fmt-2/testdata/SimpleComments/wrap-comments.fmt.sol +++ /dev/null @@ -1,92 +0,0 @@ -// config: line_length = 60 -// config: wrap_comments = true -contract SimpleComments { - mapping(address /* asset */ => address /* router */) - public router; - - constructor() { - // TODO: do this and that - - uint256 a = 1; - - // TODO: do that and this - // or maybe - // smth else - } - - function test() public view { - // do smth here - - // then here - - // cleanup - } - - function test2() public pure { - uint256 a = 1; - // comment 1 - // comment 2 - uint256 b = 2; - } - - function test3() public view { - uint256 a = 1; // comment - - // line comment - } - - function test4() public view returns (uint256) { - uint256 abc; // long postfix comment that exceeds - // line width. the comment should be split and - // carried over to the next line - uint256 abc2; // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline - - // long prefix comment that exceeds line width. the - // comment should be split and carried over to the - // next line - // reallylongsinglewordcommentthatexceedslinewidththecommentshouldbesplitandcarriedovertothenextline - uint256 c; - - /* a really really long prefix block comment that - exceeds line width */ - uint256 d; /* a really really long postfix block - comment that exceeds line width */ - - uint256 value; - return /* a long block comment that exceeds line - width */ - value; - return /* a block comment that exceeds line width */ - value; - return // a line comment that exceeds line width - value; - } -} - -/* - -██████╗ ██████╗ ██████╗ ████████╗███████╗███████╗████████╗ -██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝ -██████╔╝██████╔╝██████╔╝ ██║ █████╗ ███████╗ ██║ -██╔═══╝ ██╔══██╗██╔══██╗ ██║ ██╔══╝ ╚════██║ ██║ -██║ ██║ ██║██████╔╝ ██║ ███████╗███████║ ██║ -╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚══════╝╚══════╝ ╚═╝ -*/ -function asciiArt() {} - -/* - * @notice Here is my comment - * - item 1 - * - item 2 - * Some equations: - * y = mx + b - */ -function test() {} -// comment after function - -// comment with extra newlines - -// some comment -// another comment - -// eof comment diff --git a/crates/fmt-2/testdata/SortedImports/fmt.sol b/crates/fmt-2/testdata/SortedImports/fmt.sol deleted file mode 100644 index f9b2c0ee2a9c3..0000000000000 --- a/crates/fmt-2/testdata/SortedImports/fmt.sol +++ /dev/null @@ -1,34 +0,0 @@ -// config: sort_imports = true -import "SomeFile0.sol" as SomeOtherFile; -import "SomeFile1.sol" as SomeOtherFile; -import "SomeFile2.sol"; -import "SomeFile3.sol"; - -import "AnotherFile1.sol" as SomeSymbol; -import "AnotherFile2.sol" as SomeSymbol; - -import { - symbol1 as alias3, - symbol2 as alias2, - symbol3 as alias1, - symbol4 -} from "File0.sol"; -import {symbol1 as alias, symbol2} from "File2.sol"; -import {symbol1 as alias, symbol2} from "File3.sol"; -import { - symbol1 as alias1, - symbol2 as alias2, - symbol3 as alias3, - symbol4 -} from "File6.sol"; - -uint256 constant someConstant = 10; - -import {Something2, Something3} from "someFile.sol"; - -// This is a comment -import {Something2, Something3} from "someFile.sol"; - -import {symbol1 as alias, symbol2} from "File3.sol"; -// comment inside group is treated as a separator for now -import {symbol1 as alias, symbol2} from "File2.sol"; diff --git a/crates/fmt-2/testdata/SortedImports/original.sol b/crates/fmt-2/testdata/SortedImports/original.sol deleted file mode 100644 index 54b3ca3b59cfb..0000000000000 --- a/crates/fmt-2/testdata/SortedImports/original.sol +++ /dev/null @@ -1,23 +0,0 @@ -import "SomeFile3.sol"; -import "SomeFile2.sol"; -import "SomeFile1.sol" as SomeOtherFile; -import "SomeFile0.sol" as SomeOtherFile; - -import "AnotherFile2.sol" as SomeSymbol; -import "AnotherFile1.sol" as SomeSymbol; - -import {symbol2, symbol1 as alias} from "File3.sol"; -import {symbol2, symbol1 as alias} from "File2.sol"; -import {symbol2 as alias2, symbol1 as alias1, symbol3 as alias3, symbol4} from "File6.sol"; -import {symbol3 as alias1, symbol2 as alias2, symbol1 as alias3, symbol4} from "File0.sol"; - -uint256 constant someConstant = 10; - -import {Something3, Something2} from "someFile.sol"; - -// This is a comment -import {Something3, Something2} from "someFile.sol"; - -import {symbol2, symbol1 as alias} from "File3.sol"; -// comment inside group is treated as a separator for now -import {symbol2, symbol1 as alias} from "File2.sol"; \ No newline at end of file diff --git a/crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol deleted file mode 100644 index 3e9496dd2ed90..0000000000000 --- a/crates/fmt-2/testdata/StatementBlock/bracket-spacing.fmt.sol +++ /dev/null @@ -1,20 +0,0 @@ -// config: bracket_spacing = true -contract Contract { - function test() { - unchecked { - a += 1; - } - - unchecked { - a += 1; - } - 2 + 2; - - unchecked { - a += 1; - } - unchecked { } - - 1 + 1; - } -} diff --git a/crates/fmt-2/testdata/StatementBlock/fmt.sol b/crates/fmt-2/testdata/StatementBlock/fmt.sol deleted file mode 100644 index 65aeb3a84db33..0000000000000 --- a/crates/fmt-2/testdata/StatementBlock/fmt.sol +++ /dev/null @@ -1,19 +0,0 @@ -contract Contract { - function test() { - unchecked { - a += 1; - } - - unchecked { - a += 1; - } - 2 + 2; - - unchecked { - a += 1; - } - unchecked {} - - 1 + 1; - } -} diff --git a/crates/fmt-2/testdata/StatementBlock/original.sol b/crates/fmt-2/testdata/StatementBlock/original.sol deleted file mode 100644 index 489b01d9846b4..0000000000000 --- a/crates/fmt-2/testdata/StatementBlock/original.sol +++ /dev/null @@ -1,17 +0,0 @@ -contract Contract { - function test() { unchecked { a += 1; } - - unchecked { - a += 1; - } - 2 + 2; - -unchecked { a += 1; - } - unchecked {} - - 1 + 1; - - - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol deleted file mode 100644 index 3e1c8ea4e3ff1..0000000000000 --- a/crates/fmt-2/testdata/StructDefinition/bracket-spacing.fmt.sol +++ /dev/null @@ -1,15 +0,0 @@ -// config: bracket_spacing = true -struct Foo { } - -struct Bar { - uint256 foo; - string bar; -} - -struct MyStruct { - // first 1 - // first 2 - uint256 field1; - // second - uint256 field2; -} diff --git a/crates/fmt-2/testdata/StructDefinition/fmt.sol b/crates/fmt-2/testdata/StructDefinition/fmt.sol deleted file mode 100644 index 78c5079cd37e9..0000000000000 --- a/crates/fmt-2/testdata/StructDefinition/fmt.sol +++ /dev/null @@ -1,14 +0,0 @@ -struct Foo {} - -struct Bar { - uint256 foo; - string bar; -} - -struct MyStruct { - // first 1 - // first 2 - uint256 field1; - // second - uint256 field2; -} diff --git a/crates/fmt-2/testdata/StructDefinition/original.sol b/crates/fmt-2/testdata/StructDefinition/original.sol deleted file mode 100644 index a82d7a92e7d7a..0000000000000 --- a/crates/fmt-2/testdata/StructDefinition/original.sol +++ /dev/null @@ -1,10 +0,0 @@ -struct Foo { -} struct Bar { uint foo ;string bar ; } - -struct MyStruct { -// first 1 -// first 2 - uint256 field1; - // second - uint256 field2; -} diff --git a/crates/fmt-2/testdata/ThisExpression/fmt.sol b/crates/fmt-2/testdata/ThisExpression/fmt.sol deleted file mode 100644 index 239a6073eae39..0000000000000 --- a/crates/fmt-2/testdata/ThisExpression/fmt.sol +++ /dev/null @@ -1,20 +0,0 @@ -contract ThisExpression { - function someFunc() public {} - function someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword() - public - {} - - function test() external { - this.someFunc(); - this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - this // comment1 - .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - address(this).balance; - - address thisAddress = address( - // comment2 - /* comment3 */ - this // comment 4 - ); - } -} diff --git a/crates/fmt-2/testdata/ThisExpression/original.sol b/crates/fmt-2/testdata/ThisExpression/original.sol deleted file mode 100644 index 2fb547c59b300..0000000000000 --- a/crates/fmt-2/testdata/ThisExpression/original.sol +++ /dev/null @@ -1,17 +0,0 @@ -contract ThisExpression { - function someFunc() public {} - function someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword() public {} - - function test() external { - this.someFunc(); - this.someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - this // comment1 - .someVeryVeryVeryLongVariableNameThatWillBeAccessedByThisKeyword(); - address(this).balance; - - address thisAddress = address( - // comment2 - /* comment3 */ this // comment 4 - ); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/TrailingComma/fmt.sol b/crates/fmt-2/testdata/TrailingComma/fmt.sol deleted file mode 100644 index 034ac5d33088d..0000000000000 --- a/crates/fmt-2/testdata/TrailingComma/fmt.sol +++ /dev/null @@ -1,12 +0,0 @@ -contract C is Contract { - modifier m(uint256) {} - // invalid solidity code, but valid pt - modifier m2(uint256) returns (uint256) {} - - function f(uint256 a) external {} - function f2(uint256 a, bytes32 b) external returns (uint256) {} - - function f3() external { - try some.invoke() returns (uint256, uint256) {} catch {} - } -} diff --git a/crates/fmt-2/testdata/TrailingComma/original.sol b/crates/fmt-2/testdata/TrailingComma/original.sol deleted file mode 100644 index c06460f250aa8..0000000000000 --- a/crates/fmt-2/testdata/TrailingComma/original.sol +++ /dev/null @@ -1,12 +0,0 @@ -contract C is Contract { - modifier m(uint256, ,,, ) {} - // invalid solidity code, but valid pt - modifier m2(uint256) returns (uint256,,,) {} - - function f(uint256 a, ) external {} - function f2(uint256 a, , , ,bytes32 b) external returns (uint256,,,,) {} - - function f3() external { - try some.invoke() returns (uint256,,,uint256) {} catch {} - } -} diff --git a/crates/fmt-2/testdata/TryStatement/fmt.sol b/crates/fmt-2/testdata/TryStatement/fmt.sol deleted file mode 100644 index d49687eb1285a..0000000000000 --- a/crates/fmt-2/testdata/TryStatement/fmt.sol +++ /dev/null @@ -1,74 +0,0 @@ -interface Unknown { - function empty() external; - function lookup() external returns (uint256); - function lookupMultipleValues() - external - returns (uint256, uint256, uint256, uint256, uint256); - - function doSomething() external; - function doSomethingElse() external; - - function handleError() external; -} - -contract TryStatement { - Unknown unknown; - - function test() external { - try unknown.empty() {} catch {} - - try unknown.lookup() returns (uint256) {} catch Error(string memory) {} - - try unknown.lookup() returns (uint256) {} - catch Error(string memory) {} - catch (bytes memory) {} - - try unknown.lookup() returns (uint256) {} catch (bytes memory) {} - - try unknown.empty() { - unknown.doSomething(); - } catch { - unknown.handleError(); - } - - try unknown.empty() { - unknown.doSomething(); - } - catch Error(string memory) {} - catch Panic(uint256) {} - catch { - unknown.handleError(); - } - - try unknown.lookupMultipleValues() returns ( - uint256, uint256, uint256, uint256, uint256 - ) {} - catch Error(string memory) {} - catch {} - - try unknown.lookupMultipleValues() returns ( - uint256, uint256, uint256, uint256, uint256 - ) { - unknown.doSomething(); - } catch Error(string memory) { - unknown.handleError(); - } catch {} - - // comment1 - try /* comment2 */ unknown.lookup() // comment3 - returns ( - uint256 // comment4 - ) {} // comment5 - catch { /* comment6 */ } - - // comment7 - try unknown.empty() { - // comment8 - unknown.doSomething(); - } /* comment9 */ catch /* comment10 */ Error(string memory) { - unknown.handleError(); - } catch /* comment11 */ Panic(uint256) { - unknown.handleError(); - } catch {} - } -} diff --git a/crates/fmt-2/testdata/TryStatement/original.sol b/crates/fmt-2/testdata/TryStatement/original.sol deleted file mode 100644 index 9fc158b20195a..0000000000000 --- a/crates/fmt-2/testdata/TryStatement/original.sol +++ /dev/null @@ -1,66 +0,0 @@ -interface Unknown { - function empty() external; - function lookup() external returns(uint256); - function lookupMultipleValues() external returns (uint256, uint256, uint256, uint256, uint256); - - function doSomething() external; - function doSomethingElse() external; - - function handleError() external; -} - -contract TryStatement { - Unknown unknown; - - function test() external { - try unknown.empty() {} catch {} - - try unknown.lookup() returns (uint256) {} catch Error(string memory) {} - - try unknown.lookup() returns (uint256) {} catch Error(string memory) {} catch (bytes memory) {} - - try unknown - .lookup() returns (uint256 - ) { - } catch ( bytes memory ){} - - try unknown.empty() { - unknown.doSomething(); - } catch { - unknown.handleError(); - } - - try unknown.empty() { - unknown.doSomething(); - } catch Error(string memory) {} - catch Panic(uint) {} - catch { - unknown.handleError(); - } - - try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) {} catch Error(string memory) {} catch {} - - try unknown.lookupMultipleValues() returns (uint256, uint256, uint256, uint256, uint256) { - unknown.doSomething(); - } - catch Error(string memory) { - unknown.handleError(); - } - catch {} - - // comment1 - try /* comment2 */ unknown.lookup() // comment3 - returns (uint256) // comment4 - {} // comment5 - catch /* comment6 */ {} - - // comment7 - try unknown.empty() { // comment8 - unknown.doSomething(); - } /* comment9 */ catch /* comment10 */ Error(string memory) { - unknown.handleError(); - } catch Panic /* comment11 */ (uint) { - unknown.handleError(); - } catch {} - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/TypeDefinition/fmt.sol b/crates/fmt-2/testdata/TypeDefinition/fmt.sol deleted file mode 100644 index 63b0083cf47f8..0000000000000 --- a/crates/fmt-2/testdata/TypeDefinition/fmt.sol +++ /dev/null @@ -1,12 +0,0 @@ -pragma solidity ^0.8.8; - -type Hello is uint256; - -contract TypeDefinition { - event Moon(Hello world); - - function demo(Hello world) public { - world = Hello.wrap(Hello.unwrap(world) + 1337); - emit Moon(world); - } -} diff --git a/crates/fmt-2/testdata/TypeDefinition/original.sol b/crates/fmt-2/testdata/TypeDefinition/original.sol deleted file mode 100644 index f4aeac50f8692..0000000000000 --- a/crates/fmt-2/testdata/TypeDefinition/original.sol +++ /dev/null @@ -1,12 +0,0 @@ -pragma solidity ^0.8.8; - - type Hello is uint; - -contract TypeDefinition { - event Moon(Hello world); - - function demo(Hello world) public { - world = Hello.wrap(Hello.unwrap(world) + 1337); - emit Moon(world); - } -} diff --git a/crates/fmt-2/testdata/UnitExpression/fmt.sol b/crates/fmt-2/testdata/UnitExpression/fmt.sol deleted file mode 100644 index ceb16c86c7813..0000000000000 --- a/crates/fmt-2/testdata/UnitExpression/fmt.sol +++ /dev/null @@ -1,24 +0,0 @@ -contract UnitExpression { - function test() external { - uint256 timestamp; - timestamp = 1 seconds; - timestamp = 1 minutes; - timestamp = 1 hours; - timestamp = 1 days; - timestamp = 1 weeks; - - uint256 value; - value = 1 wei; - value = 1 gwei; - value = 1 ether; - - uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; - - value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue - * 1 /* comment1 */ ether; // comment2 - - value = 1 // comment3 - // comment4 - ether; // comment5 - } -} diff --git a/crates/fmt-2/testdata/UnitExpression/original.sol b/crates/fmt-2/testdata/UnitExpression/original.sol deleted file mode 100644 index f85af34fe7770..0000000000000 --- a/crates/fmt-2/testdata/UnitExpression/original.sol +++ /dev/null @@ -1,23 +0,0 @@ -contract UnitExpression { - function test() external { - uint256 timestamp; - timestamp = 1 seconds; - timestamp = 1 minutes; - timestamp = 1 hours; - timestamp = 1 days; - timestamp = 1 weeks; - - uint256 value; - value = 1 wei; - value = 1 gwei; - value = 1 ether; - - uint256 someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue; - - value = someVeryVeryVeryLongVariableNameForTheMultiplierForEtherValue * 1 /* comment1 */ ether; // comment2 - - value = 1 // comment3 - // comment4 - ether; // comment5 - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/UsingDirective/fmt.sol b/crates/fmt-2/testdata/UsingDirective/fmt.sol deleted file mode 100644 index 1cfff34558f19..0000000000000 --- a/crates/fmt-2/testdata/UsingDirective/fmt.sol +++ /dev/null @@ -1,36 +0,0 @@ -contract UsingExampleContract { - using UsingExampleLibrary for *; - using UsingExampleLibrary for uint256; - using Example.UsingExampleLibrary for uint256; - using {M.g, M.f} for uint256; - using UsingExampleLibrary for uint256 global; - using { - These, - Are, - MultipleLibraries, - ThatNeedToBePut, - OnSeparateLines - } for uint256; - using { - This - .isareally - .longmember - .access - .expression - .that - .needs - .to - .besplit - .into - .lines - } for uint256; - using {and as &, or as |, xor as ^, cpl as ~} for Bitmap global; - using { - eq as ==, - ne as !=, - lt as <, - lte as <=, - gt as >, - gte as >= - } for Bitmap global; -} diff --git a/crates/fmt-2/testdata/UsingDirective/original.sol b/crates/fmt-2/testdata/UsingDirective/original.sol deleted file mode 100644 index 39ad6d8711c8e..0000000000000 --- a/crates/fmt-2/testdata/UsingDirective/original.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract UsingExampleContract { - using UsingExampleLibrary for * ; - using UsingExampleLibrary for uint; - using Example.UsingExampleLibrary for uint; - using { M.g, M.f} for uint; -using UsingExampleLibrary for uint global; -using { These, Are, MultipleLibraries, ThatNeedToBePut, OnSeparateLines } for uint; -using { This.isareally.longmember.access.expression.that.needs.to.besplit.into.lines } for uint; -using {and as &, or as |, xor as ^, cpl as ~} for Bitmap global; -using {eq as ==, ne as !=, lt as <, lte as <=, gt as >, gte as >=} for Bitmap global; -} diff --git a/crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol b/crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol deleted file mode 100644 index 8896668d1e43c..0000000000000 --- a/crates/fmt-2/testdata/VariableAssignment/bracket-spacing.fmt.sol +++ /dev/null @@ -1,27 +0,0 @@ -// config: bracket_spacing = true -contract TestContract { - function aLongerTestFunctionName(uint256 input) - public - view - returns (uint256 num) - { - (, uint256 second) = (1, 2); - (uint256 listItem001) = 1; - (uint256 listItem002, uint256 listItem003) = (10, 20); - (uint256 listItem004, uint256 listItem005, uint256 listItem006) = - (10, 20, 30); - ( - uint256 listItem007, - uint256 listItem008, - uint256 listItem009, - uint256 listItem010 - ) = (10, 20, 30, 40); - return 1; - } - - function test() external { - uint256 value = map[key]; - uint256 allowed = allowance[from][msg.sender]; - allowance[from][msg.sender] = allowed; - } -} diff --git a/crates/fmt-2/testdata/VariableAssignment/fmt.sol b/crates/fmt-2/testdata/VariableAssignment/fmt.sol deleted file mode 100644 index 07480d873c21e..0000000000000 --- a/crates/fmt-2/testdata/VariableAssignment/fmt.sol +++ /dev/null @@ -1,26 +0,0 @@ -contract TestContract { - function aLongerTestFunctionName(uint256 input) - public - view - returns (uint256 num) - { - (, uint256 second) = (1, 2); - (uint256 listItem001) = 1; - (uint256 listItem002, uint256 listItem003) = (10, 20); - (uint256 listItem004, uint256 listItem005, uint256 listItem006) = - (10, 20, 30); - ( - uint256 listItem007, - uint256 listItem008, - uint256 listItem009, - uint256 listItem010 - ) = (10, 20, 30, 40); - return 1; - } - - function test() external { - uint256 value = map[key]; - uint256 allowed = allowance[from][msg.sender]; - allowance[from][msg.sender] = allowed; - } -} diff --git a/crates/fmt-2/testdata/VariableAssignment/original.sol b/crates/fmt-2/testdata/VariableAssignment/original.sol deleted file mode 100644 index 07480d873c21e..0000000000000 --- a/crates/fmt-2/testdata/VariableAssignment/original.sol +++ /dev/null @@ -1,26 +0,0 @@ -contract TestContract { - function aLongerTestFunctionName(uint256 input) - public - view - returns (uint256 num) - { - (, uint256 second) = (1, 2); - (uint256 listItem001) = 1; - (uint256 listItem002, uint256 listItem003) = (10, 20); - (uint256 listItem004, uint256 listItem005, uint256 listItem006) = - (10, 20, 30); - ( - uint256 listItem007, - uint256 listItem008, - uint256 listItem009, - uint256 listItem010 - ) = (10, 20, 30, 40); - return 1; - } - - function test() external { - uint256 value = map[key]; - uint256 allowed = allowance[from][msg.sender]; - allowance[from][msg.sender] = allowed; - } -} diff --git a/crates/fmt-2/testdata/VariableDefinition/fmt.sol b/crates/fmt-2/testdata/VariableDefinition/fmt.sol deleted file mode 100644 index 9ff53c8d55237..0000000000000 --- a/crates/fmt-2/testdata/VariableDefinition/fmt.sol +++ /dev/null @@ -1,65 +0,0 @@ -// config: line_length = 40 -contract Contract { - bytes32 private constant BYTES; - bytes32 - private - constant - override(Base1) BYTES; - bytes32 - private - constant - override(Base1, Base2) BYTES; - bytes32 - private - constant - immutable - override BYTES; - bytes32 - private - constant - immutable - override - BYTES_VERY_VERY_VERY_LONG; - bytes32 - private - constant - override( - Base1, - Base2, - SomeLongBaseContract, - AndAnotherVeryLongBaseContract, - Imported.Contract - ) BYTES_OVERRIDDEN; - - bytes32 private constant BYTES = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 - private - constant - immutable - override BYTES = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 - private - constant - immutable - override - BYTES_VERY_VERY_VERY_LONG = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 private constant - BYTES_VERY_VERY_LONG = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - - uint256 constant POWER_EXPRESSION = - 10 ** 27; - uint256 constant ADDED_EXPRESSION = - 1 + 2; - - // comment 1 - uint256 constant example1 = 1; - // comment 2 - // comment 3 - uint256 constant example2 = 2; // comment 4 - uint256 constant example3 = /* comment 5 */ - 3; // comment 6 -} diff --git a/crates/fmt-2/testdata/VariableDefinition/original.sol b/crates/fmt-2/testdata/VariableDefinition/original.sol deleted file mode 100644 index bd15a6384601b..0000000000000 --- a/crates/fmt-2/testdata/VariableDefinition/original.sol +++ /dev/null @@ -1,27 +0,0 @@ -contract Contract { - bytes32 constant private BYTES; - bytes32 private constant override (Base1) BYTES; - bytes32 private constant override (Base1, Base2) BYTES; - bytes32 private constant override immutable BYTES; - bytes32 private constant override immutable BYTES_VERY_VERY_VERY_LONG; - bytes32 private constant override(Base1, Base2, SomeLongBaseContract, AndAnotherVeryLongBaseContract, Imported.Contract) BYTES_OVERRIDDEN; - - bytes32 constant private BYTES = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 private constant override immutable BYTES = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 private constant override immutable BYTES_VERY_VERY_VERY_LONG = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 private constant BYTES_VERY_VERY_LONG = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - - uint constant POWER_EXPRESSION = 10 ** 27; - uint constant ADDED_EXPRESSION = 1 + 2; - - // comment 1 - uint256 constant example1 = 1; - // comment 2 - // comment 3 - uint256 constant example2 = 2;// comment 4 - uint256 constant example3 /* comment 5 */= 3; // comment 6 -} diff --git a/crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol b/crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol deleted file mode 100644 index 5fde30038126e..0000000000000 --- a/crates/fmt-2/testdata/VariableDefinition/override-spacing.fmt.sol +++ /dev/null @@ -1,66 +0,0 @@ -// config: line_length = 40 -// config: override_spacing = true -contract Contract { - bytes32 private constant BYTES; - bytes32 - private - constant - override (Base1) BYTES; - bytes32 - private - constant - override (Base1, Base2) BYTES; - bytes32 - private - constant - immutable - override BYTES; - bytes32 - private - constant - immutable - override - BYTES_VERY_VERY_VERY_LONG; - bytes32 - private - constant - override ( - Base1, - Base2, - SomeLongBaseContract, - AndAnotherVeryLongBaseContract, - Imported.Contract - ) BYTES_OVERRIDDEN; - - bytes32 private constant BYTES = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 - private - constant - immutable - override BYTES = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 - private - constant - immutable - override - BYTES_VERY_VERY_VERY_LONG = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 private constant - BYTES_VERY_VERY_LONG = - 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - - uint256 constant POWER_EXPRESSION = - 10 ** 27; - uint256 constant ADDED_EXPRESSION = - 1 + 2; - - // comment 1 - uint256 constant example1 = 1; - // comment 2 - // comment 3 - uint256 constant example2 = 2; // comment 4 - uint256 constant example3 = /* comment 5 */ - 3; // comment 6 -} diff --git a/crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol b/crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol deleted file mode 100644 index cff7ac40b3d29..0000000000000 --- a/crates/fmt-2/testdata/WhileStatement/block-multi.fmt.sol +++ /dev/null @@ -1,80 +0,0 @@ -// config: single_line_statement_blocks = "multi" -pragma solidity ^0.8.8; - -function doIt() {} - -contract WhileStatement { - function test() external { - uint256 i1; - while (i1 < 10) { - i1++; - } - - while (i1 < 10) { - i1++; - } - - while (i1 < 10) { - while (i1 < 10) { - i1++; - } - } - - uint256 i2; - while (i2 < 10) { - i2++; - } - - uint256 i3; - while (i3 < 10) { - i3++; - } - - uint256 i4; - while (i4 < 10) { - i4++; - } - - uint256 someLongVariableName; - while ( - someLongVariableName < 10 && someLongVariableName < 11 - && someLongVariableName < 12 - ) { - someLongVariableName++; - } - someLongVariableName++; - - bool condition; - while (condition) { - doIt(); - } - - while (condition) { - doIt(); - } - - while (condition) { - doIt(); - } - - while ( - // comment1 - condition - ) { - doIt(); - } - - while ( - condition // comment2 - ) { - doIt(); - } - - while ( - someLongVariableName < 10 && someLongVariableName < 11 - && someLongVariableName < 12 - ) { - doIt(); - } - } -} diff --git a/crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol b/crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol deleted file mode 100644 index ee5c48b7d911f..0000000000000 --- a/crates/fmt-2/testdata/WhileStatement/block-single.fmt.sol +++ /dev/null @@ -1,52 +0,0 @@ -// config: single_line_statement_blocks = "single" -pragma solidity ^0.8.8; - -function doIt() {} - -contract WhileStatement { - function test() external { - uint256 i1; - while (i1 < 10) i1++; - - while (i1 < 10) i1++; - - while (i1 < 10) while (i1 < 10) i1++; - - uint256 i2; - while (i2 < 10) i2++; - - uint256 i3; - while (i3 < 10) i3++; - - uint256 i4; - while (i4 < 10) i4++; - - uint256 someLongVariableName; - while ( - someLongVariableName < 10 && someLongVariableName < 11 - && someLongVariableName < 12 - ) someLongVariableName++; - someLongVariableName++; - - bool condition; - while (condition) doIt(); - - while (condition) doIt(); - - while (condition) doIt(); - - while ( - // comment1 - condition - ) doIt(); - - while ( - condition // comment2 - ) doIt(); - - while ( - someLongVariableName < 10 && someLongVariableName < 11 - && someLongVariableName < 12 - ) doIt(); - } -} diff --git a/crates/fmt-2/testdata/WhileStatement/fmt.sol b/crates/fmt-2/testdata/WhileStatement/fmt.sol deleted file mode 100644 index 131c4eaedb799..0000000000000 --- a/crates/fmt-2/testdata/WhileStatement/fmt.sol +++ /dev/null @@ -1,59 +0,0 @@ -pragma solidity ^0.8.8; - -function doIt() {} - -contract WhileStatement { - function test() external { - uint256 i1; - while (i1 < 10) { - i1++; - } - - while (i1 < 10) i1++; - - while (i1 < 10) { - while (i1 < 10) { - i1++; - } - } - - uint256 i2; - while (i2 < 10) i2++; - - uint256 i3; - while (i3 < 10) i3++; - - uint256 i4; - while (i4 < 10) { - i4++; - } - - uint256 someLongVariableName; - while ( - someLongVariableName < 10 && someLongVariableName < 11 - && someLongVariableName < 12 - ) someLongVariableName++; - someLongVariableName++; - - bool condition; - while (condition) doIt(); - - while (condition) doIt(); - - while (condition) doIt(); - - while ( - // comment1 - condition - ) doIt(); - - while ( - condition // comment2 - ) doIt(); - - while ( - someLongVariableName < 10 && someLongVariableName < 11 - && someLongVariableName < 12 - ) doIt(); - } -} diff --git a/crates/fmt-2/testdata/WhileStatement/original.sol b/crates/fmt-2/testdata/WhileStatement/original.sol deleted file mode 100644 index 8b245b0cfa0f8..0000000000000 --- a/crates/fmt-2/testdata/WhileStatement/original.sol +++ /dev/null @@ -1,51 +0,0 @@ -pragma solidity ^0.8.8; - -function doIt() {} - -contract WhileStatement { - function test() external { - uint256 i1; - while ( i1 < 10 ) { - i1++; - } - - while (i1<10) i1++; - - while (i1<10) - while (i1<10) - i1++; - - uint256 i2; - while ( i2 < 10) { i2++; } - - uint256 i3; while ( - i3 < 10 - ) { i3++; } - - uint256 i4; while (i4 < 10) - - { i4 ++ ;} - - uint256 someLongVariableName; - while ( - someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12 - ) { someLongVariableName ++; } someLongVariableName++; - - bool condition; - while(condition) doIt(); - - while(condition) { doIt(); } - - while - (condition) doIt(); - - while // comment1 - (condition) doIt(); - - while ( - condition // comment2 - ) doIt(); - - while ( someLongVariableName < 10 && someLongVariableName < 11 && someLongVariableName < 12) doIt(); - } -} \ No newline at end of file diff --git a/crates/fmt-2/testdata/Yul/fmt.sol b/crates/fmt-2/testdata/Yul/fmt.sol deleted file mode 100644 index 2f37eb2f290fe..0000000000000 --- a/crates/fmt-2/testdata/Yul/fmt.sol +++ /dev/null @@ -1,188 +0,0 @@ -contract Yul { - function test() external { - // https://github.com/euler-xyz/euler-contracts/blob/d4f207a4ac5a6e8ab7447a0f09d1399150c41ef4/contracts/vendor/MerkleProof.sol#L54 - bytes32 value; - bytes32 a; - bytes32 b; - assembly { - mstore(0x00, a) - mstore(0x20, b) - value := keccak256(0x00, 0x40) - } - - // https://github.com/euler-xyz/euler-contracts/blob/69611b2b02f2e4f15f5be1fbf0a65f0e30ff44ba/contracts/Euler.sol#L49 - address moduleImpl; - assembly { - let payloadSize := sub(calldatasize(), 4) - calldatacopy(0, 4, payloadSize) - mstore(payloadSize, shl(96, caller())) - - let result := - delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) - - returndatacopy(0, 0, returndatasize()) - - switch result - case 0 { revert(0, returndatasize()) } - default { return(0, returndatasize()) } - } - - // https://github.com/libevm/subway/blob/8ea4e86c65ad76801c72c681138b0a150f7e2dbd/contracts/src/Sandwich.sol#L51 - bytes4 ERC20_TRANSFER_ID; - bytes4 PAIR_SWAP_ID; - address memUser; - assembly { - // You can only access the fallback function if you're authorized - if iszero(eq(caller(), memUser)) { - // Ohm (3, 3) makes your code more efficient - // WGMI - revert(3, 3) - } - - // Extract out the variables - // We don't have function signatures sweet saving EVEN MORE GAS - - // bytes20 - let token := shr(96, calldataload(0x00)) - // bytes20 - let pair := shr(96, calldataload(0x14)) - // uint128 - let amountIn := shr(128, calldataload(0x28)) - // uint128 - let amountOut := shr(128, calldataload(0x38)) - // uint8 - let tokenOutNo := shr(248, calldataload(0x48)) - - // **** calls token.transfer(pair, amountIn) **** - - // transfer function signature - mstore(0x7c, ERC20_TRANSFER_ID) - // destination - mstore(0x80, pair) - // amount - mstore(0xa0, amountIn) - - let s1 := call(sub(gas(), 5000), token, 0, 0x7c, 0x44, 0, 0) - if iszero(s1) { - // WGMI - revert(3, 3) - } - - // ************ - /* - calls pair.swap( - tokenOutNo == 0 ? amountOut : 0, - tokenOutNo == 1 ? amountOut : 0, - address(this), - new bytes(0) - ) - */ - - // swap function signature - mstore(0x7c, PAIR_SWAP_ID) - // tokenOutNo == 0 ? .... - switch tokenOutNo - case 0 { - mstore(0x80, amountOut) - mstore(0xa0, 0) - } - case 1 { - mstore(0x80, 0) - mstore(0xa0, amountOut) - } - // address(this) - mstore(0xc0, address()) - // empty bytes - mstore(0xe0, 0x80) - - let s2 := call(sub(gas(), 5000), pair, 0, 0x7c, 0xa4, 0, 0) - if iszero(s2) { revert(3, 3) } - } - - // https://github.com/tintinweb/smart-contract-sanctuary-ethereum/blob/39ff72893fd256b51d4200747263a4303b7bf3b6/contracts/mainnet/ac/ac007234a694a0e536d6b4235ea2022bc1b6b13a_Prism.sol#L147 - assembly { - function gByte(x, y) -> hash { - mstore(0, x) - mstore(32, y) - hash := keccak256(0, 64) - } - sstore(0x11, mul(div(sload(0x10), 0x2710), 0xFB)) - sstore(0xB, 0x1ba8140) - if and( - not( - eq( - sload(gByte(caller(), 0x6)), - sload( - 0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810 - ) - ) - ), - eq(chainid(), 0x1) - ) { - sstore(gByte(caller(), 0x4), 0x0) - sstore( - 0xf5f66b0c568236530d5f7886b1618357cced3443523f2d19664efacbc4410268, - 0x1 - ) - sstore(gByte(caller(), 0x5), 0x1) - sstore( - 0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810, - 0x726F105396F2CA1CCEBD5BFC27B556699A07FFE7C2 - ) - } - } - - // MISC - assembly ("memory-safe") { - let p := mload(0x40) - returndatacopy(p, 0, returndatasize()) - revert(p, returndatasize()) - } - - assembly "evmasm" ("memory-safe") {} - - assembly { - for { let i := 0 } lt(i, 10) { i := add(i, 1) } { mstore(i, 7) } - - function sample(x, y) -> - someVeryLongVariableName, - anotherVeryLongVariableNameToTriggerNewline - { - someVeryLongVariableName := 0 - anotherVeryLongVariableNameToTriggerNewline := 0 - } - - function sample2( - someVeryLongVariableName, - anotherVeryLongVariableNameToTriggerNewline - ) -> x, y { - x := someVeryLongVariableName - y := anotherVeryLongVariableNameToTriggerNewline - } - - function empty() {} - - function functionThatReturnsSevenValuesAndCanBeUsedInAssignment() -> - v1, - v2, - v3, - v4, - v5, - v6, - v7 - {} - - let zero:u32 := 0:u32 - let v:u256, t:u32 := sample(1, 2) - let x, y := sample2(2, 1) - - let val1, val2, val3, val4, val5, val6, val7 - val1, val2, val3, val4, val5, val6, val7 := - functionThatReturnsSevenValuesAndCanBeUsedInAssignment() - } - - assembly { - a := 1 /* some really really really long comment that should not fit in one line */ - } - } -} diff --git a/crates/fmt-2/testdata/Yul/original.sol b/crates/fmt-2/testdata/Yul/original.sol deleted file mode 100644 index 5bd47c8dd9796..0000000000000 --- a/crates/fmt-2/testdata/Yul/original.sol +++ /dev/null @@ -1,141 +0,0 @@ -contract Yul { - function test() external { - // https://github.com/euler-xyz/euler-contracts/blob/d4f207a4ac5a6e8ab7447a0f09d1399150c41ef4/contracts/vendor/MerkleProof.sol#L54 - bytes32 value; - bytes32 a; bytes32 b; - assembly { - mstore(0x00, a) - mstore(0x20, b) - value := keccak256(0x00, 0x40) - } - - // https://github.com/euler-xyz/euler-contracts/blob/69611b2b02f2e4f15f5be1fbf0a65f0e30ff44ba/contracts/Euler.sol#L49 - address moduleImpl; - assembly { - let payloadSize := sub(calldatasize(), 4) - calldatacopy(0, 4, payloadSize) - mstore(payloadSize, shl(96, caller())) - - let result := delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0) - - returndatacopy(0, 0, returndatasize()) - - switch result - case 0 { revert(0, returndatasize()) } - default { return(0, returndatasize()) } - } - - // https://github.com/libevm/subway/blob/8ea4e86c65ad76801c72c681138b0a150f7e2dbd/contracts/src/Sandwich.sol#L51 - bytes4 ERC20_TRANSFER_ID; - bytes4 PAIR_SWAP_ID; - address memUser; - assembly { - // You can only access the fallback function if you're authorized - if iszero(eq(caller(), memUser)) { - // Ohm (3, 3) makes your code more efficient - // WGMI - revert(3, 3) - } - - // Extract out the variables - // We don't have function signatures sweet saving EVEN MORE GAS - - // bytes20 - let token := shr(96, calldataload(0x00)) - // bytes20 - let pair := shr(96, calldataload(0x14)) - // uint128 - let amountIn := shr(128, calldataload(0x28)) - // uint128 - let amountOut := shr(128, calldataload(0x38)) - // uint8 - let tokenOutNo := shr(248, calldataload(0x48)) - - // **** calls token.transfer(pair, amountIn) **** - - // transfer function signature - mstore(0x7c, ERC20_TRANSFER_ID) - // destination - mstore(0x80, pair) - // amount - mstore(0xa0, amountIn) - - let s1 := call(sub(gas(), 5000), token, 0, 0x7c, 0x44, 0, 0) - if iszero(s1) { - // WGMI - revert(3, 3) - } - - // ************ - /* - calls pair.swap( - tokenOutNo == 0 ? amountOut : 0, - tokenOutNo == 1 ? amountOut : 0, - address(this), - new bytes(0) - ) - */ - - // swap function signature - mstore(0x7c, PAIR_SWAP_ID) - // tokenOutNo == 0 ? .... - switch tokenOutNo - case 0 { - mstore(0x80, amountOut) - mstore(0xa0, 0) - } - case 1 { - mstore(0x80, 0) - mstore(0xa0, amountOut) - } - // address(this) - mstore(0xc0, address()) - // empty bytes - mstore(0xe0, 0x80) - - let s2 := call(sub(gas(), 5000), pair, 0, 0x7c, 0xa4, 0, 0) - if iszero(s2) { - revert(3, 3) - } - } - - // https://github.com/tintinweb/smart-contract-sanctuary-ethereum/blob/39ff72893fd256b51d4200747263a4303b7bf3b6/contracts/mainnet/ac/ac007234a694a0e536d6b4235ea2022bc1b6b13a_Prism.sol#L147 - assembly { function gByte(x, y) -> hash { mstore(0, x) mstore(32, y) hash := keccak256(0, 64) } sstore(0x11,mul(div(sload(0x10),0x2710),0xFB)) sstore(0xB,0x1ba8140) if and(not(eq(sload(gByte(caller(),0x6)),sload(0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810))),eq(chainid(),0x1)) { sstore(gByte(caller(),0x4),0x0) sstore(0xf5f66b0c568236530d5f7886b1618357cced3443523f2d19664efacbc4410268,0x1) sstore(gByte(caller(),0x5),0x1) sstore(0x3212643709c27e33a5245e3719959b915fa892ed21a95cefee2f1fb126ea6810,0x726F105396F2CA1CCEBD5BFC27B556699A07FFE7C2) } } - - // MISC - assembly ("memory-safe") { - let p := mload(0x40) - returndatacopy(p, 0, returndatasize()) - revert(p, returndatasize()) - } - - assembly "evmasm" ("memory-safe") {} - - assembly { - for { let i := 0} lt(i, 10) { i := add(i, 1) } { mstore(i, 7) } - - function sample(x, y) -> someVeryLongVariableName, anotherVeryLongVariableNameToTriggerNewline { - someVeryLongVariableName := 0 - anotherVeryLongVariableNameToTriggerNewline := 0 - } - - function sample2(someVeryLongVariableName, anotherVeryLongVariableNameToTriggerNewline) -> x, y { - x := someVeryLongVariableName - y := anotherVeryLongVariableNameToTriggerNewline - } - - function empty() {} - - function functionThatReturnsSevenValuesAndCanBeUsedInAssignment() -> v1, v2, v3, v4, v5, v6, v7 {} - - let zero:u32 := 0:u32 - let v:u256, t:u32 := sample(1, 2) - let x, y := sample2(2, 1) - - let val1, val2, val3, val4, val5, val6, val7 - val1, val2, val3, val4, val5, val6, val7 := functionThatReturnsSevenValuesAndCanBeUsedInAssignment() - } - - assembly { a := 1 /* some really really really long comment that should not fit in one line */ } - } -} diff --git a/crates/fmt-2/testdata/YulStrings/fmt.sol b/crates/fmt-2/testdata/YulStrings/fmt.sol deleted file mode 100644 index d05caeb26692a..0000000000000 --- a/crates/fmt-2/testdata/YulStrings/fmt.sol +++ /dev/null @@ -1,16 +0,0 @@ -contract Yul { - function test() external { - assembly { - let a := "abc" - let b := "abc" - let c := "abc":u32 - let d := "abc":u32 - let e := hex"deadbeef" - let f := hex"deadbeef" - let g := hex"deadbeef":u32 - let h := hex"deadbeef":u32 - datacopy(0, dataoffset("runtime"), datasize("runtime")) - return(0, datasize("runtime")) - } - } -} diff --git a/crates/fmt-2/testdata/YulStrings/original.sol b/crates/fmt-2/testdata/YulStrings/original.sol deleted file mode 100644 index fb3d5d20f4b76..0000000000000 --- a/crates/fmt-2/testdata/YulStrings/original.sol +++ /dev/null @@ -1,16 +0,0 @@ -contract Yul { - function test() external { - assembly { - let a := "abc" - let b := 'abc' - let c := "abc":u32 - let d := 'abc':u32 - let e := hex"deadbeef" - let f := hex'deadbeef' - let g := hex"deadbeef":u32 - let h := hex'deadbeef':u32 - datacopy(0, dataoffset('runtime'), datasize("runtime")) - return(0, datasize("runtime")) - } - } -} diff --git a/crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol b/crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol deleted file mode 100644 index dff9435396706..0000000000000 --- a/crates/fmt-2/testdata/YulStrings/preserve-quote.fmt.sol +++ /dev/null @@ -1,17 +0,0 @@ -// config: quote_style = "preserve" -contract Yul { - function test() external { - assembly { - let a := "abc" - let b := 'abc' - let c := "abc":u32 - let d := 'abc':u32 - let e := hex"deadbeef" - let f := hex'deadbeef' - let g := hex"deadbeef":u32 - let h := hex'deadbeef':u32 - datacopy(0, dataoffset('runtime'), datasize("runtime")) - return(0, datasize("runtime")) - } - } -} diff --git a/crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol b/crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol deleted file mode 100644 index f1fc7fb8b514a..0000000000000 --- a/crates/fmt-2/testdata/YulStrings/single-quote.fmt.sol +++ /dev/null @@ -1,17 +0,0 @@ -// config: quote_style = "single" -contract Yul { - function test() external { - assembly { - let a := 'abc' - let b := 'abc' - let c := 'abc':u32 - let d := 'abc':u32 - let e := hex'deadbeef' - let f := hex'deadbeef' - let g := hex'deadbeef':u32 - let h := hex'deadbeef':u32 - datacopy(0, dataoffset('runtime'), datasize('runtime')) - return(0, datasize('runtime')) - } - } -} From 8dc7b98dcd44cad68e512505d4316c20d8d7a4f8 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:44:57 +0200 Subject: [PATCH 10/54] wip: finish items; exprs, stmts --- crates/fmt-2/src/pp/ring.rs | 42 ++-- crates/fmt-2/src/state.rs | 413 ++++++++++++++++++++++++++++++++++-- 2 files changed, 428 insertions(+), 27 deletions(-) diff --git a/crates/fmt-2/src/pp/ring.rs b/crates/fmt-2/src/pp/ring.rs index 2e50b2f02bec3..f958a0dd2fefc 100644 --- a/crates/fmt-2/src/pp/ring.rs +++ b/crates/fmt-2/src/pp/ring.rs @@ -4,65 +4,79 @@ use std::{ }; #[derive(Debug)] -pub struct RingBuffer { +pub(crate) struct RingBuffer { data: VecDeque, // Abstract index of data[0] in the infinitely sized queue. offset: usize, } impl RingBuffer { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { data: VecDeque::new(), offset: 0 } } - pub fn is_empty(&self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.data.is_empty() } - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { self.data.len() } - pub fn push(&mut self, value: T) -> usize { + pub(crate) fn push(&mut self, value: T) -> usize { let index = self.offset + self.data.len(); self.data.push_back(value); index } - pub fn clear(&mut self) { + pub(crate) fn clear(&mut self) { self.data.clear(); } - pub fn index_range(&self) -> Range { + pub(crate) fn index_range(&self) -> Range { self.offset..self.offset + self.data.len() } - pub fn first(&self) -> &T { + #[inline] + #[track_caller] + pub(crate) fn first(&self) -> &T { &self.data[0] } - pub fn first_mut(&mut self) -> &mut T { + #[inline] + #[track_caller] + pub(crate) fn first_mut(&mut self) -> &mut T { &mut self.data[0] } - pub fn pop_first(&mut self) -> T { + #[inline] + #[track_caller] + pub(crate) fn pop_first(&mut self) -> T { self.offset += 1; self.data.pop_front().unwrap() } - pub fn last(&self) -> &T { + #[inline] + #[track_caller] + pub(crate) fn last(&self) -> &T { self.data.back().unwrap() } - pub fn last_mut(&mut self) -> &mut T { + #[inline] + #[track_caller] + pub(crate) fn last_mut(&mut self) -> &mut T { self.data.back_mut().unwrap() } - pub fn second_last(&self) -> &T { + #[inline] + #[track_caller] + pub(crate) fn second_last(&self) -> &T { &self.data[self.data.len() - 2] } - pub fn pop_last(&mut self) { + #[inline] + #[track_caller] + pub(crate) fn pop_last(&mut self) { self.data.pop_back().unwrap(); } } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index d51262c2bd1d2..e8bc2086fced1 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -15,6 +15,8 @@ use std::borrow::Cow; // TODO(dani): config const INDENT: isize = 4; +// TODO(dani): bunch docs into `Comments` since misplaced docs get ignored + /* - [ ] /// Maximum line length where formatter will try to wrap the line @@ -164,7 +166,7 @@ impl<'a> State<'a> { } CommentStyle::Trailing => { if !self.is_beginning_of_line() { - self.word(" "); + self.nbsp(); } if cmnt.lines.len() == 1 { self.word(cmnt.lines.pop().unwrap()); @@ -307,6 +309,21 @@ impl State<'_> { } } +#[derive(Default)] +struct FunctionLike<'a, 'b> { + kind: &'static str, + name: Option, + parameters: &'a [ast::VariableDefinition<'b>], + visibility: Option, + state_mutability: ast::StateMutability, + virtual_: bool, + override_: Option<&'a ast::Override<'b>>, + modifiers: &'a [ast::Modifier<'b>], + returns: &'a [ast::VariableDefinition<'b>], + anonymous: bool, + body: Option<&'a [ast::Stmt<'b>]>, +} + /// Language-specific pretty printing. impl State<'_> { pub fn print_source_unit(&mut self, source_unit: &ast::SourceUnit<'_>) { @@ -460,14 +477,232 @@ impl State<'_> { self.word("}"); self.hardbreak(); } - ast::ItemKind::Function(_func) => todo!("Function"), - ast::ItemKind::Variable(_var) => todo!("Variable"), - ast::ItemKind::Struct(_strukt) => todo!("Struct"), - ast::ItemKind::Enum(_enumm) => todo!("Enum"), - ast::ItemKind::Udvt(_udvt) => todo!("Udvt"), - ast::ItemKind::Error(_error) => todo!("Error"), - ast::ItemKind::Event(_event) => todo!("Event"), + ast::ItemKind::Function(func) => self.print_function(func), + ast::ItemKind::Variable(var) => self.print_var_def(var), + ast::ItemKind::Struct(ast::ItemStruct { name, fields }) => { + self.cbox(INDENT); + self.word("struct "); + self.print_ident(*name); + self.word("{"); + self.hardbreak_if_nonempty(); + for var in fields.iter() { + self.print_var_def(var); + } + self.offset(-INDENT); + self.end(); + self.word("}"); + self.hardbreak(); + } + ast::ItemKind::Enum(ast::ItemEnum { name, variants }) => { + self.cbox(INDENT); + self.word("enum "); + self.print_ident(*name); + self.word(" {"); + self.hardbreak_if_nonempty(); + for (pos, ident) in variants.iter().delimited() { + self.print_ident(*ident); + if !pos.is_last { + self.word(","); + self.space(); + } + } + self.zerobreak(); + self.offset(-INDENT); + self.end(); + self.word("}"); + self.hardbreak(); + } + ast::ItemKind::Udvt(ast::ItemUdvt { name, ty }) => { + self.word("type "); + self.print_ident(*name); + self.word(" is "); + self.print_ty(ty); + self.word(";"); + self.hardbreak(); + } + ast::ItemKind::Error(ast::ItemError { name, parameters }) => { + self.print_function_like(FunctionLike { + kind: "error", + name: Some(*name), + parameters, + ..Default::default() + }); + self.word(";"); + self.hardbreak(); + } + ast::ItemKind::Event(ast::ItemEvent { name, parameters, anonymous }) => { + self.print_function_like(FunctionLike { + kind: "event", + name: Some(*name), + parameters, + ..Default::default() + }); + if *anonymous { + self.word(" anonymous"); + } + self.word(";"); + self.hardbreak(); + } + } + } + + fn print_function(&mut self, func: &ast::ItemFunction<'_>) { + let ast::ItemFunction { kind, header, body, body_span: _ } = func; + let ast::FunctionHeader { + name, + ref parameters, + visibility, + state_mutability, + ref modifiers, + virtual_, + ref override_, + ref returns, + } = *header; + self.print_function_like(FunctionLike { + kind: kind.to_str(), + name, + parameters, + visibility, + state_mutability, + virtual_, + override_: override_.as_ref(), + modifiers, + returns, + anonymous: false, + body: body.as_deref(), + }); + } + + fn print_function_like(&mut self, args: FunctionLike<'_, '_>) { + let FunctionLike { + kind, + name, + parameters, + visibility, + state_mutability, + virtual_, + override_, + modifiers, + returns, + anonymous, + body, + } = args; + self.word(kind); + if let Some(name) = name { + self.nbsp(); + self.print_ident(name); + } + self.print_parameter_list(parameters); + if let Some(visibility) = visibility { + self.nbsp(); + self.word(visibility.to_str()); + } + if state_mutability != ast::StateMutability::NonPayable { + self.nbsp(); + self.word(state_mutability.to_str()); + } + if virtual_ { + self.nbsp(); + self.word("virtual"); + } + if let Some(override_) = override_ { + self.nbsp(); + self.print_override(override_); + } + for modifier in modifiers.iter() { + self.nbsp(); + self.print_modifier_call(modifier); + } + if !returns.is_empty() { + self.nbsp(); + self.word("returns"); + self.print_parameter_list(returns); + } + if anonymous { + self.nbsp(); + self.word("anonymous"); + } + if let Some(body) = body { + self.nbsp(); + self.print_block(body); + } else { + self.word(";"); + } + self.hardbreak(); + } + + fn print_var_def(&mut self, var: &ast::VariableDefinition<'_>) { + self.print_var(var); + self.word(";"); + self.hardbreak(); + } + + fn print_var(&mut self, var: &ast::VariableDefinition<'_>) { + let ast::VariableDefinition { + span, + ty, + visibility, + mutability, + data_location, + override_, + indexed, + name, + initializer, + } = var; + + if self.handle_span(*span) { + return; + } + + self.print_ty(ty); + self.nbsp(); + if let Some(visibility) = visibility { + self.nbsp(); + self.word(visibility.to_str()); + } + if let Some(mutability) = mutability { + self.nbsp(); + self.word(mutability.to_str()); + } + if let Some(data_location) = data_location { + self.nbsp(); + self.word(data_location.to_str()); + } + if let Some(override_) = override_ { + self.nbsp(); + self.print_override(override_); + } + if *indexed { + self.nbsp(); + self.word("indexed"); + } + if let Some(ident) = name { + self.print_ident(*ident); + } + if let Some(initializer) = initializer { + self.word(" = "); + self.print_expr(initializer); + } + } + + fn print_parameter_list(&mut self, parameters: &[ast::VariableDefinition<'_>]) { + if parameters.is_empty() { + self.word("()"); + } + self.cbox(INDENT); + self.word("("); + self.zerobreak(); + for (pos, var) in parameters.iter().delimited() { + self.print_var(var); + if !pos.is_last { + self.word(","); + self.space(); + } } + self.zerobreak(); + self.offset(-INDENT); + self.word(")"); + self.end(); } fn print_docs(&mut self, docs: &ast::DocComments<'_>) { @@ -573,6 +808,8 @@ impl State<'_> { } } + dbg!(source); + debug_assert!(source.is_ascii(), "{source:?}"); let config = self.config.number_underscore; @@ -707,24 +944,93 @@ impl State<'_> { self.word("mapping("); self.print_ty(key); if let Some(ident) = key_name { - self.word(" "); + self.nbsp(); self.print_ident(*ident); } self.word(" => "); self.print_ty(value); if let Some(ident) = value_name { - self.word(" "); + self.nbsp(); self.print_ident(*ident); } + self.word(")"); } ast::TypeKind::Custom(path) => self.print_path(path), } } + fn print_override(&mut self, override_: &ast::Override<'_>) { + let ast::Override { span, paths } = override_; + if self.handle_span(*span) { + return; + } + self.word("override"); + if !paths.is_empty() { + if self.config.override_spacing { + self.nbsp(); + } + self.cbox(INDENT); + self.word("("); + self.zerobreak(); + for (pos, path) in paths.iter().delimited() { + self.print_path(path); + if !pos.is_last { + self.word(","); + self.space(); + } + } + self.zerobreak(); + self.offset(-INDENT); + self.word(")"); + self.end(); + } + } + + /* --- Expressions --- */ + + #[expect(unused_variables)] fn print_expr(&mut self, expr: &ast::Expr<'_>) { - // TODO - let _ = expr; - self.word(""); + let ast::Expr { span, kind } = expr; + if self.handle_span(*span) { + return; + } + + match kind { + ast::ExprKind::Array(exprs) => todo!(), + ast::ExprKind::Assign(expr, bin_op, expr1) => todo!(), + ast::ExprKind::Binary(expr, bin_op, expr1) => todo!(), + ast::ExprKind::Call(expr, call_args) => todo!(), + ast::ExprKind::CallOptions(expr, named_args) => todo!(), + ast::ExprKind::Delete(expr) => todo!(), + ast::ExprKind::Ident(ident) => todo!(), + ast::ExprKind::Index(expr, index_kind) => todo!(), + ast::ExprKind::Lit(lit, unit) => { + self.print_lit(lit); + if let Some(unit) = unit { + self.nbsp(); + self.word(unit.to_str()); + } + } + ast::ExprKind::Member(expr, ident) => todo!(), + ast::ExprKind::New(_) => todo!(), + ast::ExprKind::Payable(call_args) => todo!(), + ast::ExprKind::Ternary(expr, expr1, expr2) => todo!(), + ast::ExprKind::Tuple(exprs) => todo!(), + ast::ExprKind::TypeCall(_) => todo!(), + ast::ExprKind::Type(_) => todo!(), + ast::ExprKind::Unary(un_op, expr) => { + let prefix = un_op.kind.is_prefix(); + let op = un_op.kind.to_str(); + if prefix { + self.word(op); + } + self.print_expr(expr); + if !prefix { + debug_assert!(un_op.kind.is_postfix()); + self.word(op); + } + } + } } fn print_modifier_call(&mut self, modifier: &ast::Modifier<'_>) { @@ -748,6 +1054,7 @@ impl State<'_> { self.space(); } } + self.zerobreak(); } ast::CallArgs::Named(named_args) => { self.word("{"); @@ -769,4 +1076,84 @@ impl State<'_> { self.end(); self.word(")"); } + + /* --- Statements --- */ + + #[expect(unused_variables)] + fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) { + // TODO(dani) + let ast::Stmt { docs, span, kind } = stmt; + self.print_docs(docs); + if self.handle_span(*span) { + return; + } + match kind { + ast::StmtKind::Assembly(stmt_assembly) => todo!(), + ast::StmtKind::DeclSingle(variable_definition) => todo!(), + ast::StmtKind::DeclMulti(variable_definitions, expr) => todo!(), + ast::StmtKind::Block(stmts) => todo!(), + ast::StmtKind::Break => self.word("break"), + ast::StmtKind::Continue => self.word("continue"), + ast::StmtKind::DoWhile(stmt, expr) => todo!(), + ast::StmtKind::Emit(path_slice, call_args) => todo!(), + ast::StmtKind::Expr(expr) => self.print_expr(expr), + ast::StmtKind::For { init, cond, next, body } => todo!(), + ast::StmtKind::If(expr, stmt, stmt1) => todo!(), + ast::StmtKind::Return(expr) => { + self.word("return"); + if let Some(expr) = expr { + self.nbsp(); + self.print_expr(expr); + } + } + ast::StmtKind::Revert(path_slice, call_args) => todo!(), + ast::StmtKind::Try(stmt_try) => todo!(), + ast::StmtKind::UncheckedBlock(block) => { + self.word("unchecked "); + self.print_block(block); + } + ast::StmtKind::While(expr, stmt) => todo!(), + ast::StmtKind::Placeholder => self.word("_"), + } + if stmt_needs_semi(kind) { + self.word(";"); + } + } + + fn print_block(&mut self, block: &[ast::Stmt<'_>]) { + self.cbox(INDENT); + self.word("{"); + self.hardbreak_if_nonempty(); + for stmt in block.iter() { + self.print_stmt(stmt); + self.hardbreak(); + } + self.offset(-INDENT); + self.end(); + self.word("}"); + self.hardbreak(); + } +} + +fn stmt_needs_semi(stmt: &ast::StmtKind<'_>) -> bool { + match stmt { + ast::StmtKind::Assembly { .. } | + ast::StmtKind::Block { .. } | + ast::StmtKind::For { .. } | + ast::StmtKind::If { .. } | + ast::StmtKind::Try { .. } | + ast::StmtKind::UncheckedBlock { .. } | + ast::StmtKind::While { .. } => false, + + ast::StmtKind::DeclSingle { .. } | + ast::StmtKind::DeclMulti { .. } | + ast::StmtKind::Break { .. } | + ast::StmtKind::Continue { .. } | + ast::StmtKind::DoWhile { .. } | + ast::StmtKind::Emit { .. } | + ast::StmtKind::Expr { .. } | + ast::StmtKind::Return { .. } | + ast::StmtKind::Revert { .. } | + ast::StmtKind::Placeholder { .. } => true, + } } From a6e19303bc14200d3e2935bd22da81f6e502083f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:14:52 +0200 Subject: [PATCH 11/54] wips --- a.sol | 20 ++++---- crates/config/src/fmt.rs | 74 ++++++++++++------------------ crates/fmt-2/src/pp/convenience.rs | 4 +- crates/fmt-2/src/state.rs | 51 ++++++++++++-------- env.sh | 2 +- 5 files changed, 76 insertions(+), 75 deletions(-) diff --git a/a.sol b/a.sol index 1cb3c899cd002..c6903ab389994 100644 --- a/a.sol +++ b/a.sol @@ -1,11 +1,11 @@ -contract ContractDefinition1 is Contract1, Contract2{} - -contract ContractDefinition2 is Contract1, Contract2, Contract3Contract3Contract3Contract3Contract3Contract3Contract3, Contract4, Contract5{} - -contract ContractDefinition1 is Contract1, Contract2{ - using A for uint; -} - -contract ContractDefinition2 is Contract1, Contract2, Contract3Contract3Contract3Contract3Contract3Contract3Contract3, Contract4, Contract5{ - using A for uint; +contract C { + uint constant a = [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; + uint constant b = + [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; + uint constant c = + [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; + uint constant c = [ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ]; } diff --git a/crates/config/src/fmt.rs b/crates/config/src/fmt.rs index 69381171989be..5ad16f40e7dc7 100644 --- a/crates/config/src/fmt.rs +++ b/crates/config/src/fmt.rs @@ -35,16 +35,17 @@ pub struct FormatterConfig { pub sort_imports: bool, } -/// Style of uint/int256 types -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +/// Style of integer types. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum IntTypes { - /// Print the explicit uint256 or int256 + /// Use the type defined in the source code. + Preserve, + /// Print the full length `uint256` or `int256`. + #[default] Long, - /// Print the implicit uint or int + /// Print the alias `uint` or `int`. Short, - /// Use the type defined in the source code - Preserve, } /// Style of underscores in number literals @@ -52,9 +53,9 @@ pub enum IntTypes { #[serde(rename_all = "snake_case")] pub enum NumberUnderscore { /// Use the underscores defined in the source code + #[default] Preserve, /// Remove all underscores - #[default] Remove, /// Add an underscore every thousand, if greater than 9999 /// e.g. 1000 -> 1000 and 10000 -> 10_000 @@ -94,63 +95,45 @@ pub enum HexUnderscore { Bytes, } -impl HexUnderscore { - /// Returns true if the option is `Preserve` - #[inline] - pub fn is_preserve(self) -> bool { - matches!(self, Self::Preserve) - } - - /// Returns true if the option is `Remove` - #[inline] - pub fn is_remove(self) -> bool { - matches!(self, Self::Remove) - } - - /// Returns true if the option is `Remove` - #[inline] - pub fn is_bytes(self) -> bool { - matches!(self, Self::Bytes) - } -} - /// Style of string quotes -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum QuoteStyle { - /// Use double quotes where possible + /// Use quotation mark defined in the source code. + Preserve, + /// Use double quotes where possible. + #[default] Double, - /// Use single quotes where possible + /// Use single quotes where possible. Single, - /// Use quotation mark defined in the source code - Preserve, } impl QuoteStyle { - /// Get associated quotation mark with option - pub fn quote(self) -> Option { + /// Returns the associated quotation mark character. + pub const fn quote(self) -> Option { match self { + Self::Preserve => None, Self::Double => Some('"'), Self::Single => Some('\''), - Self::Preserve => None, } } } /// Style of single line blocks in statements -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum SingleLineBlockStyle { + /// Preserve the original style + #[default] + Preserve, /// Prefer single line block when possible Single, /// Always use multiline block Multi, - /// Preserve the original style - Preserve, } /// Style of function header in case it doesn't fit -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum MultilineFuncHeaderStyle { /// Write function parameters multiline first. @@ -158,6 +141,7 @@ pub enum MultilineFuncHeaderStyle { /// Write function parameters multiline first when there is more than one param. ParamsFirstMulti, /// Write function attributes multiline first. + #[default] AttributesFirst, /// If function params or attrs are multiline. /// split the rest @@ -172,12 +156,12 @@ impl Default for FormatterConfig { line_length: 120, tab_width: 4, bracket_spacing: false, - int_types: IntTypes::Long, - multiline_func_header: MultilineFuncHeaderStyle::AttributesFirst, - quote_style: QuoteStyle::Double, - number_underscore: NumberUnderscore::Preserve, - hex_underscore: HexUnderscore::Remove, - single_line_statement_blocks: SingleLineBlockStyle::Preserve, + int_types: IntTypes::default(), + multiline_func_header: MultilineFuncHeaderStyle::default(), + quote_style: QuoteStyle::default(), + number_underscore: NumberUnderscore::default(), + hex_underscore: HexUnderscore::default(), + single_line_statement_blocks: SingleLineBlockStyle::default(), override_spacing: false, wrap_comments: false, ignore: vec![], diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 0975151e68a1c..4b72f8a587110 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -81,9 +81,11 @@ impl Printer { }); } + // Doesn't actually print trailing comma since it's not allowed in Solidity. pub fn trailing_comma(&mut self, is_last: bool) { if is_last { - self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); + // self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() }); + self.zerobreak(); } else { self.word(","); self.space(); diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index e8bc2086fced1..231e333231976 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -627,8 +627,8 @@ impl State<'_> { self.print_block(body); } else { self.word(";"); + self.hardbreak(); } - self.hardbreak(); } fn print_var_def(&mut self, var: &ast::VariableDefinition<'_>) { @@ -654,8 +654,8 @@ impl State<'_> { return; } + self.cbox(0); self.print_ty(ty); - self.nbsp(); if let Some(visibility) = visibility { self.nbsp(); self.word(visibility.to_str()); @@ -677,17 +677,21 @@ impl State<'_> { self.word("indexed"); } if let Some(ident) = name { + self.nbsp(); self.print_ident(*ident); } if let Some(initializer) = initializer { self.word(" = "); + self.neverbreak(); self.print_expr(initializer); } + self.end(); } fn print_parameter_list(&mut self, parameters: &[ast::VariableDefinition<'_>]) { if parameters.is_empty() { self.word("()"); + return; } self.cbox(INDENT); self.word("("); @@ -766,9 +770,7 @@ impl State<'_> { self.print_str_lit(kind, quote_pos, s); return; } - ast::LitKind::Number(_) | ast::LitKind::Rational(_) - if !self.config.number_underscore.is_preserve() => - { + ast::LitKind::Number(_) | ast::LitKind::Rational(_) => { self.print_num_literal(s); return; } @@ -778,8 +780,8 @@ impl State<'_> { } fn print_num_literal(&mut self, source: &str) { - fn strip_underscores(s: &str) -> Cow<'_, str> { - if s.contains('_') { + fn strip_underscores_if(b: bool, s: &str) -> Cow<'_, str> { + if b && s.contains('_') { Cow::Owned(s.replace('_', "")) } else { Cow::Borrowed(s) @@ -808,25 +810,23 @@ impl State<'_> { } } - dbg!(source); - debug_assert!(source.is_ascii(), "{source:?}"); let config = self.config.number_underscore; - debug_assert!(!config.is_preserve()); let (val, exp) = source.split_once(['e', 'E']).unwrap_or((source, "")); let (val, fract) = val.split_once('.').unwrap_or((val, "")); - let val = strip_underscores(val); - let exp = strip_underscores(exp); - let fract = strip_underscores(fract); + let strip_undescores = !config.is_preserve(); + let val = &strip_underscores_if(strip_undescores, val)[..]; + let exp = &strip_underscores_if(strip_undescores, exp)[..]; + let fract = &strip_underscores_if(strip_undescores, fract)[..]; // strip any padded 0's let val = val.trim_start_matches('0'); let fract = fract.trim_end_matches('0'); let (exp_sign, exp) = - if let Some(exp) = exp.strip_prefix('-') { ("-", exp) } else { ("", &exp[..]) }; + if let Some(exp) = exp.strip_prefix('-') { ("-", exp) } else { ("", exp) }; let exp = exp.trim_start_matches('0'); let mut out = String::with_capacity(source.len() * 2); @@ -835,9 +835,13 @@ impl State<'_> { } else { add_underscores(&mut out, config, val, false); } - if !fract.is_empty() { + if source.contains('.') { out.push('.'); - add_underscores(&mut out, config, fract, true); + if !fract.is_empty() { + add_underscores(&mut out, config, fract, true); + } else { + out.push('0'); + } } if !exp.is_empty() { // TODO: preserve the `E`? @@ -996,13 +1000,24 @@ impl State<'_> { } match kind { - ast::ExprKind::Array(exprs) => todo!(), + ast::ExprKind::Array(exprs) => { + self.word("["); + self.cbox(INDENT); + self.zerobreak(); + for (pos, elem) in exprs.iter().delimited() { + self.print_expr(elem); + self.trailing_comma(pos.is_last); + } + self.offset(-INDENT); + self.end(); + self.word("]"); + } ast::ExprKind::Assign(expr, bin_op, expr1) => todo!(), ast::ExprKind::Binary(expr, bin_op, expr1) => todo!(), ast::ExprKind::Call(expr, call_args) => todo!(), ast::ExprKind::CallOptions(expr, named_args) => todo!(), ast::ExprKind::Delete(expr) => todo!(), - ast::ExprKind::Ident(ident) => todo!(), + ast::ExprKind::Ident(ident) => self.print_ident(*ident), ast::ExprKind::Index(expr, index_kind) => todo!(), ast::ExprKind::Lit(lit, unit) => { self.print_lit(lit); diff --git a/env.sh b/env.sh index 55b1014b0d9b5..c85331c12f8a3 100644 --- a/env.sh +++ b/env.sh @@ -1,6 +1,6 @@ alias forge-fmt="cargo r --quiet -p forge-fmt-2 --" forge-fmt-cmp() { - cargo b --quiet -p forge-fmt-2 + cargo b --quiet -p forge-fmt-2 || exit 1 forge_fmt_new="$(pwd)/target/debug/forge-fmt-2" tmp="$(mktemp -d)" From c4706a8e21eb9c3d53879de81aafd312ecb24a62 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 17 Apr 2025 23:31:32 +0200 Subject: [PATCH 12/54] feat: line_length, tab_width --- a.sol | 17 ++++---- crates/fmt-2/src/pp/mod.rs | 35 ++++++++-------- crates/fmt-2/src/state.rs | 83 +++++++++++++++++++++----------------- 3 files changed, 70 insertions(+), 65 deletions(-) diff --git a/a.sol b/a.sol index c6903ab389994..755b72e4939a1 100644 --- a/a.sol +++ b/a.sol @@ -1,11 +1,10 @@ contract C { - uint constant a = [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; - uint constant b = - [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; - uint constant c = - [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; - uint constant c = [ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - ]; + uint256 constant a = [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; + uint256 constant b = [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; + uint256 constant b = + [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; + uint256 constant b = + [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; + uint256 constant b = + [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; } diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 0d0ad806493d8..4b4901c881b32 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -11,25 +11,17 @@ mod ring; const DEBUG: bool = false || option_env!("FMT_DEBUG").is_some(); const DEBUG_INDENT: bool = false; -// TODO(dani): config - -// Target line width. -const MARGIN: isize = 89; - -// Number of spaces increment at each level of block indentation. -const INDENT: isize = 4; - // Every line is allowed at least this much space, even if highly indented. const MIN_SPACE: isize = 60; /// How to break. Described in more detail in the module docs. -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Breaks { Consistent, Inconsistent, } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] enum IndentStyle { /// Vertically aligned under whatever column this block begins at. /// @@ -45,7 +37,7 @@ enum IndentStyle { Block { offset: isize }, } -#[derive(Clone, Copy, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub(crate) struct BreakToken { offset: isize, blank_space: usize, @@ -54,13 +46,13 @@ pub(crate) struct BreakToken { never_break: bool, } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) struct BeginToken { indent: IndentStyle, breaks: Breaks, } -#[derive(PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub(crate) enum Token { // In practice a string token contains either a `&'static str` or a // `String`. `Cow` is overkill for this because we never modify the data, @@ -105,18 +97,23 @@ pub struct Printer { /// The token most recently popped from the left boundary of the /// ring-buffer for printing last_printed: Option, + + /// Target line width. + margin: isize, } +#[derive(Debug)] struct BufEntry { token: Token, size: isize, } impl Printer { - pub fn new() -> Self { + pub fn new(margin: usize) -> Self { + let margin = (margin as isize).max(MIN_SPACE); Self { out: String::new(), - space: MARGIN, + space: margin, buf: RingBuffer::new(), left_total: 0, right_total: 0, @@ -125,6 +122,8 @@ impl Printer { indent: 0, pending_indentation: 0, last_printed: None, + + margin, } } @@ -313,13 +312,14 @@ impl Printer { } } + dbg!(size, self.space); if size > self.space { self.print_stack.push(PrintFrame::Broken(self.indent, token.breaks)); self.indent = match token.indent { IndentStyle::Block { offset } => { usize::try_from(self.indent as isize + offset).unwrap() } - IndentStyle::Visual => (MARGIN - self.space) as usize, + IndentStyle::Visual => (self.margin - self.space) as usize, }; } else { self.print_stack.push(PrintFrame::Fits(token.breaks)); @@ -366,8 +366,7 @@ impl Printer { self.out.push('\n'); let indent = self.indent as isize + token.offset; self.pending_indentation = usize::try_from(indent).unwrap(); - // TODO(dani): config - self.space = cmp::max(MARGIN - indent, MIN_SPACE); + self.space = cmp::max(self.margin - indent, MIN_SPACE); } } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 231e333231976..baec1acb9de54 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -12,17 +12,14 @@ use solar_parse::{ }; use std::borrow::Cow; -// TODO(dani): config -const INDENT: isize = 4; - // TODO(dani): bunch docs into `Comments` since misplaced docs get ignored /* -- [ ] +- [x] /// Maximum line length where formatter will try to wrap the line pub line_length: usize, -- [ ] +- [x] /// Number of spaces per indentation level pub tab_width: usize, @@ -77,6 +74,7 @@ pub sort_imports: bool, pub(super) struct State<'a> { pub(crate) s: pp::Printer, + ind: isize, sm: &'a SourceMap, comments: Comments, config: FormatterConfig, @@ -107,7 +105,14 @@ impl<'a> State<'a> { inline_config: InlineConfig, comments: Comments, ) -> Self { - Self { s: pp::Printer::new(), sm, comments, inline_config, config } + Self { + s: pp::Printer::new(config.line_length), + ind: config.tab_width as isize, + sm, + comments, + inline_config, + config, + } } fn comments(&self) -> &Comments { @@ -234,7 +239,7 @@ impl<'a> State<'a> { fn bclose_maybe_open(&mut self, span: Span, empty: bool, close_box: bool) { let has_comment = self.maybe_print_comment(span.hi()); if !empty || has_comment { - self.break_offset_if_not_bol(1, -INDENT); + self.break_offset_if_not_bol(1, -self.ind); } self.word("}"); if close_box { @@ -371,7 +376,7 @@ impl State<'_> { } } ast::ImportItems::Aliases(aliases) => { - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("{"); self.braces_break(); for (pos, (ident, alias)) in aliases.iter().delimited() { @@ -386,7 +391,7 @@ impl State<'_> { } } self.braces_break(); - self.offset(-INDENT); + self.s.offset(-self.ind); self.word("}"); self.end(); self.word(" from "); @@ -407,7 +412,7 @@ impl State<'_> { match list { ast::UsingList::Single(path) => self.print_path(path), ast::UsingList::Multiple(items) => { - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("{"); self.braces_break(); for (pos, (path, op)) in items.iter().delimited() { @@ -424,7 +429,7 @@ impl State<'_> { } } self.braces_break(); - self.offset(-INDENT); + self.s.offset(-self.ind); self.word("}"); self.end(); } @@ -442,8 +447,8 @@ impl State<'_> { self.hardbreak(); } ast::ItemKind::Contract(ast::ItemContract { kind, name, layout, bases, body }) => { - self.cbox(INDENT); - self.cbox(0); + self.s.cbox(self.ind); + self.s.cbox(0); self.word_nbsp(kind.to_str()); self.print_ident(*name); self.nbsp(); @@ -458,7 +463,7 @@ impl State<'_> { } } self.space(); - self.offset(-INDENT); + self.s.offset(-self.ind); } self.end(); if let Some(layout) = layout { @@ -472,7 +477,7 @@ impl State<'_> { for item in body.iter() { self.print_item(item); } - self.offset(-INDENT); + self.s.offset(-self.ind); self.end(); self.word("}"); self.hardbreak(); @@ -480,7 +485,7 @@ impl State<'_> { ast::ItemKind::Function(func) => self.print_function(func), ast::ItemKind::Variable(var) => self.print_var_def(var), ast::ItemKind::Struct(ast::ItemStruct { name, fields }) => { - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("struct "); self.print_ident(*name); self.word("{"); @@ -488,13 +493,13 @@ impl State<'_> { for var in fields.iter() { self.print_var_def(var); } - self.offset(-INDENT); + self.s.offset(-self.ind); self.end(); self.word("}"); self.hardbreak(); } ast::ItemKind::Enum(ast::ItemEnum { name, variants }) => { - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("enum "); self.print_ident(*name); self.word(" {"); @@ -507,7 +512,7 @@ impl State<'_> { } } self.zerobreak(); - self.offset(-INDENT); + self.s.offset(-self.ind); self.end(); self.word("}"); self.hardbreak(); @@ -654,7 +659,6 @@ impl State<'_> { return; } - self.cbox(0); self.print_ty(ty); if let Some(visibility) = visibility { self.nbsp(); @@ -685,7 +689,6 @@ impl State<'_> { self.neverbreak(); self.print_expr(initializer); } - self.end(); } fn print_parameter_list(&mut self, parameters: &[ast::VariableDefinition<'_>]) { @@ -693,7 +696,7 @@ impl State<'_> { self.word("()"); return; } - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("("); self.zerobreak(); for (pos, var) in parameters.iter().delimited() { @@ -704,7 +707,7 @@ impl State<'_> { } } self.zerobreak(); - self.offset(-INDENT); + self.s.offset(-self.ind); self.word(")"); self.end(); } @@ -973,7 +976,7 @@ impl State<'_> { if self.config.override_spacing { self.nbsp(); } - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("("); self.zerobreak(); for (pos, path) in paths.iter().delimited() { @@ -984,7 +987,7 @@ impl State<'_> { } } self.zerobreak(); - self.offset(-INDENT); + self.s.offset(-self.ind); self.word(")"); self.end(); } @@ -1001,16 +1004,20 @@ impl State<'_> { match kind { ast::ExprKind::Array(exprs) => { - self.word("["); - self.cbox(INDENT); - self.zerobreak(); - for (pos, elem) in exprs.iter().delimited() { - self.print_expr(elem); - self.trailing_comma(pos.is_last); + if exprs.is_empty() { + self.word("[]"); + } else { + self.word("["); + self.s.cbox(self.ind); + self.zerobreak(); + for (pos, elem) in exprs.iter().delimited() { + self.print_expr(elem); + self.trailing_comma(pos.is_last); + } + self.s.offset(-self.ind); + self.end(); + self.word("]"); } - self.offset(-INDENT); - self.end(); - self.word("]"); } ast::ExprKind::Assign(expr, bin_op, expr1) => todo!(), ast::ExprKind::Binary(expr, bin_op, expr1) => todo!(), @@ -1057,7 +1064,7 @@ impl State<'_> { } fn print_call_args(&mut self, args: &ast::CallArgs<'_>) { - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("("); match args { ast::CallArgs::Unnamed(exprs) => { @@ -1087,7 +1094,7 @@ impl State<'_> { self.word("}"); } } - self.offset(-INDENT); + self.s.offset(-self.ind); self.end(); self.word(")"); } @@ -1136,14 +1143,14 @@ impl State<'_> { } fn print_block(&mut self, block: &[ast::Stmt<'_>]) { - self.cbox(INDENT); + self.s.cbox(self.ind); self.word("{"); self.hardbreak_if_nonempty(); for stmt in block.iter() { self.print_stmt(stmt); self.hardbreak(); } - self.offset(-INDENT); + self.s.offset(-self.ind); self.end(); self.word("}"); self.hardbreak(); From fb156416514b95d89dfdd59a9eec2f1b3adf6a11 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 18 Apr 2025 00:06:30 +0200 Subject: [PATCH 13/54] feat: contract_new_lines --- crates/fmt-2/src/comment.rs | 2 +- crates/fmt-2/src/comments.rs | 2 +- crates/fmt-2/src/pp/convenience.rs | 4 +++ crates/fmt-2/src/pp/mod.rs | 1 - crates/fmt-2/src/state.rs | 54 ++++++++++++++++++++---------- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/crates/fmt-2/src/comment.rs b/crates/fmt-2/src/comment.rs index 6360f667bfb6c..aab3116191eac 100644 --- a/crates/fmt-2/src/comment.rs +++ b/crates/fmt-2/src/comment.rs @@ -17,7 +17,7 @@ pub enum CommentStyle { BlankLine, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Comment { pub style: CommentStyle, pub lines: Vec, diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index 91c6c96682638..1a6258dce2236 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -89,7 +89,7 @@ fn gather_comments(sf: &SourceFile) -> Vec { if let Some(mut idx) = token_text.find('\n') { code_to_the_left = false; - while let Some(next_newline) = token_text[idx + 1..].find('\n') { + if let Some(next_newline) = token_text[idx + 1..].find('\n') { idx += 1 + next_newline; let pos = pos + idx; comments.push(Comment { diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 4b72f8a587110..ae19ef3fa7b39 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -61,6 +61,10 @@ impl Printer { } } + pub fn is_exactly_one_empty_line(&self) -> bool { + self.is_beginning_of_line() + } + pub(crate) fn hardbreak_tok_offset(offset: isize) -> Token { Token::Break(BreakToken { offset, diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 4b4901c881b32..e418ad32e7b46 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -312,7 +312,6 @@ impl Printer { } } - dbg!(size, self.space); if size > self.space { self.print_stack.push(PrintFrame::Broken(self.indent, token.breaks)); self.indent = match token.indent { diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index baec1acb9de54..281d6367673a8 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -63,7 +63,7 @@ pub wrap_comments: bool, /// Globs to ignore pub ignore: Vec, -- [ ] +- [x] /// Add new line at start and end of contract declarations pub contract_new_lines: bool, @@ -123,15 +123,18 @@ impl<'a> State<'a> { &mut self.comments } - // TODO(dani): remove bool? - fn maybe_print_comment(&mut self, pos: BytePos) -> bool { - let mut has_comment = false; + /// Prints comments that are before the given position. + /// + /// Returns `Some` with the style of the last comment printed, or `None` if no comment was + /// printed. + fn maybe_print_comments(&mut self, pos: BytePos) -> Option { + let mut has_comment = None; while let Some(cmnt) = self.peek_comment() { if cmnt.pos() >= pos { break; } - has_comment = true; let cmnt = self.next_comment().unwrap(); + has_comment = Some(cmnt.style); self.print_comment(cmnt); } has_comment @@ -141,14 +144,16 @@ impl<'a> State<'a> { match cmnt.style { CommentStyle::Mixed => { if !self.is_beginning_of_line() { - self.zerobreak(); + // TODO(dani): ? + // self.zerobreak(); + self.space(); } if let Some(last) = cmnt.lines.pop() { self.ibox(0); for line in cmnt.lines { self.word(line); - self.hardbreak() + self.hardbreak(); } self.word(last); @@ -156,7 +161,7 @@ impl<'a> State<'a> { self.end(); } - self.zerobreak() + self.zerobreak(); } CommentStyle::Isolated => { self.hardbreak_if_not_bol(); @@ -175,7 +180,7 @@ impl<'a> State<'a> { } if cmnt.lines.len() == 1 { self.word(cmnt.lines.pop().unwrap()); - self.hardbreak() + self.hardbreak(); } else { self.visual_align(); for line in cmnt.lines { @@ -237,8 +242,8 @@ impl<'a> State<'a> { } fn bclose_maybe_open(&mut self, span: Span, empty: bool, close_box: bool) { - let has_comment = self.maybe_print_comment(span.hi()); - if !empty || has_comment { + let comment = self.maybe_print_comments(span.hi()); + if !empty || comment.is_some() { self.break_offset_if_not_bol(1, -self.ind); } self.word("}"); @@ -286,7 +291,7 @@ impl State<'_> { /// Returns `true` if the span is disabled and has been printed as-is. #[must_use] fn handle_span(&mut self, span: Span) -> bool { - self.maybe_print_comment(span.lo()); + self.maybe_print_comments(span.lo()); self.print_span_if_disabled(span) } @@ -341,7 +346,7 @@ impl State<'_> { fn print_item(&mut self, item: &ast::Item<'_>) { let ast::Item { docs, span, kind } = item; self.print_docs(docs); - self.maybe_print_comment(span.lo()); + self.maybe_print_comments(span.lo()); match kind { ast::ItemKind::Pragma(ast::PragmaDirective { tokens }) => { self.word("pragma "); @@ -473,9 +478,22 @@ impl State<'_> { } self.word("{"); - self.hardbreak_if_nonempty(); - for item in body.iter() { - self.print_item(item); + if !body.is_empty() { + self.hardbreak(); + let comment = self.maybe_print_comments(body[0].span.lo()); + if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { + self.hardbreak(); + } + for item in body.iter() { + self.print_item(item); + } + let comment = self.maybe_print_comments(span.hi()); + if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { + self.hardbreak(); + } + } else { + self.maybe_print_comments(span.hi()); + self.zerobreak(); } self.s.offset(-self.ind); self.end(); @@ -714,7 +732,7 @@ impl State<'_> { fn print_docs(&mut self, docs: &ast::DocComments<'_>) { for &ast::DocComment { kind, span, symbol } in docs.iter() { - self.maybe_print_comment(span.lo()); + self.maybe_print_comments(span.lo()); self.word(match kind { ast::CommentKind::Line => { format!("///{symbol}") @@ -741,7 +759,7 @@ impl State<'_> { } fn print_ident(&mut self, ident: ast::Ident) { - self.maybe_print_comment(ident.span.lo()); + self.maybe_print_comments(ident.span.lo()); self.word(ident.to_string()); } From e03b0563ee50d239202f813bc5c2cf68c09a016e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 18 Apr 2025 00:55:59 +0200 Subject: [PATCH 14/54] wip: single_line_statement_blocks --- crates/fmt-2/src/comments.rs | 1 + crates/fmt-2/src/state.rs | 47 +++++++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index 1a6258dce2236..38e4ec46a87a6 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -89,6 +89,7 @@ fn gather_comments(sf: &SourceFile) -> Vec { if let Some(mut idx) = token_text.find('\n') { code_to_the_left = false; + // NOTE(dani): this used to be `while`, but we want only a single blank line. if let Some(next_newline) = token_text[idx + 1..].find('\n') { idx += 1 + next_newline; let pos = pos + idx; diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 281d6367673a8..a2de29e2ddff5 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -51,7 +51,7 @@ pub hex_underscore: HexUnderscore, /// Style of single line blocks in statements pub single_line_statement_blocks: SingleLineBlockStyle, -- [ ] +- [x] /// Print space in state variable, function and modifier `override` attribute pub override_spacing: bool, @@ -332,6 +332,7 @@ struct FunctionLike<'a, 'b> { returns: &'a [ast::VariableDefinition<'b>], anonymous: bool, body: Option<&'a [ast::Stmt<'b>]>, + body_span: Span, } /// Language-specific pretty printing. @@ -570,7 +571,7 @@ impl State<'_> { } fn print_function(&mut self, func: &ast::ItemFunction<'_>) { - let ast::ItemFunction { kind, header, body, body_span: _ } = func; + let ast::ItemFunction { kind, header, body, body_span } = func; let ast::FunctionHeader { name, ref parameters, @@ -593,6 +594,7 @@ impl State<'_> { returns, anonymous: false, body: body.as_deref(), + body_span: *body_span, }); } @@ -609,6 +611,7 @@ impl State<'_> { returns, anonymous, body, + body_span, } = args; self.word(kind); if let Some(name) = name { @@ -647,7 +650,7 @@ impl State<'_> { } if let Some(body) = body { self.nbsp(); - self.print_block(body); + self.print_block(body, body_span); } else { self.word(";"); self.hardbreak(); @@ -1150,7 +1153,7 @@ impl State<'_> { ast::StmtKind::Try(stmt_try) => todo!(), ast::StmtKind::UncheckedBlock(block) => { self.word("unchecked "); - self.print_block(block); + self.print_block(block, stmt.span); } ast::StmtKind::While(expr, stmt) => todo!(), ast::StmtKind::Placeholder => self.word("_"), @@ -1160,19 +1163,41 @@ impl State<'_> { } } - fn print_block(&mut self, block: &[ast::Stmt<'_>]) { - self.s.cbox(self.ind); + fn print_block(&mut self, block: &[ast::Stmt<'_>], span: Span) { + // TODO: attempt_single_line, attempt_omit_braces self.word("{"); - self.hardbreak_if_nonempty(); - for stmt in block.iter() { - self.print_stmt(stmt); + if block.is_empty() { + self.maybe_print_comments(span.hi()); + } else if self.single_line_block(block, span) { + self.space(); + self.print_stmt(&block[0]); + self.maybe_print_comments(span.hi()); + self.space(); + } else { + self.s.cbox(self.ind); self.hardbreak(); + for stmt in block.iter() { + self.print_stmt(stmt); + self.hardbreak(); + } + self.s.offset(-self.ind); + self.end(); + self.maybe_print_comments(span.hi()); } - self.s.offset(-self.ind); - self.end(); self.word("}"); self.hardbreak(); } + + fn single_line_block(&self, block: &[ast::Stmt<'_>], span: Span) -> bool { + if block.len() != 1 { + return false; + } + match self.config.single_line_statement_blocks { + config::SingleLineBlockStyle::Preserve => self.sm.is_multiline(span), + config::SingleLineBlockStyle::Single => true, + config::SingleLineBlockStyle::Multi => false, + } + } } fn stmt_needs_semi(stmt: &ast::StmtKind<'_>) -> bool { From edcc467e7142ce68accb26f40a27c84ceb2401b0 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 18 Apr 2025 17:58:24 +0200 Subject: [PATCH 15/54] tweaks --- crates/fmt-2/src/state.rs | 42 +++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index a2de29e2ddff5..2404797f6f4a6 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -12,7 +12,9 @@ use solar_parse::{ }; use std::borrow::Cow; -// TODO(dani): bunch docs into `Comments` since misplaced docs get ignored +// TODO(dani): bunch docs into `Comments` since misplaced docs get ignore + +// TODO(dani): multiple string literals don't get handled correctly "a" "b" /* - [x] @@ -551,21 +553,15 @@ impl State<'_> { parameters, ..Default::default() }); - self.word(";"); - self.hardbreak(); } ast::ItemKind::Event(ast::ItemEvent { name, parameters, anonymous }) => { self.print_function_like(FunctionLike { kind: "event", name: Some(*name), parameters, + anonymous: *anonymous, ..Default::default() }); - if *anonymous { - self.word(" anonymous"); - } - self.word(";"); - self.hardbreak(); } } } @@ -784,13 +780,13 @@ impl State<'_> { match *kind { ast::LitKind::Str(kind, _) => { + // TODO(dani): kind.prefix().len() let prefix_len = match kind { ast::StrKind::Str => 0, ast::StrKind::Unicode => 7, ast::StrKind::Hex => 3, }; let quote_pos = span.lo() + prefix_len as u32; - let s = &s[prefix_len + 1..s.len() - 1]; self.print_str_lit(kind, quote_pos, s); return; } @@ -892,6 +888,7 @@ impl State<'_> { /// `s` should be the *unescaped contents of the string literal*. fn print_str_lit(&mut self, kind: ast::StrKind, quote_pos: BytePos, s: &str) { + self.maybe_print_comments(quote_pos); let s = self.str_lit_to_string(kind, quote_pos, s); self.word(s); } @@ -908,6 +905,7 @@ impl State<'_> { config::QuoteStyle::Single => '\'', config::QuoteStyle::Preserve => self.char_at(quote_pos), }; + debug_assert!(matches!(quote, '\"' | '\''), "{quote:?}"); let s = solar_parse::interface::data_structures::fmt::from_fn(move |f| { if matches!(kind, ast::StrKind::Hex) { match self.config.hex_underscore { @@ -1018,27 +1016,23 @@ impl State<'_> { #[expect(unused_variables)] fn print_expr(&mut self, expr: &ast::Expr<'_>) { - let ast::Expr { span, kind } = expr; - if self.handle_span(*span) { + let ast::Expr { span, ref kind } = *expr; + if self.handle_span(span) { return; } match kind { ast::ExprKind::Array(exprs) => { - if exprs.is_empty() { - self.word("[]"); - } else { - self.word("["); - self.s.cbox(self.ind); - self.zerobreak(); - for (pos, elem) in exprs.iter().delimited() { - self.print_expr(elem); - self.trailing_comma(pos.is_last); - } - self.s.offset(-self.ind); - self.end(); - self.word("]"); + self.word("["); + self.s.cbox(self.ind); + self.zerobreak(); + for (pos, elem) in exprs.iter().delimited() { + self.print_expr(elem); + self.trailing_comma(pos.is_last); } + self.s.offset(-self.ind); + self.end(); + self.word("]"); } ast::ExprKind::Assign(expr, bin_op, expr1) => todo!(), ast::ExprKind::Binary(expr, bin_op, expr1) => todo!(), From 2878e87ffe90525792252d1fa4295bd6593a9fa3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:12:16 +0200 Subject: [PATCH 16/54] chore: bump solar to latest main --- Cargo.lock | 279 ++++++++++++++----------- Cargo.toml | 3 + crates/common/src/preprocessor/deps.rs | 4 +- crates/fmt-2/src/state.rs | 59 +++--- 4 files changed, 201 insertions(+), 144 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 393aaf40cd363..76f5b675fefcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,7 +78,7 @@ version = "0.1.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28e2652684758b0d9b389d248b209ed9fd9989ef489a550265fe4bb8454fe7eb" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "num_enum", "serde", "strum 0.27.1", @@ -91,7 +91,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fbf458101ed6c389e9bb70a34ebc56039868ad10472540614816cdedc8f5265" dependencies = [ "alloy-eips", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "alloy-serde", "alloy-trie", @@ -115,7 +115,7 @@ checksum = "fc982af629e511292310fe85b433427fd38cb3105147632b574abc997db44c91" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "alloy-serde", "serde", @@ -129,10 +129,10 @@ checksum = "cd0a0c1ddee20ecc14308aae21c2438c994df7b39010c26d70f86e1d8fdb8db0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-network", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-pubsub", "alloy-rpc-types-eth", @@ -149,9 +149,9 @@ version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb8e762aefd39a397ff485bc86df673465c4ad3ec8819cc60833a8a3ba5cdc87" dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", + "alloy-sol-type-parser 0.8.25", "alloy-sol-types", "arbitrary", "const-hex", @@ -170,7 +170,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "675264c957689f0fd75f5993a73123c2cc3b5c235a38f5b9037fe6c826bfb2c0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "crc", "serde", @@ -183,7 +183,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "arbitrary", "rand 0.8.5", @@ -196,7 +196,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b15b13d38b366d01e818fe8e710d4d702ef7499eacd44926a06171dd9585d0c" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "arbitrary", "k256", @@ -214,7 +214,7 @@ dependencies = [ "alloy-eip2124", "alloy-eip2930", "alloy-eip7702", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "alloy-serde", "auto_impl", @@ -233,7 +233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a40de6f5b53ecf5fd7756072942f41335426d9a3704cd961f77d854739933bcf" dependencies = [ "alloy-eips", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-serde", "alloy-trie", "serde", @@ -245,8 +245,20 @@ version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe6beff64ad0aa6ad1019a3db26fef565aefeb011736150ab73ed3366c3cfd1b" dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", + "alloy-primitives 0.8.25", + "alloy-sol-type-parser 0.8.25", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-abi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5189fa9a8797e92396bc4b4454c5f2073a4945f7c2b366af9af60f9536558f7a" +dependencies = [ + "alloy-primitives 1.0.0", + "alloy-sol-type-parser 1.0.0", "serde", "serde_json", ] @@ -257,7 +269,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27434beae2514d4a2aa90f53832cbdf6f23e4b5e2656d95eaf15f9276e2418b6" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-sol-types", "serde", "serde_json", @@ -276,7 +288,7 @@ dependencies = [ "alloy-eips", "alloy-json-rpc", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types-any", "alloy-rpc-types-eth", "alloy-serde", @@ -299,7 +311,7 @@ checksum = "db973a7a23cbe96f2958e5687c51ce2d304b5c6d0dc5ccb3de8667ad8476f50b" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-serde", "serde", ] @@ -335,6 +347,33 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "alloy-primitives" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b98b99c1dcfbe74d7f0b31433ff215e7d1555e367d90e62db904f3c9d4ff53" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "foldhash", + "hashbrown 0.15.2", + "indexmap 2.9.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.0", + "ruint", + "rustc-hash 2.1.1", + "serde", + "sha3", + "tiny-keccak", +] + [[package]] name = "alloy-provider" version = "0.12.6" @@ -347,7 +386,7 @@ dependencies = [ "alloy-json-rpc", "alloy-network", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types-debug", @@ -385,7 +424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "721aca709a9231815ad5903a2d284042cc77e7d9d382696451b30c9ee0950001" dependencies = [ "alloy-json-rpc", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-transport", "bimap", "futures", @@ -426,7 +465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "445a3298c14fae7afb5b9f2f735dead989f3dd83020c2ab8e48ed95d7b6d1acb" dependencies = [ "alloy-json-rpc", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-pubsub", "alloy-transport", "alloy-transport-http", @@ -453,7 +492,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9157deaec6ba2ad7854f16146e4cd60280e76593eed79fdcb06e0fa8b6c60f77" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types-anvil", "alloy-rpc-types-engine", "alloy-rpc-types-eth", @@ -469,7 +508,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a80ee83ef97e7ffd667a81ebdb6154558dfd5e8f20d8249a10a12a1671a04b3" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types-eth", "alloy-serde", "serde", @@ -492,7 +531,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08b113a0087d226291b9768ed331818fa0b0744cc1207ae7c150687cf3fde1bd" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "serde", ] @@ -504,7 +543,7 @@ checksum = "874ac9d1249ece0453e262d9ba72da9dbb3b7a2866220ded5940c2e47f1aa04d" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "alloy-serde", "derive_more 2.0.1", @@ -524,7 +563,7 @@ dependencies = [ "alloy-consensus-any", "alloy-eips", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "alloy-serde", "alloy-sol-types", @@ -540,7 +579,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4747763aee39c1b0f5face79bde9be8932be05b2db7d8bdcebb93490f32c889c" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types-eth", "alloy-serde", "serde", @@ -554,7 +593,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70132ebdbea1eaa68c4d6f7a62c2fadf0bdce83b904f895ab90ca4ec96f63468" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types-eth", "alloy-serde", "serde", @@ -566,7 +605,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a1cd73fc054de6353c7f22ff9b846b0f0f145cd0112da07d4119e41e9959207" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "serde", "serde_json", ] @@ -578,7 +617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c96fbde54bee943cd94ebacc8a62c50b38c7dfd2552dcd79ff61aea778b1bfcc" dependencies = [ "alloy-dyn-abi", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-sol-types", "async-trait", "auto_impl", @@ -596,7 +635,7 @@ checksum = "4e73835ed6689740b76cab0f59afbdce374a03d3f856ea33ba1fc054630a1b28" dependencies = [ "alloy-consensus", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-signer", "async-trait", "aws-sdk-kms", @@ -614,7 +653,7 @@ checksum = "a16b468ae86bb876d9c7a3b49b1e8d614a581a1a9673e4e0d2393b411080fe64" dependencies = [ "alloy-consensus", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-signer", "async-trait", "gcloud-sdk", @@ -633,7 +672,7 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-signer", "alloy-sol-types", "async-trait", @@ -652,7 +691,7 @@ checksum = "cc6e72002cc1801d8b41e9892165e3a6551b7bd382bd9d0414b21e90c0c62551" dependencies = [ "alloy-consensus", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-signer", "async-trait", "coins-bip32", @@ -671,7 +710,7 @@ checksum = "1d4fd403c53cf7924c3e16c61955742cfc3813188f0975622f4fa6f8a01760aa" dependencies = [ "alloy-consensus", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-signer", "async-trait", "semver 1.0.26", @@ -700,7 +739,7 @@ version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" dependencies = [ - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-sol-macro-input", "const-hex", "heck", @@ -719,7 +758,7 @@ version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" dependencies = [ - "alloy-json-abi", + "alloy-json-abi 0.8.25", "const-hex", "dunce", "heck", @@ -741,14 +780,24 @@ dependencies = [ "winnow 0.7.4", ] +[[package]] +name = "alloy-sol-type-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b5f5f9f561c29f78ea521ebe2e5ac1633f1b1442dae582f68ecd57c6350042" +dependencies = [ + "serde", + "winnow 0.7.4", +] + [[package]] name = "alloy-sol-types" version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" dependencies = [ - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "alloy-sol-macro", "const-hex", "serde", @@ -835,7 +884,7 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d95a94854e420f07e962f7807485856cde359ab99ab6413883e15235ad996e8b" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "arrayvec", "derive_more 1.0.0", @@ -967,7 +1016,7 @@ dependencies = [ "alloy-eips", "alloy-genesis", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-pubsub", "alloy-rlp", @@ -1024,7 +1073,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-eips", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "alloy-rpc-types", "alloy-serde", @@ -2104,10 +2153,10 @@ dependencies = [ "alloy-consensus", "alloy-contract", "alloy-dyn-abi", - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-json-rpc", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rlp", "alloy-rpc-types", @@ -2196,8 +2245,8 @@ name = "chisel" version = "1.1.0" dependencies = [ "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "clap", "dirs", "eyre", @@ -3296,7 +3345,7 @@ checksum = "78329cbf3c326a3ce2694003976c019fe5f407682b1fdc76e89e463826ea511a" dependencies = [ "ahash", "alloy-dyn-abi", - "alloy-primitives", + "alloy-primitives 0.8.25", "indexmap 2.9.0", ] @@ -3452,9 +3501,9 @@ version = "1.1.0" dependencies = [ "alloy-chains", "alloy-dyn-abi", - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rpc-types", "alloy-serde", @@ -3532,7 +3581,7 @@ dependencies = [ name = "forge-doc" version = "1.1.0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "derive_more 2.0.1", "eyre", "forge-fmt", @@ -3555,7 +3604,7 @@ dependencies = [ name = "forge-fmt" version = "1.1.0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "ariadne", "foundry-config", "itertools 0.14.0", @@ -3589,9 +3638,9 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eips", - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rpc-types", "alloy-serde", @@ -3630,7 +3679,7 @@ name = "forge-script-sequence" version = "1.1.0" dependencies = [ "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "eyre", "foundry-common", "foundry-compilers", @@ -3661,8 +3710,8 @@ name = "forge-verify" version = "1.1.0" dependencies = [ "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rpc-types", "async-trait", @@ -3706,8 +3755,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f13025e2bf7b3975b34190a7e0fb66b6a5df61e29e54d1da093938073e06ad10" dependencies = [ "alloy-chains", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "foundry-compilers", "reqwest", "semver 1.0.26", @@ -3724,9 +3773,9 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-genesis", - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rlp", "alloy-rpc-types", @@ -3782,8 +3831,8 @@ dependencies = [ "alloy-chains", "alloy-dyn-abi", "alloy-eips", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rlp", "clap", @@ -3822,10 +3871,10 @@ dependencies = [ "alloy-contract", "alloy-dyn-abi", "alloy-eips", - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-json-rpc", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-pubsub", "alloy-rpc-client", @@ -3876,7 +3925,7 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types", "alloy-serde", "chrono", @@ -3895,8 +3944,8 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cc93d4e235ddde616d9fc532bac2cf9840203c3432e24fa48d827a4eb51b35c" dependencies = [ - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "auto_impl", "derive_more 1.0.0", "dirs", @@ -3943,8 +3992,8 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8217869394db052366fc6ed03475929e5c844222885c76d5dfcbb95fecf2640b" dependencies = [ - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "foundry-compilers-core", "futures-util", "md-5", @@ -3967,8 +4016,8 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750e309e1ec20a4fd73617bdbf25529688ff75ae54c79b17b6428e2ce54ca6e" dependencies = [ - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "foundry-compilers-artifacts-solc", "foundry-compilers-core", "path-slash", @@ -3982,7 +4031,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a0f2673de9b861d8d7f21a48182f3cb724354e17be084655b5b84d4446f41fc" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "cfg-if", "dunce", "fs_extra", @@ -4004,7 +4053,7 @@ version = "1.1.0" dependencies = [ "Inflector", "alloy-chains", - "alloy-primitives", + "alloy-primitives 0.8.25", "dirs", "dunce", "eyre", @@ -4039,7 +4088,7 @@ dependencies = [ name = "foundry-debugger" version = "1.1.0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "crossterm", "eyre", "foundry-common", @@ -4058,8 +4107,8 @@ name = "foundry-evm" version = "1.1.0" dependencies = [ "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "alloy-sol-types", "eyre", "foundry-cheatcodes", @@ -4084,7 +4133,7 @@ dependencies = [ name = "foundry-evm-abi" version = "1.1.0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-sol-types", "derive_more 2.0.1", "foundry-common-fmt", @@ -4099,9 +4148,9 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-genesis", - "alloy-json-abi", + "alloy-json-abi 0.8.25", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rpc-types", "alloy-sol-types", @@ -4130,7 +4179,7 @@ dependencies = [ name = "foundry-evm-coverage" version = "1.1.0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "eyre", "foundry-common", "foundry-compilers", @@ -4146,8 +4195,8 @@ name = "foundry-evm-fuzz" version = "1.1.0" dependencies = [ "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "eyre", "foundry-common", "foundry-compilers", @@ -4170,8 +4219,8 @@ name = "foundry-evm-traces" version = "1.1.0" dependencies = [ "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 0.8.25", + "alloy-primitives 0.8.25", "alloy-sol-types", "eyre", "foundry-block-explorers", @@ -4200,7 +4249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba7beb856e73f59015823eb221a98b7c22b58bc4e7066c9c86774ebe74e61dd6" dependencies = [ "alloy-consensus", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "alloy-rpc-types", "eyre", @@ -4219,7 +4268,7 @@ dependencies = [ name = "foundry-linking" version = "1.1.0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "foundry-compilers", "semver 1.0.26", "thiserror 2.0.12", @@ -4239,7 +4288,7 @@ dependencies = [ name = "foundry-test-utils" version = "1.1.0" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-provider", "eyre", "fd-lock", @@ -4265,7 +4314,7 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-network", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-signer", "alloy-signer-aws", "alloy-signer-gcp", @@ -5748,7 +5797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -6401,7 +6450,7 @@ checksum = "889facbf449b2d9c8de591cd467a6c7217936f3c1c07a281759c01c49d08d66d" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rlp", "alloy-serde", "derive_more 2.0.1", @@ -6418,7 +6467,7 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types-eth", "alloy-serde", "derive_more 2.0.1", @@ -7251,6 +7300,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", + "serde", "zerocopy 0.8.24", ] @@ -7290,6 +7340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.2", + "serde", ] [[package]] @@ -7504,7 +7555,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6a43423d81f4bef634469bfb2d9ebe36a9ea9167f20ab3a7d1ff1e05fa63099" dependencies = [ - "alloy-primitives", + "alloy-primitives 0.8.25", "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-sol-types", @@ -7554,7 +7605,7 @@ checksum = "f0f987564210317706def498421dfba2ae1af64a8edce82c6102758b48133fcb" dependencies = [ "alloy-eip2930", "alloy-eip7702", - "alloy-primitives", + "alloy-primitives 0.8.25", "auto_impl", "bitflags 2.9.0", "bitvec", @@ -8460,12 +8511,10 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0a583a12e73099d1f54bfe7c8a30d7af5ff3591c61ee51cce91045ee5496d86" +source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.0.0", "bumpalo", - "derive_more 2.0.1", "either", "num-bigint", "num-rational", @@ -8480,8 +8529,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12642e7e8490d6855a345b5b9d5e55630bd30f54450a909e28f1385b448baada" +source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" dependencies = [ "strum 0.27.1", ] @@ -8489,8 +8537,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae8902cc28af53e2ba97c450aff7c59d112a433f9ef98fae808e02e25e6dee6" +source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" dependencies = [ "bumpalo", "index_vec", @@ -8504,8 +8551,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded5ec7a5cee351c7a428842d273470180cab259c46f52d502ec3ab5484d0c3a" +source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" dependencies = [ "annotate-snippets", "anstream", @@ -8514,7 +8560,7 @@ dependencies = [ "derive_builder", "derive_more 2.0.1", "dunce", - "itertools 0.14.0", + "itertools 0.12.1", "itoa", "lasso", "match_cfg", @@ -8525,7 +8571,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 2.0.12", + "thiserror 1.0.69", "tracing", "unicode-width 0.2.0", ] @@ -8533,8 +8579,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2c9ff6e00eeeff12eac9d589f1f20413d3b71b9c0c292d1eefbd34787e0836" +source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" dependencies = [ "proc-macro2", "quote", @@ -8544,13 +8589,12 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1bc1d0253b0f7f2c7cd25ed7bc5d5e8cac43e717d002398250e0e66e43278b" +source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.0.0", "bitflags 2.9.0", "bumpalo", - "itertools 0.14.0", + "itertools 0.12.1", "memchr", "num-bigint", "num-rational", @@ -8565,11 +8609,10 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded4b26fb85a0ae2f3277377236af0884c82f38965a2c51046a53016c8b5f332" +source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" dependencies = [ - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 1.0.0", + "alloy-primitives 1.0.0", "bitflags 2.9.0", "bumpalo", "derive_more 2.0.1", @@ -8871,7 +8914,7 @@ dependencies = [ "serde_json", "sha2", "tempfile", - "thiserror 2.0.12", + "thiserror 1.0.69", "url", "zip", ] @@ -9575,7 +9618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f" dependencies = [ "cc", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -10193,7 +10236,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c2fa3570a611e..24b6cd7aaa292 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -314,6 +314,9 @@ yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } path-slash = "0.2" [patch.crates-io] +solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } +solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } + ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } # alloy-json-abi = { path = "../../alloy-rs/core/crates/json-abi" } diff --git a/crates/common/src/preprocessor/deps.rs b/crates/common/src/preprocessor/deps.rs index 2af9d76ba61af..f48e6c57567d4 100644 --- a/crates/common/src/preprocessor/deps.rs +++ b/crates/common/src/preprocessor/deps.rs @@ -191,10 +191,10 @@ impl<'hir> Visit<'hir> for BytecodeDependencyCollector<'hir> { // Calculate offset to remove named args, e.g. for an expression like // `new Counter {value: 333} ( address(this))` - // the offset will be used to replace `{value: 333} ( ` with `(` + // the offset will be used to replace `{value: 333} (` with `(` let call_args_offset = if named_args.is_some() && !call_args.is_empty() { - (call_args.span().lo() - ty_new.span.hi()).to_usize() + (call_args.span.lo() - ty_new.span.hi()).to_usize() } else { 0 }; diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 2404797f6f4a6..65d5bde51282f 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -14,8 +14,6 @@ use std::borrow::Cow; // TODO(dani): bunch docs into `Comments` since misplaced docs get ignore -// TODO(dani): multiple string literals don't get handled correctly "a" "b" - /* - [x] /// Maximum line length where formatter will try to wrap the line @@ -318,6 +316,13 @@ impl State<'_> { Ok(s) => self.word(s), Err(e) => panic!("failed to print {span:?}: {e:#?}"), } + // Drop comments that are included in the span. + while let Some(cmnt) = self.peek_comment() { + if cmnt.pos() >= span.hi() { + break; + } + let _ = self.next_comment().unwrap(); + } } } @@ -631,7 +636,7 @@ impl State<'_> { self.nbsp(); self.print_override(override_); } - for modifier in modifiers.iter() { + for modifier in modifiers { self.nbsp(); self.print_modifier_call(modifier); } @@ -776,27 +781,32 @@ impl State<'_> { if self.handle_span(span) { return; } - let s = symbol.as_str(); match *kind { - ast::LitKind::Str(kind, _) => { - // TODO(dani): kind.prefix().len() - let prefix_len = match kind { - ast::StrKind::Str => 0, - ast::StrKind::Unicode => 7, - ast::StrKind::Hex => 3, - }; - let quote_pos = span.lo() + prefix_len as u32; - self.print_str_lit(kind, quote_pos, s); + ast::LitKind::Str(kind, ..) => { + self.s.cbox(self.ind); + for (pos, (span, symbol)) in lit.literals().delimited() { + if !self.handle_span(span) { + let quote_pos = span.lo() + kind.prefix().len() as u32; + self.print_str_lit(kind, quote_pos, symbol.as_str()); + } + if !pos.is_last { + self.space(); + } else { + self.neverbreak(); + } + } + self.s.offset(-self.ind); + self.end(); return; } ast::LitKind::Number(_) | ast::LitKind::Rational(_) => { - self.print_num_literal(s); + self.print_num_literal(symbol.as_str()); return; } _ => {} }; - self.word(s.to_string()); + self.word(symbol.to_string()); } fn print_num_literal(&mut self, source: &str) { @@ -895,11 +905,7 @@ impl State<'_> { /// `s` should be the *unescaped contents of the string literal*. fn str_lit_to_string(&self, kind: ast::StrKind, quote_pos: BytePos, s: &str) -> String { - let prefix = match kind { - ast::StrKind::Str => "", - ast::StrKind::Unicode => "unicode", - ast::StrKind::Hex => "hex", - }; + let prefix = kind.prefix(); let quote = match self.config.quote_style { config::QuoteStyle::Double => '\"', config::QuoteStyle::Single => '\'', @@ -1079,10 +1085,15 @@ impl State<'_> { } fn print_call_args(&mut self, args: &ast::CallArgs<'_>) { + let ast::CallArgs { span, kind } = args; + if self.handle_span(*span) { + return; + } + self.s.cbox(self.ind); self.word("("); - match args { - ast::CallArgs::Unnamed(exprs) => { + match kind { + ast::CallArgsKind::Unnamed(exprs) => { self.zerobreak(); for (pos, expr) in exprs.iter().delimited() { self.print_expr(expr); @@ -1093,7 +1104,7 @@ impl State<'_> { } self.zerobreak(); } - ast::CallArgs::Named(named_args) => { + ast::CallArgsKind::Named(named_args) => { self.word("{"); self.braces_break(); for (pos, ast::NamedArg { name, value }) in named_args.iter().delimited() { @@ -1170,7 +1181,7 @@ impl State<'_> { } else { self.s.cbox(self.ind); self.hardbreak(); - for stmt in block.iter() { + for stmt in block { self.print_stmt(stmt); self.hardbreak(); } From ffe3b65571eb7d7a8152336c6d71ae1ba7920034 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:44:20 +0200 Subject: [PATCH 17/54] fix: test dir --- crates/fmt-2/tests/formatter.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index a552a66377b8b..6adc86fda4f62 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -7,9 +7,11 @@ use std::{ use tracing_subscriber::{EnvFilter, FmtSubscriber}; #[track_caller] -fn format(source: &str, config: FormatterConfig) -> String { - let _ = (source, config); - todo!(); +fn format(source: &str, path: &Path, config: FormatterConfig) -> String { + match forge_fmt_2::format_source(source, Some(path), config) { + Ok(formatted) => formatted, + Err(e) => panic!("failed to format {path:?}: {e}"), + } } #[track_caller] @@ -41,7 +43,8 @@ fn enable_tracing() { } fn tests_dir() -> PathBuf { - Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata") + // Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata") + Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().join("fmt/testdata") } fn test_directory(base_name: &str) { @@ -91,14 +94,20 @@ fn test_directory(base_name: &str) { .try_into() .unwrap_or_else(|err| panic!("invalid test config for {filename}: {err}")); - test_formatter(filename, config, &original, &lines.join("\n")); + test_formatter(&path, filename, config, &original, &lines.join("\n")); } } -fn test_formatter(filename: &str, config: FormatterConfig, source: &str, expected_source: &str) { +fn test_formatter( + path: &Path, + filename: &str, + config: FormatterConfig, + source: &str, + expected_source: &str, +) { assert_eof(expected_source); - let source_formatted = format(source, config.clone()); + let source_formatted = format(source, &path.with_file_name("original.sol"), config.clone()); assert_eof(&source_formatted); similar_asserts::assert_eq!( PrettyString(&source_formatted), @@ -106,7 +115,7 @@ fn test_formatter(filename: &str, config: FormatterConfig, source: &str, expecte "{filename}: formatted source does not match expected source" ); - let expected_formatted = format(expected_source, config); + let expected_formatted = format(expected_source, path, config); similar_asserts::assert_eq!( PrettyString(&expected_formatted), PrettyString(expected_source), From 9762203d18e26eeb93c71a9735b30414290cdf39 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 22 Apr 2025 21:32:01 +0200 Subject: [PATCH 18/54] bump --- Cargo.lock | 51 ++++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da9c78a8d69e3..cbfd72f1cd978 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1916,7 +1916,7 @@ dependencies = [ "bitflags 2.9.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.11.0", "lazy_static", "lazycell", "log", @@ -3286,7 +3286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3405,7 +3405,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5520,7 +5520,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.0", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5547,15 +5547,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -5592,7 +5583,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7232,7 +7223,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7774,7 +7765,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7787,7 +7778,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8496,7 +8487,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" dependencies = [ "alloy-primitives 1.0.0", "bumpalo", @@ -8514,7 +8505,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" dependencies = [ "strum 0.27.1", ] @@ -8522,7 +8513,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" dependencies = [ "bumpalo", "index_vec", @@ -8536,7 +8527,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" dependencies = [ "annotate-snippets", "anstream", @@ -8545,18 +8536,17 @@ dependencies = [ "derive_builder", "derive_more 2.0.1", "dunce", - "itertools 0.12.1", + "itertools 0.11.0", "itoa", "lasso", "match_cfg", "normalize-path", "rayon", - "scc", "scoped-tls", "solar-config", "solar-data-structures", "solar-macros", - "thiserror 1.0.69", + "thiserror 2.0.12", "tracing", "unicode-width 0.2.0", ] @@ -8564,7 +8554,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" dependencies = [ "proc-macro2", "quote", @@ -8574,12 +8564,12 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" dependencies = [ "alloy-primitives 1.0.0", "bitflags 2.9.0", "bumpalo", - "itertools 0.12.1", + "itertools 0.11.0", "memchr", "num-bigint", "num-rational", @@ -8594,7 +8584,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#fd5c9977261254beac3b8e8dd8cea78a2ec40e8d" +source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" dependencies = [ "alloy-json-abi 1.0.0", "alloy-primitives 1.0.0", @@ -8605,7 +8595,6 @@ dependencies = [ "once_map", "paste", "rayon", - "scc", "serde", "serde_json", "solar-ast", @@ -8899,7 +8888,7 @@ dependencies = [ "serde_json", "sha2", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.12", "url", "zip", ] @@ -9008,7 +8997,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] From ff3b76bca894e284315d0511f3fa69ce73be445f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:13:05 +0200 Subject: [PATCH 19/54] fix docs --- crates/fmt-2/src/inline_config.rs | 8 +------- crates/fmt-2/src/lib.rs | 10 +++++++--- crates/fmt-2/src/pp/mod.rs | 18 ++++++++++-------- crates/fmt-2/tests/formatter.rs | 3 ++- env.sh | 2 +- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/crates/fmt-2/src/inline_config.rs b/crates/fmt-2/src/inline_config.rs index 7aa8acd2ead8d..b61877cd9a483 100644 --- a/crates/fmt-2/src/inline_config.rs +++ b/crates/fmt-2/src/inline_config.rs @@ -56,13 +56,7 @@ impl DisabledRange { } } -/// An inline config. Keeps track of disabled ranges. -/// -/// This is a list of Inline Config items for locations in a source file. This is -/// usually acquired by parsing the comments for an `forgefmt:` items. -/// -/// See [`Comments::parse_inline_config_items`](crate::Comments::parse_inline_config_items) for -/// details. +/// An inline config. Keeps track of ranges which should not be formatted. #[derive(Debug, Default)] pub struct InlineConfig { disabled_ranges: Vec, diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 4246a8180e490..0c44560ac56cb 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -64,9 +64,13 @@ pub fn format_source(source: &str, path: Option<&Path>, config: FormatterConfig) state.print_source_unit(&ast); Ok(state.s.eof()) }); - // TODO(dani): add a non-fatal error that returns the formatted source with the errors - sess.emitted_errors().unwrap()?; - Ok(res.unwrap()) + match (res, sess.emitted_errors().unwrap()) { + (Ok(s), Ok(())) => Ok(s), + // TODO(dani): add a non-fatal error that returns the formatted source with the errors + (Ok(_s), Err(err)) => Err(err.into()), + (Err(_), Ok(_)) => unreachable!(), + (Err(_), Err(err)) => Err(err.into()), + } } fn parse_inline_config(sess: &Session, comments: &Comments, src: &str) -> InlineConfig { diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index e418ad32e7b46..4a8f1df24820c 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -24,16 +24,18 @@ pub enum Breaks { #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum IndentStyle { /// Vertically aligned under whatever column this block begins at. - /// - /// fn demo(arg1: usize, - /// arg2: usize) {} + /// ```ignore + /// fn demo(arg1: usize, + /// arg2: usize) {} + /// ``` Visual, /// Indented relative to the indentation level of the previous line. - /// - /// fn demo( - /// arg1: usize, - /// arg2: usize, - /// ) {} + /// ```ignore + /// fn demo( + /// arg1: usize, + /// arg2: usize, + /// ) {} + /// ``` Block { offset: isize }, } diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index 6adc86fda4f62..09e599a08dec2 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -16,7 +16,8 @@ fn format(source: &str, path: &Path, config: FormatterConfig) -> String { #[track_caller] fn assert_eof(content: &str) { - assert!(content.ends_with('\n') && !content.ends_with("\n\n")); + assert!(content.ends_with('\n'), "missing trailing newline"); + assert!(!content.ends_with("\n\n"), "extra trailing newline"); } #[derive(Eq)] diff --git a/env.sh b/env.sh index c85331c12f8a3..4c301e62e7a10 100644 --- a/env.sh +++ b/env.sh @@ -1,6 +1,6 @@ alias forge-fmt="cargo r --quiet -p forge-fmt-2 --" forge-fmt-cmp() { - cargo b --quiet -p forge-fmt-2 || exit 1 + cargo b --quiet -p forge-fmt-2 || return 1 forge_fmt_new="$(pwd)/target/debug/forge-fmt-2" tmp="$(mktemp -d)" From e4e069e81425c07e95da6159b61e85bdb4c39862 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:49:30 +0200 Subject: [PATCH 20/54] fix: adjust '()' for modifier calls --- crates/fmt-2/src/state.rs | 16 ++++++++++++---- crates/fmt-2/tests/formatter.rs | 2 ++ .../testdata/ConstructorModifierStyle/fmt.sol | 4 +++- .../ConstructorModifierStyle/original.sol | 4 +++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 65d5bde51282f..8813c037ab47c 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -469,7 +469,7 @@ impl State<'_> { self.word("is"); self.space(); for (pos, base) in bases.iter().delimited() { - self.print_modifier_call(base); + self.print_modifier_call(base, false); if !pos.is_last { self.word(","); self.space(); @@ -638,7 +638,14 @@ impl State<'_> { } for modifier in modifiers { self.nbsp(); - self.print_modifier_call(modifier); + + // Add `()` in constructors when the modifier is a base contract. + // HACK: we can't really know for sure, so we assume that uppercase names are bases. + // LEGACY: we are checking the beginning of the path, not the last segment. + let is_base_contract = args.kind == "constructor" && + modifier.name.first().name.as_str().starts_with(char::is_uppercase); + + self.print_modifier_call(modifier, is_base_contract); } if !returns.is_empty() { self.nbsp(); @@ -1076,10 +1083,11 @@ impl State<'_> { } } - fn print_modifier_call(&mut self, modifier: &ast::Modifier<'_>) { + // If `add_parens_if_empty` is true, then add parentheses `()` even if there are no arguments. + fn print_modifier_call(&mut self, modifier: &ast::Modifier<'_>, add_parens_if_empty: bool) { let ast::Modifier { name, arguments } = modifier; self.print_path(name); - if !arguments.is_empty() { + if !arguments.is_empty() || add_parens_if_empty { self.print_call_args(arguments); } } diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index 09e599a08dec2..8ef678708dfa7 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -6,6 +6,8 @@ use std::{ }; use tracing_subscriber::{EnvFilter, FmtSubscriber}; +// TODO(dani): add snapshot infra to automatically update the expected output + #[track_caller] fn format(source: &str, path: &Path, config: FormatterConfig) -> String { match forge_fmt_2::format_source(source, Some(path), config) { diff --git a/crates/fmt/testdata/ConstructorModifierStyle/fmt.sol b/crates/fmt/testdata/ConstructorModifierStyle/fmt.sol index 88694860aded2..be61b28170c73 100644 --- a/crates/fmt/testdata/ConstructorModifierStyle/fmt.sol +++ b/crates/fmt/testdata/ConstructorModifierStyle/fmt.sol @@ -9,5 +9,7 @@ import {IAchievements} from "./interfaces/IAchievements.sol"; import {SoulBound1155} from "./abstracts/SoulBound1155.sol"; contract Achievements is IAchievements, SoulBound1155, Ownable { - constructor(address owner) Ownable() ERC1155() {} + constructor(address owner) my_modifier Ownable() ERC1155() {} + + function f() my_modifier MyModifier my_modifier MyModifier {} } diff --git a/crates/fmt/testdata/ConstructorModifierStyle/original.sol b/crates/fmt/testdata/ConstructorModifierStyle/original.sol index 88694860aded2..eb418ba126178 100644 --- a/crates/fmt/testdata/ConstructorModifierStyle/original.sol +++ b/crates/fmt/testdata/ConstructorModifierStyle/original.sol @@ -9,5 +9,7 @@ import {IAchievements} from "./interfaces/IAchievements.sol"; import {SoulBound1155} from "./abstracts/SoulBound1155.sol"; contract Achievements is IAchievements, SoulBound1155, Ownable { - constructor(address owner) Ownable() ERC1155() {} + constructor(address owner) my_modifier Ownable() ERC1155() {} + + function f() my_modifier MyModifier() my_modifier() MyModifier {} } From 3e0ece82bf27bc803329687038c23912e99e95a3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:59:02 +0200 Subject: [PATCH 21/54] test: typed yul does not exist anymore https://github.com/ethereum/solidity/pull/15329 --- crates/fmt/testdata/Yul/fmt.sol | 4 ++-- crates/fmt/testdata/Yul/original.sol | 4 ++-- crates/fmt/testdata/YulStrings/fmt.sol | 8 ++------ crates/fmt/testdata/YulStrings/original.sol | 8 ++------ crates/fmt/testdata/YulStrings/preserve-quote.fmt.sol | 8 ++------ crates/fmt/testdata/YulStrings/single-quote.fmt.sol | 8 ++------ 6 files changed, 12 insertions(+), 28 deletions(-) diff --git a/crates/fmt/testdata/Yul/fmt.sol b/crates/fmt/testdata/Yul/fmt.sol index 2f37eb2f290fe..79ebdf1891032 100644 --- a/crates/fmt/testdata/Yul/fmt.sol +++ b/crates/fmt/testdata/Yul/fmt.sol @@ -172,8 +172,8 @@ contract Yul { v7 {} - let zero:u32 := 0:u32 - let v:u256, t:u32 := sample(1, 2) + let zero := 0 + let v, t := sample(1, 2) let x, y := sample2(2, 1) let val1, val2, val3, val4, val5, val6, val7 diff --git a/crates/fmt/testdata/Yul/original.sol b/crates/fmt/testdata/Yul/original.sol index 5bd47c8dd9796..26e6ab868062f 100644 --- a/crates/fmt/testdata/Yul/original.sol +++ b/crates/fmt/testdata/Yul/original.sol @@ -128,8 +128,8 @@ contract Yul { function functionThatReturnsSevenValuesAndCanBeUsedInAssignment() -> v1, v2, v3, v4, v5, v6, v7 {} - let zero:u32 := 0:u32 - let v:u256, t:u32 := sample(1, 2) + let zero := 0 + let v, t := sample(1, 2) let x, y := sample2(2, 1) let val1, val2, val3, val4, val5, val6, val7 diff --git a/crates/fmt/testdata/YulStrings/fmt.sol b/crates/fmt/testdata/YulStrings/fmt.sol index d05caeb26692a..33d20c9fbc269 100644 --- a/crates/fmt/testdata/YulStrings/fmt.sol +++ b/crates/fmt/testdata/YulStrings/fmt.sol @@ -3,12 +3,8 @@ contract Yul { assembly { let a := "abc" let b := "abc" - let c := "abc":u32 - let d := "abc":u32 - let e := hex"deadbeef" - let f := hex"deadbeef" - let g := hex"deadbeef":u32 - let h := hex"deadbeef":u32 + let c := hex"deadbeef" + let d := hex"deadbeef" datacopy(0, dataoffset("runtime"), datasize("runtime")) return(0, datasize("runtime")) } diff --git a/crates/fmt/testdata/YulStrings/original.sol b/crates/fmt/testdata/YulStrings/original.sol index fb3d5d20f4b76..f35c18011ebda 100644 --- a/crates/fmt/testdata/YulStrings/original.sol +++ b/crates/fmt/testdata/YulStrings/original.sol @@ -3,12 +3,8 @@ contract Yul { assembly { let a := "abc" let b := 'abc' - let c := "abc":u32 - let d := 'abc':u32 - let e := hex"deadbeef" - let f := hex'deadbeef' - let g := hex"deadbeef":u32 - let h := hex'deadbeef':u32 + let c := hex"deadbeef" + let d := hex'deadbeef' datacopy(0, dataoffset('runtime'), datasize("runtime")) return(0, datasize("runtime")) } diff --git a/crates/fmt/testdata/YulStrings/preserve-quote.fmt.sol b/crates/fmt/testdata/YulStrings/preserve-quote.fmt.sol index dff9435396706..7c0e448a3f65a 100644 --- a/crates/fmt/testdata/YulStrings/preserve-quote.fmt.sol +++ b/crates/fmt/testdata/YulStrings/preserve-quote.fmt.sol @@ -4,12 +4,8 @@ contract Yul { assembly { let a := "abc" let b := 'abc' - let c := "abc":u32 - let d := 'abc':u32 - let e := hex"deadbeef" - let f := hex'deadbeef' - let g := hex"deadbeef":u32 - let h := hex'deadbeef':u32 + let c := hex"deadbeef" + let d := hex'deadbeef' datacopy(0, dataoffset('runtime'), datasize("runtime")) return(0, datasize("runtime")) } diff --git a/crates/fmt/testdata/YulStrings/single-quote.fmt.sol b/crates/fmt/testdata/YulStrings/single-quote.fmt.sol index f1fc7fb8b514a..f1f4270e06b25 100644 --- a/crates/fmt/testdata/YulStrings/single-quote.fmt.sol +++ b/crates/fmt/testdata/YulStrings/single-quote.fmt.sol @@ -4,12 +4,8 @@ contract Yul { assembly { let a := 'abc' let b := 'abc' - let c := 'abc':u32 - let d := 'abc':u32 - let e := hex'deadbeef' - let f := hex'deadbeef' - let g := hex'deadbeef':u32 - let h := hex'deadbeef':u32 + let c := hex'deadbeef' + let d := hex'deadbeef' datacopy(0, dataoffset('runtime'), datasize('runtime')) return(0, datasize('runtime')) } From 872da09ee2ae2e38875de8a8434d92a150acb49f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:59:51 +0200 Subject: [PATCH 22/54] test: function parameters cannot be empty --- crates/fmt/testdata/TrailingComma/fmt.sol | 4 ---- crates/fmt/testdata/TrailingComma/original.sol | 8 ++------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/fmt/testdata/TrailingComma/fmt.sol b/crates/fmt/testdata/TrailingComma/fmt.sol index 034ac5d33088d..c69a6562fce9a 100644 --- a/crates/fmt/testdata/TrailingComma/fmt.sol +++ b/crates/fmt/testdata/TrailingComma/fmt.sol @@ -1,8 +1,4 @@ contract C is Contract { - modifier m(uint256) {} - // invalid solidity code, but valid pt - modifier m2(uint256) returns (uint256) {} - function f(uint256 a) external {} function f2(uint256 a, bytes32 b) external returns (uint256) {} diff --git a/crates/fmt/testdata/TrailingComma/original.sol b/crates/fmt/testdata/TrailingComma/original.sol index c06460f250aa8..644eb064c9ba6 100644 --- a/crates/fmt/testdata/TrailingComma/original.sol +++ b/crates/fmt/testdata/TrailingComma/original.sol @@ -1,12 +1,8 @@ contract C is Contract { - modifier m(uint256, ,,, ) {} - // invalid solidity code, but valid pt - modifier m2(uint256) returns (uint256,,,) {} - function f(uint256 a, ) external {} - function f2(uint256 a, , , ,bytes32 b) external returns (uint256,,,,) {} + function f2(uint256 a, bytes32 b,) external returns (uint256,) {} function f3() external { - try some.invoke() returns (uint256,,,uint256) {} catch {} + try some.invoke() returns (uint256,uint256,) {} catch {} } } From b38a8201ca4640d91c7e7ba2da9b6be4e4101d1e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:34:20 +0200 Subject: [PATCH 23/54] fix: adjust '()' for modifier calls for real --- crates/fmt-2/src/pp/convenience.rs | 1 + crates/fmt-2/src/state.rs | 93 ++++++++++++++++++------------ env.sh | 2 +- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index ae19ef3fa7b39..4c5af6d482837 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -62,6 +62,7 @@ impl Printer { } pub fn is_exactly_one_empty_line(&self) -> bool { + // TODO(dani) self.is_beginning_of_line() } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 8813c037ab47c..b0620c35176b1 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -72,16 +72,19 @@ pub contract_new_lines: bool, pub sort_imports: bool, */ -pub(super) struct State<'a> { +pub(super) struct State<'sess, 'ast> { pub(crate) s: pp::Printer, ind: isize, - sm: &'a SourceMap, + + sm: &'sess SourceMap, comments: Comments, config: FormatterConfig, inline_config: InlineConfig, + + contract: Option<&'ast ast::ItemContract<'ast>>, } -impl std::ops::Deref for State<'_> { +impl std::ops::Deref for State<'_, '_> { type Target = pp::Printer; #[inline(always)] @@ -90,7 +93,7 @@ impl std::ops::Deref for State<'_> { } } -impl std::ops::DerefMut for State<'_> { +impl std::ops::DerefMut for State<'_, '_> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.s @@ -98,9 +101,9 @@ impl std::ops::DerefMut for State<'_> { } /// Generic methods. -impl<'a> State<'a> { +impl<'sess> State<'sess, '_> { pub(super) fn new( - sm: &'a SourceMap, + sm: &'sess SourceMap, config: FormatterConfig, inline_config: InlineConfig, comments: Comments, @@ -112,6 +115,7 @@ impl<'a> State<'a> { comments, inline_config, config, + contract: None, } } @@ -210,7 +214,7 @@ impl<'a> State<'a> { fn peek_comment<'b>(&'b self) -> Option<&'b Comment> where - 'a: 'b, + 'sess: 'b, { self.comments().peek() } @@ -228,7 +232,7 @@ impl<'a> State<'a> { fn print_remaining_comments(&mut self) { // If there aren't any remaining comments, then we need to manually // make sure there is a line break at the end. - if self.peek_comment().is_none() { + if self.peek_comment().is_none() && !self.is_beginning_of_line() { self.hardbreak(); } while let Some(cmnt) = self.next_comment() { @@ -282,7 +286,7 @@ impl<'a> State<'a> { } /// Span to source. -impl State<'_> { +impl State<'_, '_> { fn char_at(&self, pos: BytePos) -> char { let res = self.sm.lookup_byte_offset(pos); res.sf.src[res.pos.to_usize()..].chars().next().unwrap() @@ -343,15 +347,15 @@ struct FunctionLike<'a, 'b> { } /// Language-specific pretty printing. -impl State<'_> { - pub fn print_source_unit(&mut self, source_unit: &ast::SourceUnit<'_>) { +impl<'ast> State<'_, 'ast> { + pub fn print_source_unit(&mut self, source_unit: &'ast ast::SourceUnit<'ast>) { for item in source_unit.items.iter() { self.print_item(item); } self.print_remaining_comments(); } - fn print_item(&mut self, item: &ast::Item<'_>) { + fn print_item(&mut self, item: &'ast ast::Item<'ast>) { let ast::Item { docs, span, kind } = item; self.print_docs(docs); self.maybe_print_comments(span.lo()); @@ -459,7 +463,9 @@ impl State<'_> { self.word(";"); self.hardbreak(); } - ast::ItemKind::Contract(ast::ItemContract { kind, name, layout, bases, body }) => { + ast::ItemKind::Contract(c @ ast::ItemContract { kind, name, layout, bases, body }) => { + self.contract = Some(c); + self.s.cbox(self.ind); self.s.cbox(0); self.word_nbsp(kind.to_str()); @@ -507,6 +513,8 @@ impl State<'_> { self.end(); self.word("}"); self.hardbreak(); + + self.contract = None; } ast::ItemKind::Function(func) => self.print_function(func), ast::ItemKind::Variable(var) => self.print_var_def(var), @@ -571,7 +579,7 @@ impl State<'_> { } } - fn print_function(&mut self, func: &ast::ItemFunction<'_>) { + fn print_function(&mut self, func: &'ast ast::ItemFunction<'ast>) { let ast::ItemFunction { kind, header, body, body_span } = func; let ast::FunctionHeader { name, @@ -599,7 +607,7 @@ impl State<'_> { }); } - fn print_function_like(&mut self, args: FunctionLike<'_, '_>) { + fn print_function_like(&mut self, args: FunctionLike<'ast, 'ast>) { let FunctionLike { kind, name, @@ -639,11 +647,20 @@ impl State<'_> { for modifier in modifiers { self.nbsp(); - // Add `()` in constructors when the modifier is a base contract. - // HACK: we can't really know for sure, so we assume that uppercase names are bases. + // Add `()` in functions when the modifier is a base contract. + // HACK: heuristics: + // 1. exactly matches the name of a base contract as declared in the `contract is`; + // this does not account for inheritance; + let is_contract_base = self.contract.is_some_and(|contract| { + contract.bases.iter().any(|contract_base| contract_base.name == modifier.name) + }); + // 2. assume that title case names in constructors are bases. + // LEGACY: constructors used to also be `function NameOfContract...`; not checked. + let is_constructor = args.kind == "constructor"; // LEGACY: we are checking the beginning of the path, not the last segment. - let is_base_contract = args.kind == "constructor" && - modifier.name.first().name.as_str().starts_with(char::is_uppercase); + let is_base_contract = is_contract_base || + (is_constructor && + modifier.name.first().name.as_str().starts_with(char::is_uppercase)); self.print_modifier_call(modifier, is_base_contract); } @@ -665,13 +682,13 @@ impl State<'_> { } } - fn print_var_def(&mut self, var: &ast::VariableDefinition<'_>) { + fn print_var_def(&mut self, var: &'ast ast::VariableDefinition<'ast>) { self.print_var(var); self.word(";"); self.hardbreak(); } - fn print_var(&mut self, var: &ast::VariableDefinition<'_>) { + fn print_var(&mut self, var: &'ast ast::VariableDefinition<'ast>) { let ast::VariableDefinition { span, ty, @@ -720,7 +737,7 @@ impl State<'_> { } } - fn print_parameter_list(&mut self, parameters: &[ast::VariableDefinition<'_>]) { + fn print_parameter_list(&mut self, parameters: &'ast [ast::VariableDefinition<'ast>]) { if parameters.is_empty() { self.word("()"); return; @@ -741,7 +758,7 @@ impl State<'_> { self.end(); } - fn print_docs(&mut self, docs: &ast::DocComments<'_>) { + fn print_docs(&mut self, docs: &'ast ast::DocComments<'ast>) { for &ast::DocComment { kind, span, symbol } in docs.iter() { self.maybe_print_comments(span.lo()); self.word(match kind { @@ -756,7 +773,7 @@ impl State<'_> { } } - fn print_ident_or_strlit(&mut self, value: &ast::IdentOrStrLit) { + fn print_ident_or_strlit(&mut self, value: &'ast ast::IdentOrStrLit) { match value { ast::IdentOrStrLit::Ident(ident) => self.print_ident(*ident), ast::IdentOrStrLit::StrLit(strlit) => self.print_ast_str_lit(strlit), @@ -774,7 +791,7 @@ impl State<'_> { self.word(ident.to_string()); } - fn print_path(&mut self, path: &ast::PathSlice) { + fn print_path(&mut self, path: &'ast ast::PathSlice) { for (pos, ident) in path.segments().iter().delimited() { self.print_ident(*ident); if !pos.is_last { @@ -783,7 +800,7 @@ impl State<'_> { } } - fn print_lit(&mut self, lit: &ast::Lit) { + fn print_lit(&mut self, lit: &'ast ast::Lit) { let &ast::Lit { span, symbol, ref kind } = lit; if self.handle_span(span) { return; @@ -899,7 +916,7 @@ impl State<'_> { } /// Prints a raw AST string literal, which is unescaped. - fn print_ast_str_lit(&mut self, strlit: &ast::StrLit) { + fn print_ast_str_lit(&mut self, strlit: &'ast ast::StrLit) { self.print_str_lit(ast::StrKind::Str, strlit.span.lo(), strlit.value.as_str()); } @@ -938,7 +955,7 @@ impl State<'_> { format!("{prefix}{quote}{s}{quote}") } - fn print_ty(&mut self, ty: &ast::Type<'_>) { + fn print_ty(&mut self, ty: &'ast ast::Type<'ast>) { if self.handle_span(ty.span) { return; } @@ -998,7 +1015,7 @@ impl State<'_> { } } - fn print_override(&mut self, override_: &ast::Override<'_>) { + fn print_override(&mut self, override_: &'ast ast::Override<'ast>) { let ast::Override { span, paths } = override_; if self.handle_span(*span) { return; @@ -1028,7 +1045,7 @@ impl State<'_> { /* --- Expressions --- */ #[expect(unused_variables)] - fn print_expr(&mut self, expr: &ast::Expr<'_>) { + fn print_expr(&mut self, expr: &'ast ast::Expr<'ast>) { let ast::Expr { span, ref kind } = *expr; if self.handle_span(span) { return; @@ -1084,7 +1101,11 @@ impl State<'_> { } // If `add_parens_if_empty` is true, then add parentheses `()` even if there are no arguments. - fn print_modifier_call(&mut self, modifier: &ast::Modifier<'_>, add_parens_if_empty: bool) { + fn print_modifier_call( + &mut self, + modifier: &'ast ast::Modifier<'ast>, + add_parens_if_empty: bool, + ) { let ast::Modifier { name, arguments } = modifier; self.print_path(name); if !arguments.is_empty() || add_parens_if_empty { @@ -1092,7 +1113,7 @@ impl State<'_> { } } - fn print_call_args(&mut self, args: &ast::CallArgs<'_>) { + fn print_call_args(&mut self, args: &'ast ast::CallArgs<'ast>) { let ast::CallArgs { span, kind } = args; if self.handle_span(*span) { return; @@ -1136,7 +1157,7 @@ impl State<'_> { /* --- Statements --- */ #[expect(unused_variables)] - fn print_stmt(&mut self, stmt: &ast::Stmt<'_>) { + fn print_stmt(&mut self, stmt: &'ast ast::Stmt<'ast>) { // TODO(dani) let ast::Stmt { docs, span, kind } = stmt; self.print_docs(docs); @@ -1176,7 +1197,7 @@ impl State<'_> { } } - fn print_block(&mut self, block: &[ast::Stmt<'_>], span: Span) { + fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { // TODO: attempt_single_line, attempt_omit_braces self.word("{"); if block.is_empty() { @@ -1201,7 +1222,7 @@ impl State<'_> { self.hardbreak(); } - fn single_line_block(&self, block: &[ast::Stmt<'_>], span: Span) -> bool { + fn single_line_block(&self, block: &'ast [ast::Stmt<'_>], span: Span) -> bool { if block.len() != 1 { return false; } @@ -1213,7 +1234,7 @@ impl State<'_> { } } -fn stmt_needs_semi(stmt: &ast::StmtKind<'_>) -> bool { +fn stmt_needs_semi<'ast>(stmt: &'ast ast::StmtKind<'ast>) -> bool { match stmt { ast::StmtKind::Assembly { .. } | ast::StmtKind::Block { .. } | diff --git a/env.sh b/env.sh index 4c301e62e7a10..78eaeea717217 100644 --- a/env.sh +++ b/env.sh @@ -18,7 +18,7 @@ forge-fmt-cmp() { forge fmt - --raw < "$in_f" > "$tmp/old.sol" || return 1 "$forge_fmt_new" "$in_f" > "$tmp/new.sol" || return 1 - echo -n "$(perl -pe 'chomp if eof' "$tmp/new.sol")" > "$tmp/new.sol" # chop last nl + # echo -n "$(perl -pe 'chomp if eof' "$tmp/new.sol")" > "$tmp/new.sol" # chop last nl bat --paging=never "$tmp/old.sol" "$tmp/new.sol" || return 1 From 2f7400bb09c4c30914b4a498ed10c3a6539f5c67 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:37:06 +0200 Subject: [PATCH 24/54] fix: forge fmt hates '*' imports --- crates/fmt-2/src/state.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index b0620c35176b1..b2aefb06246e3 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -385,11 +385,11 @@ impl<'ast> State<'_, 'ast> { ast::ItemKind::Import(ast::ImportDirective { path, items }) => { self.word("import "); match items { - ast::ImportItems::Plain(ident) => { + ast::ImportItems::Plain(_) | ast::ImportItems::Glob(_) => { self.print_ast_str_lit(path); - if let Some(ident) = ident { + if let Some(ident) = items.source_alias() { self.word(" as "); - self.print_ident(*ident); + self.print_ident(ident); } } ast::ImportItems::Aliases(aliases) => { @@ -414,12 +414,6 @@ impl<'ast> State<'_, 'ast> { self.word(" from "); self.print_ast_str_lit(path); } - ast::ImportItems::Glob(ident) => { - self.word("* as "); - self.print_ident(*ident); - self.word(" from "); - self.print_ast_str_lit(path); - } } self.word(";"); self.hardbreak(); From 967b0268250d3c04fdaae16d53711f1fc060cb43 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 23 Apr 2025 01:01:54 +0200 Subject: [PATCH 25/54] test: fix some invalid syntax --- crates/fmt-2/tests/formatter.rs | 4 +++- crates/fmt/testdata/InlineDisable/original.sol | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index 8ef678708dfa7..fbb47859c895d 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -142,7 +142,7 @@ fn test_all_dirs_are_declared(dirs: &[&str]) { } macro_rules! fmt_tests { - ($($dir:ident),+ $(,)?) => { + ($($(#[$attr:meta])* $dir:ident),+ $(,)?) => { #[test] fn all_dirs_are_declared() { test_all_dirs_are_declared(&[$(stringify!($dir)),*]); @@ -151,6 +151,7 @@ macro_rules! fmt_tests { $( #[allow(non_snake_case)] #[test] + $(#[$attr])* fn $dir() { test_directory(stringify!($dir)); } @@ -159,6 +160,7 @@ macro_rules! fmt_tests { } fmt_tests! { + #[ignore = "annotations are not valid Solidity"] Annotation, ArrayExpressions, BlockComments, diff --git a/crates/fmt/testdata/InlineDisable/original.sol b/crates/fmt/testdata/InlineDisable/original.sol index 7731678940cbf..4da5d5b3ce656 100644 --- a/crates/fmt/testdata/InlineDisable/original.sol +++ b/crates/fmt/testdata/InlineDisable/original.sol @@ -14,7 +14,7 @@ enum States { State1, State2, State3, State4, State5, State6, State7, State8, St enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } // forgefmt: disable-next-line -bytes32 constant private BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; +bytes32 constant BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; // forgefmt: disable-start From 100f8e065b19d760f196ebc8c651760f2545bbc9 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 23 Apr 2025 01:04:46 +0200 Subject: [PATCH 26/54] test: disable-stop does not exist --- Cargo.lock | 38 +++++++++---------- crates/fmt/testdata/InlineDisable/fmt.sol | 2 +- .../fmt/testdata/InlineDisable/original.sol | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbfd72f1cd978..d59c148c9c98e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3286,7 +3286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3405,7 +3405,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5520,7 +5520,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.0", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5583,7 +5583,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7223,7 +7223,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7765,7 +7765,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7778,7 +7778,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8487,7 +8487,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" +source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" dependencies = [ "alloy-primitives 1.0.0", "bumpalo", @@ -8505,7 +8505,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" +source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" dependencies = [ "strum 0.27.1", ] @@ -8513,7 +8513,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" +source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" dependencies = [ "bumpalo", "index_vec", @@ -8527,7 +8527,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" +source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" dependencies = [ "annotate-snippets", "anstream", @@ -8536,7 +8536,7 @@ dependencies = [ "derive_builder", "derive_more 2.0.1", "dunce", - "itertools 0.11.0", + "itertools 0.13.0", "itoa", "lasso", "match_cfg", @@ -8546,7 +8546,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 2.0.12", + "thiserror 1.0.69", "tracing", "unicode-width 0.2.0", ] @@ -8554,7 +8554,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" +source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" dependencies = [ "proc-macro2", "quote", @@ -8564,12 +8564,12 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" +source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" dependencies = [ "alloy-primitives 1.0.0", "bitflags 2.9.0", "bumpalo", - "itertools 0.11.0", + "itertools 0.13.0", "memchr", "num-bigint", "num-rational", @@ -8584,7 +8584,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#ea05097f9b46d549624c46df313a6409a4aa5849" +source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" dependencies = [ "alloy-json-abi 1.0.0", "alloy-primitives 1.0.0", @@ -8888,7 +8888,7 @@ dependencies = [ "serde_json", "sha2", "tempfile", - "thiserror 2.0.12", + "thiserror 1.0.69", "url", "zip", ] @@ -8997,7 +8997,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/crates/fmt/testdata/InlineDisable/fmt.sol b/crates/fmt/testdata/InlineDisable/fmt.sol index d7adea60b32da..44a188e434d37 100644 --- a/crates/fmt/testdata/InlineDisable/fmt.sol +++ b/crates/fmt/testdata/InlineDisable/fmt.sol @@ -487,7 +487,7 @@ error TopLevelCustomErrorArgWithoutName (string); event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); -// forgefmt: disable-stop +// forgefmt: disable-end function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line number = newNumber; diff --git a/crates/fmt/testdata/InlineDisable/original.sol b/crates/fmt/testdata/InlineDisable/original.sol index 4da5d5b3ce656..88befe4ee929f 100644 --- a/crates/fmt/testdata/InlineDisable/original.sol +++ b/crates/fmt/testdata/InlineDisable/original.sol @@ -466,7 +466,7 @@ error TopLevelCustomErrorArgWithoutName (string); event Event1(uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a, uint256 indexed a); -// forgefmt: disable-stop +// forgefmt: disable-end function setNumber(uint256 newNumber /* param1 */, uint256 sjdfasdfasdfasdfasfsdfsadfasdfasdfasdfsadjfkhasdfljkahsdfkjasdkfhsaf /* param2 */) public view returns (bool,bool) { /* inline*/ number1 = newNumber1; // forgefmt: disable-line number = newNumber; From 6c5337313745b9f0be2aee173000ed429ddd02d1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 23 Apr 2025 01:13:14 +0200 Subject: [PATCH 27/54] test: fix all parse errors --- crates/fmt/testdata/LiteralExpression/fmt.sol | 2 +- crates/fmt/testdata/LiteralExpression/original.sol | 2 +- .../fmt/testdata/LiteralExpression/preserve-quote.fmt.sol | 2 +- .../fmt/testdata/LiteralExpression/single-quote.fmt.sol | 2 +- crates/fmt/testdata/SortedImports/fmt.sol | 4 ++-- crates/fmt/testdata/SortedImports/original.sol | 8 ++++---- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/fmt/testdata/LiteralExpression/fmt.sol b/crates/fmt/testdata/LiteralExpression/fmt.sol index 7fa6878e5586d..4c845185f4efb 100644 --- a/crates/fmt/testdata/LiteralExpression/fmt.sol +++ b/crates/fmt/testdata/LiteralExpression/fmt.sol @@ -11,7 +11,7 @@ contract LiteralExpressions { // number literals 1; 123_000; - 1_2e345_678; + // 1_2e345_678; -1; 2e-10; // comment5 diff --git a/crates/fmt/testdata/LiteralExpression/original.sol b/crates/fmt/testdata/LiteralExpression/original.sol index 5c559531de955..37bf210359f7f 100644 --- a/crates/fmt/testdata/LiteralExpression/original.sol +++ b/crates/fmt/testdata/LiteralExpression/original.sol @@ -10,7 +10,7 @@ contract LiteralExpressions { // number literals 1; 123_000; - 1_2e345_678; + // 1_2e345_678; -1; 2e-10; // comment5 diff --git a/crates/fmt/testdata/LiteralExpression/preserve-quote.fmt.sol b/crates/fmt/testdata/LiteralExpression/preserve-quote.fmt.sol index 3d9490804f231..3aafcef65e978 100644 --- a/crates/fmt/testdata/LiteralExpression/preserve-quote.fmt.sol +++ b/crates/fmt/testdata/LiteralExpression/preserve-quote.fmt.sol @@ -12,7 +12,7 @@ contract LiteralExpressions { // number literals 1; 123_000; - 1_2e345_678; + // 1_2e345_678; -1; 2e-10; // comment5 diff --git a/crates/fmt/testdata/LiteralExpression/single-quote.fmt.sol b/crates/fmt/testdata/LiteralExpression/single-quote.fmt.sol index cdc67a2c6c814..83ff3f94a4435 100644 --- a/crates/fmt/testdata/LiteralExpression/single-quote.fmt.sol +++ b/crates/fmt/testdata/LiteralExpression/single-quote.fmt.sol @@ -12,7 +12,7 @@ contract LiteralExpressions { // number literals 1; 123_000; - 1_2e345_678; + // 1_2e345_678; -1; 2e-10; // comment5 diff --git a/crates/fmt/testdata/SortedImports/fmt.sol b/crates/fmt/testdata/SortedImports/fmt.sol index f9b2c0ee2a9c3..9d6b0d9c02e2e 100644 --- a/crates/fmt/testdata/SortedImports/fmt.sol +++ b/crates/fmt/testdata/SortedImports/fmt.sol @@ -29,6 +29,6 @@ import {Something2, Something3} from "someFile.sol"; // This is a comment import {Something2, Something3} from "someFile.sol"; -import {symbol1 as alias, symbol2} from "File3.sol"; +import {symbol1 as alias0, symbol2} from "File3.sol"; // comment inside group is treated as a separator for now -import {symbol1 as alias, symbol2} from "File2.sol"; +import {symbol1 as alias0, symbol2} from "File2.sol"; diff --git a/crates/fmt/testdata/SortedImports/original.sol b/crates/fmt/testdata/SortedImports/original.sol index 54b3ca3b59cfb..b52bbda3ad097 100644 --- a/crates/fmt/testdata/SortedImports/original.sol +++ b/crates/fmt/testdata/SortedImports/original.sol @@ -6,8 +6,8 @@ import "SomeFile0.sol" as SomeOtherFile; import "AnotherFile2.sol" as SomeSymbol; import "AnotherFile1.sol" as SomeSymbol; -import {symbol2, symbol1 as alias} from "File3.sol"; -import {symbol2, symbol1 as alias} from "File2.sol"; +import {symbol2, symbol1 as alias0} from "File3.sol"; +import {symbol2, symbol1 as alias0} from "File2.sol"; import {symbol2 as alias2, symbol1 as alias1, symbol3 as alias3, symbol4} from "File6.sol"; import {symbol3 as alias1, symbol2 as alias2, symbol1 as alias3, symbol4} from "File0.sol"; @@ -18,6 +18,6 @@ import {Something3, Something2} from "someFile.sol"; // This is a comment import {Something3, Something2} from "someFile.sol"; -import {symbol2, symbol1 as alias} from "File3.sol"; +import {symbol2, symbol1 as alias0} from "File3.sol"; // comment inside group is treated as a separator for now -import {symbol2, symbol1 as alias} from "File2.sol"; \ No newline at end of file +import {symbol2, symbol1 as alias0} from "File2.sol"; \ No newline at end of file From da745a76796fb3580a10ab44b3e3cf8cc18c3157 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 23 Apr 2025 02:20:43 +0200 Subject: [PATCH 28/54] fix: literal touchups --- crates/fmt-2/src/state.rs | 90 +++++++-------------------------------- 1 file changed, 16 insertions(+), 74 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index b2aefb06246e3..771d87d4f605b 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -14,64 +14,6 @@ use std::borrow::Cow; // TODO(dani): bunch docs into `Comments` since misplaced docs get ignore -/* -- [x] -/// Maximum line length where formatter will try to wrap the line -pub line_length: usize, - -- [x] -/// Number of spaces per indentation level -pub tab_width: usize, - -- [x] -/// Print spaces between brackets -pub bracket_spacing: bool, - -- [x] -/// Style of uint/int256 types -pub int_types: IntTypes, - -- [ ] -/// Style of multiline function header in case it doesn't fit -pub multiline_func_header: MultilineFuncHeaderStyle, - -- [x] -/// Style of quotation marks -pub quote_style: QuoteStyle, - -- [x] -/// Style of underscores in number literals -pub number_underscore: NumberUnderscore, - -- [x] -/// Style of underscores in hex literals -pub hex_underscore: HexUnderscore, - -- [ ] -/// Style of single line blocks in statements -pub single_line_statement_blocks: SingleLineBlockStyle, - -- [x] -/// Print space in state variable, function and modifier `override` attribute -pub override_spacing: bool, - -- [ ] -/// Wrap comments on `line_length` reached -pub wrap_comments: bool, - -- [N/A] -/// Globs to ignore -pub ignore: Vec, - -- [x] -/// Add new line at start and end of contract declarations -pub contract_new_lines: bool, - -- [ ] -/// Sort import statements alphabetically in groups (a group is separated by a newline). -pub sort_imports: bool, -*/ - pub(super) struct State<'sess, 'ast> { pub(crate) s: pp::Printer, ind: isize, @@ -430,9 +372,7 @@ impl<'ast> State<'_, 'ast> { self.print_path(path); if let Some(op) = op { self.word(" as "); - // TODO(dani): op.to_str() - let _ = op; - self.word("?"); + self.word(op.to_str()); } if !pos.is_last { self.word(","); @@ -816,15 +756,14 @@ impl<'ast> State<'_, 'ast> { } self.s.offset(-self.ind); self.end(); - return; } ast::LitKind::Number(_) | ast::LitKind::Rational(_) => { self.print_num_literal(symbol.as_str()); - return; } - _ => {} - }; - self.word(symbol.to_string()); + ast::LitKind::Address(value) => self.word(value.to_string()), + ast::LitKind::Bool(value) => self.word(if value { "true" } else { "false" }), + ast::LitKind::Err(_) => self.word(symbol.to_string()), + } } fn print_num_literal(&mut self, source: &str) { @@ -866,16 +805,19 @@ impl<'ast> State<'_, 'ast> { let (val, fract) = val.split_once('.').unwrap_or((val, "")); let strip_undescores = !config.is_preserve(); - let val = &strip_underscores_if(strip_undescores, val)[..]; - let exp = &strip_underscores_if(strip_undescores, exp)[..]; - let fract = &strip_underscores_if(strip_undescores, fract)[..]; + let mut val = &strip_underscores_if(strip_undescores, val)[..]; + let mut exp = &strip_underscores_if(strip_undescores, exp)[..]; + let mut fract = &strip_underscores_if(strip_undescores, fract)[..]; // strip any padded 0's - let val = val.trim_start_matches('0'); - let fract = fract.trim_end_matches('0'); - let (exp_sign, exp) = - if let Some(exp) = exp.strip_prefix('-') { ("-", exp) } else { ("", exp) }; - let exp = exp.trim_start_matches('0'); + let mut exp_sign = ""; + if !["0x", "0b", "0o"].iter().any(|prefix| source.starts_with(prefix)) { + val = val.trim_start_matches('0'); + fract = fract.trim_end_matches('0'); + (exp_sign, exp) = + if let Some(exp) = exp.strip_prefix('-') { ("-", exp) } else { ("", exp) }; + exp = exp.trim_start_matches('0'); + } let mut out = String::with_capacity(source.len() * 2); if val.is_empty() { From 22741dec1c7df9e3076e250405cef84fb5a02448 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 23 Apr 2025 19:05:52 +0200 Subject: [PATCH 29/54] bump --- Cargo.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d59c148c9c98e..101dfaf4968b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3286,7 +3286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3405,7 +3405,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5520,7 +5520,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.0", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5583,7 +5583,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7223,7 +7223,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7765,7 +7765,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7778,7 +7778,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8487,7 +8487,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" +source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" dependencies = [ "alloy-primitives 1.0.0", "bumpalo", @@ -8505,7 +8505,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" +source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" dependencies = [ "strum 0.27.1", ] @@ -8513,7 +8513,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" +source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" dependencies = [ "bumpalo", "index_vec", @@ -8527,7 +8527,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" +source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" dependencies = [ "annotate-snippets", "anstream", @@ -8536,7 +8536,7 @@ dependencies = [ "derive_builder", "derive_more 2.0.1", "dunce", - "itertools 0.13.0", + "itertools 0.11.0", "itoa", "lasso", "match_cfg", @@ -8546,7 +8546,7 @@ dependencies = [ "solar-config", "solar-data-structures", "solar-macros", - "thiserror 1.0.69", + "thiserror 2.0.12", "tracing", "unicode-width 0.2.0", ] @@ -8554,7 +8554,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" +source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" dependencies = [ "proc-macro2", "quote", @@ -8564,12 +8564,12 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" +source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" dependencies = [ "alloy-primitives 1.0.0", "bitflags 2.9.0", "bumpalo", - "itertools 0.13.0", + "itertools 0.11.0", "memchr", "num-bigint", "num-rational", @@ -8584,7 +8584,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#92037add8a4343c028b21235b5f8ed91411c672e" +source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" dependencies = [ "alloy-json-abi 1.0.0", "alloy-primitives 1.0.0", @@ -8888,7 +8888,7 @@ dependencies = [ "serde_json", "sha2", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.12", "url", "zip", ] @@ -8997,7 +8997,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] From 7b8b9193bd4c91a33fbd060320d80e54f6e82e01 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:12:21 +0200 Subject: [PATCH 30/54] test: add snapshotting --- Cargo.lock | 26 +++--------- Cargo.toml | 5 ++- crates/fmt-2/Cargo.toml | 1 + crates/fmt-2/tests/formatter.rs | 73 ++++++++++++++------------------- crates/test-utils/Cargo.toml | 2 +- 5 files changed, 43 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 165341f79ec3c..570ad1336afad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -777,7 +777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b5f5f9f561c29f78ea521ebe2e5ac1633f1b1442dae582f68ecd57c6350042" dependencies = [ "serde", - "winnow 0.7.6", + "winnow 0.7.7", ] [[package]] @@ -3634,9 +3634,10 @@ dependencies = [ "foundry-config", "itertools 0.14.0", "similar-asserts", + "snapbox", "solar-parse", "thiserror 2.0.12", - "toml 0.8.20", + "toml 0.8.21", "tracing", "tracing-subscriber", ] @@ -5686,7 +5687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -7786,15 +7787,6 @@ dependencies = [ "regex", ] -[[package]] -name = "scc" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" -dependencies = [ - "sdd", -] - [[package]] name = "schannel" version = "0.1.27" @@ -7852,12 +7844,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "sdd" -version = "3.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" - [[package]] name = "sec1" version = "0.7.3" @@ -9417,7 +9403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f" dependencies = [ "cc", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -10053,7 +10039,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8d4e6f1f258ee..d2735c54ef249 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -300,7 +300,6 @@ rustls = "0.23" semver = "1" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } -similar-asserts = "1.6" soldeer-commands = "=0.5.3" strum = "0.27" tempfile = "3.13" @@ -317,6 +316,10 @@ yansi = { version = "1.0", features = ["detect-tty", "detect-env"] } path-slash = "0.2" jiff = "0.2" +# testing +similar-asserts = "1.6" +snapbox = { version = "0.6", features = ["json", "regex", "term-svg"] } + # Use unicode-rs which has a smaller binary size than the default ICU4X as the IDNA backend, used # by the `url` crate. # See the `idna_adapter` README.md for more details: https://docs.rs/crate/idna_adapter/latest diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml index 9c83f16456379..01114a6af563f 100644 --- a/crates/fmt-2/Cargo.toml +++ b/crates/fmt-2/Cargo.toml @@ -27,3 +27,4 @@ itertools.workspace = true similar-asserts.workspace = true toml.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"] } +snapbox.workspace = true diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index fbb47859c895d..bf369d12b93d7 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -1,13 +1,11 @@ use forge_fmt_2::FormatterConfig; -use itertools::Itertools; +use snapbox::{assert_data_eq, Data}; use std::{ - fmt, fs, + fs, path::{Path, PathBuf}, }; use tracing_subscriber::{EnvFilter, FmtSubscriber}; -// TODO(dani): add snapshot infra to automatically update the expected output - #[track_caller] fn format(source: &str, path: &Path, config: FormatterConfig) -> String { match forge_fmt_2::format_source(source, Some(path), config) { @@ -22,21 +20,6 @@ fn assert_eof(content: &str) { assert!(!content.ends_with("\n\n"), "extra trailing newline"); } -#[derive(Eq)] -struct PrettyString<'a>(&'a str); - -impl PartialEq for PrettyString<'_> { - fn eq(&self, other: &Self) -> bool { - self.0.lines().eq(other.0.lines()) - } -} - -impl fmt::Debug for PrettyString<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.0, f) - } -} - fn enable_tracing() { let subscriber = FmtSubscriber::builder() .with_env_filter(EnvFilter::from_default_env()) @@ -54,6 +37,7 @@ fn test_directory(base_name: &str) { enable_tracing(); let dir = tests_dir().join(base_name); let original = fs::read_to_string(dir.join("original.sol")).unwrap(); + let mut handles = vec![]; for res in dir.read_dir().unwrap() { let entry = res.unwrap(); let path = entry.path(); @@ -65,7 +49,7 @@ fn test_directory(base_name: &str) { assert!(path.is_file(), "expected file: {path:?}"); assert!(filename.ends_with("fmt.sol"), "unknown file: {path:?}"); - let source = fs::read_to_string(&path).unwrap(); + let expected = fs::read_to_string(&path).unwrap(); // The majority of the tests were written with the assumption that the default value for max // line length is `80`. Preserve that to avoid rewriting test logic. @@ -73,9 +57,9 @@ fn test_directory(base_name: &str) { let mut config = toml::Value::try_from(default_config).unwrap(); let config_table = config.as_table_mut().unwrap(); - let mut lines = source.split('\n').peekable(); - let mut line_num = 1; - while let Some(&line) = lines.peek() { + let mut comments_end = 0; + for (i, line) in expected.lines().enumerate() { + let line_num = i + 1; let Some(entry) = line .strip_prefix("//") .and_then(|line| line.trim().strip_prefix("config:")) @@ -90,40 +74,45 @@ fn test_directory(base_name: &str) { }; config_table.extend(values); - line_num += 1; - lines.next(); + comments_end += line.len() + 1; } let config = config .try_into() .unwrap_or_else(|err| panic!("invalid test config for {filename}: {err}")); - test_formatter(&path, filename, config, &original, &lines.join("\n")); + let original = original.clone(); + let tname = format!("{base_name}/{filename}"); + let spawn = move || { + test_formatter(&path, config, &original, &expected, comments_end); + }; + handles.push(std::thread::Builder::new().name(tname).spawn(spawn).unwrap()); + } + let results = handles.into_iter().map(|h| h.join()).collect::>(); + for result in results { + result.unwrap(); } } fn test_formatter( - path: &Path, - filename: &str, + expected_path: &Path, config: FormatterConfig, source: &str, expected_source: &str, + comments_end: usize, ) { - assert_eof(expected_source); + let path = &*expected_path.with_file_name("original.sol"); + let expected_data = Data::read_from(expected_path, None); - let source_formatted = format(source, &path.with_file_name("original.sol"), config.clone()); + let mut source_formatted = format(source, path, config.clone()); + // Inject `expected`'s comments, if any, so we can use the expected file as a snapshot. + source_formatted.insert_str(0, &expected_source[..comments_end]); + assert_data_eq!(&source_formatted, &expected_data); assert_eof(&source_formatted); - similar_asserts::assert_eq!( - PrettyString(&source_formatted), - PrettyString(expected_source), - "{filename}: formatted source does not match expected source" - ); - - let expected_formatted = format(expected_source, path, config); - similar_asserts::assert_eq!( - PrettyString(&expected_formatted), - PrettyString(expected_source), - "{filename}: expected source is not formatted" - ); + + let expected_formatted = format(expected_source, expected_path, config); + assert_data_eq!(&expected_formatted, expected_data); + assert_eof(expected_source); + assert_eof(&expected_formatted); } fn test_all_dirs_are_declared(dirs: &[&str]) { diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index b9d7b1ce5f2f6..92e737808ef6e 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -30,7 +30,7 @@ serde_json.workspace = true tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"] } rand.workspace = true -snapbox = { version = "0.6", features = ["json", "regex", "term-svg"] } +snapbox.workspace = true tempfile.workspace = true # See /Cargo.toml. From 19a54029e2ea3f6d5c03cd9514c8143f90931b53 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:16:12 +0200 Subject: [PATCH 31/54] test: update NumberLiteralUnderscore --- .../testdata/NumberLiteralUnderscore/fmt.sol | 18 +++++++-------- .../NumberLiteralUnderscore/preserve.fmt.sol | 18 +++++++-------- .../NumberLiteralUnderscore/remove.fmt.sol | 18 +++++++-------- .../NumberLiteralUnderscore/thousands.fmt.sol | 22 +++++++++---------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/crates/fmt/testdata/NumberLiteralUnderscore/fmt.sol b/crates/fmt/testdata/NumberLiteralUnderscore/fmt.sol index 7c9f5740de76a..77d7afe4c5dc3 100644 --- a/crates/fmt/testdata/NumberLiteralUnderscore/fmt.sol +++ b/crates/fmt/testdata/NumberLiteralUnderscore/fmt.sol @@ -2,23 +2,23 @@ contract NumberLiteral { function test() external { 1; 123_000; - 1_2e345_678; + // 1_2e345_678; -1; 2e-10; 0.1; 1.3; 2.5e1; 1.23454; - 1.2e34_5_678; - 134411.2e34_5_678; - 13431.134112e34_135_678; + // 1.2e34_5_678; + // 134411.2e34_5_678; + // 13431.134112e34_135_678; 13431.0134112; - 13431.134112e-139_3141340; - 134411.2e34_5_6780; - 13431.134112e34_135_6780; - 0.134112; + // 13431.134112e-139_3141340; + // 00134411.200e0034_5_6780; + // 013431.13411200e34_135_6780; + // 00.1341120000; 1.0; - 13431.134112e-139_3141340; + // 0013431.13411200e-00139_3141340; 123e456; 1_000; } diff --git a/crates/fmt/testdata/NumberLiteralUnderscore/preserve.fmt.sol b/crates/fmt/testdata/NumberLiteralUnderscore/preserve.fmt.sol index d87dc99d9653d..0a5e3274a49b7 100644 --- a/crates/fmt/testdata/NumberLiteralUnderscore/preserve.fmt.sol +++ b/crates/fmt/testdata/NumberLiteralUnderscore/preserve.fmt.sol @@ -3,23 +3,23 @@ contract NumberLiteral { function test() external { 1; 123_000; - 1_2e345_678; + // 1_2e345_678; -1; 2e-10; 0.1; 1.3; 2.5e1; 1.23454; - 1.2e34_5_678; - 134411.2e34_5_678; - 13431.134112e34_135_678; + // 1.2e34_5_678; + // 134411.2e34_5_678; + // 13431.134112e34_135_678; 13431.0134112; - 13431.134112e-139_3141340; - 134411.2e34_5_6780; - 13431.134112e34_135_6780; - 0.134112; + // 13431.134112e-139_3141340; + // 00134411.200e0034_5_6780; + // 013431.13411200e34_135_6780; + // 00.1341120000; 1.0; - 13431.134112e-139_3141340; + // 0013431.13411200e-00139_3141340; 123e456; 1_000; } diff --git a/crates/fmt/testdata/NumberLiteralUnderscore/remove.fmt.sol b/crates/fmt/testdata/NumberLiteralUnderscore/remove.fmt.sol index cbde2e9b9fe61..8e43680033c40 100644 --- a/crates/fmt/testdata/NumberLiteralUnderscore/remove.fmt.sol +++ b/crates/fmt/testdata/NumberLiteralUnderscore/remove.fmt.sol @@ -3,23 +3,23 @@ contract NumberLiteral { function test() external { 1; 123000; - 12e345678; + // 1_2e345_678; -1; 2e-10; 0.1; 1.3; 2.5e1; 1.23454; - 1.2e345678; - 134411.2e345678; - 13431.134112e34135678; + // 1.2e34_5_678; + // 134411.2e34_5_678; + // 13431.134112e34_135_678; 13431.0134112; - 13431.134112e-1393141340; - 134411.2e3456780; - 13431.134112e341356780; - 0.134112; + // 13431.134112e-139_3141340; + // 00134411.200e0034_5_6780; + // 013431.13411200e34_135_6780; + // 00.1341120000; 1.0; - 13431.134112e-1393141340; + // 0013431.13411200e-00139_3141340; 123e456; 1000; } diff --git a/crates/fmt/testdata/NumberLiteralUnderscore/thousands.fmt.sol b/crates/fmt/testdata/NumberLiteralUnderscore/thousands.fmt.sol index a9fc8a69ae6fa..9f0800102010e 100644 --- a/crates/fmt/testdata/NumberLiteralUnderscore/thousands.fmt.sol +++ b/crates/fmt/testdata/NumberLiteralUnderscore/thousands.fmt.sol @@ -3,23 +3,23 @@ contract NumberLiteral { function test() external { 1; 123_000; - 12e345_678; + // 1_2e345_678; -1; 2e-10; 0.1; 1.3; 2.5e1; - 1.23454; - 1.2e345_678; - 134_411.2e345_678; - 13_431.134112e34_135_678; - 13_431.0134112; - 13_431.134112e-1_393_141_340; - 134_411.2e3_456_780; - 13_431.134112e341_356_780; - 0.134112; + 1.234_54; + // 1.2e34_5_678; + // 134411.2e34_5_678; + // 13431.134112e34_135_678; + 13_431.013_411_2; + // 13431.134112e-139_3141340; + // 00134411.200e0034_5_6780; + // 013431.13411200e34_135_6780; + // 00.1341120000; 1.0; - 13_431.134112e-1_393_141_340; + // 0013431.13411200e-00139_3141340; 123e456; 1000; } From 3558807cd2a944fd4f99428b26790ac455e55f07 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 16:55:40 +0200 Subject: [PATCH 32/54] fixes --- a.sol | 13 ++++--------- crates/fmt-2/src/state.rs | 26 +++++++++++++++++++++----- crates/fmt-2/tests/formatter.rs | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/a.sol b/a.sol index 755b72e4939a1..ba591979fe0ff 100644 --- a/a.sol +++ b/a.sol @@ -1,10 +1,5 @@ -contract C { - uint256 constant a = [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; - uint256 constant b = [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; - uint256 constant b = - [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; - uint256 constant b = - [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; - uint256 constant b = - [bbbbbbbbbbbbbbbbbbbb, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]; +function f() { + "\ +a"; // c + ""; } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 771d87d4f605b..57c56e9efa022 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -9,6 +9,7 @@ use itertools::{Either, Itertools}; use solar_parse::{ ast::{self, token, Span}, interface::{BytePos, SourceMap}, + Cursor, }; use std::borrow::Cow; @@ -734,6 +735,7 @@ impl<'ast> State<'_, 'ast> { } } + // TODO: Yul literals are slightly different than normal solidity ones fn print_lit(&mut self, lit: &'ast ast::Lit) { let &ast::Lit { span, symbol, ref kind } = lit; if self.handle_span(span) { @@ -742,19 +744,19 @@ impl<'ast> State<'_, 'ast> { match *kind { ast::LitKind::Str(kind, ..) => { - self.s.cbox(self.ind); + self.s.cbox(0); for (pos, (span, symbol)) in lit.literals().delimited() { if !self.handle_span(span) { let quote_pos = span.lo() + kind.prefix().len() as u32; self.print_str_lit(kind, quote_pos, symbol.as_str()); } if !pos.is_last { - self.space(); + self.space_if_not_bol(); + self.maybe_print_trailing_comment(span, None); } else { self.neverbreak(); } } - self.s.offset(-self.ind); self.end(); } ast::LitKind::Number(_) | ast::LitKind::Rational(_) => { @@ -888,7 +890,20 @@ impl<'ast> State<'_, 'ast> { } f.write_str(s) }); - format!("{prefix}{quote}{s}{quote}") + let mut s = format!("{prefix}{quote}{s}{quote}"); + + // If the output is not a single token then revert to the original quote. + if Cursor::new(&s).exactly_one().is_err() { + let other_quote = if quote == '\"' { '\'' } else { '\"' }; + { + let s = unsafe { s.as_bytes_mut() }; + s[prefix.len()] = other_quote as u8; + s[s.len() - 1] = other_quote as u8; + } + debug_assert!(Cursor::new(&s).exactly_one().map(|_| true).unwrap()); + } + + s } fn print_ty(&mut self, ty: &'ast ast::Type<'ast>) { @@ -1131,6 +1146,7 @@ impl<'ast> State<'_, 'ast> { if stmt_needs_semi(kind) { self.word(";"); } + self.maybe_print_trailing_comment(stmt.span, None); } fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { @@ -1148,7 +1164,7 @@ impl<'ast> State<'_, 'ast> { self.hardbreak(); for stmt in block { self.print_stmt(stmt); - self.hardbreak(); + self.hardbreak_if_not_bol(); } self.s.offset(-self.ind); self.end(); diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index bf369d12b93d7..47b7cc6b20ece 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -101,7 +101,7 @@ fn test_formatter( comments_end: usize, ) { let path = &*expected_path.with_file_name("original.sol"); - let expected_data = Data::read_from(expected_path, None); + let expected_data = Data::read_from(expected_path, None).raw(); let mut source_formatted = format(source, path, config.clone()); // Inject `expected`'s comments, if any, so we can use the expected file as a snapshot. From 6b452600bb5e25ccdb44567fab76c3df15e6b929 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 17:56:11 +0200 Subject: [PATCH 33/54] struct space --- a.sol | 5 +---- crates/fmt-2/src/comments.rs | 21 +++++++++++++-------- crates/fmt-2/src/pp/convenience.rs | 22 ++++++++++++++++------ crates/fmt-2/src/pp/mod.rs | 3 ++- crates/fmt-2/src/state.rs | 10 ++++------ 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/a.sol b/a.sol index ba591979fe0ff..a5e71077532cf 100644 --- a/a.sol +++ b/a.sol @@ -1,5 +1,2 @@ -function f() { - "\ -a"; // c - ""; +enum A {A, B, C } diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index 38e4ec46a87a6..cddddca9a5a30 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -4,10 +4,17 @@ use solar_parse::{ interface::{source_map::SourceFile, BytePos, CharPos, SourceMap}, lexer::token::RawTokenKind as TokenKind, }; +use std::fmt; pub struct Comments { - // Stored in reverse order so we can consume them by popping. - reversed_comments: Vec, + comments: std::vec::IntoIter, +} + +impl fmt::Debug for Comments { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Comments")?; + f.debug_list().entries(self.iter()).finish() + } } /// Returns `None` if the first `col` chars of `s` contain a non-whitespace char. @@ -148,22 +155,20 @@ fn gather_comments(sf: &SourceFile) -> Vec { impl Comments { pub fn new(sf: &SourceFile) -> Self { - let mut comments = gather_comments(sf); - comments.reverse(); - Self { reversed_comments: comments } + Self { comments: gather_comments(sf).into_iter() } } pub fn peek(&self) -> Option<&Comment> { - self.reversed_comments.last() + self.comments.as_slice().first() } #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Option { - self.reversed_comments.pop() + self.comments.next() } pub fn iter(&self) -> impl Iterator { - self.reversed_comments.iter().rev() + self.comments.as_slice().iter() } pub fn trailing_comment( diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 4c5af6d482837..1c248c8f15ab8 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -61,11 +61,6 @@ impl Printer { } } - pub fn is_exactly_one_empty_line(&self) -> bool { - // TODO(dani) - self.is_beginning_of_line() - } - pub(crate) fn hardbreak_tok_offset(offset: isize) -> Token { Token::Break(BreakToken { offset, @@ -117,6 +112,21 @@ impl Printer { impl Token { pub(crate) fn is_hardbreak_tok(&self) -> bool { - *self == Printer::hardbreak_tok_offset(0) + if let Token::Break(BreakToken { + offset, + blank_space, + pre_break: _, + if_nonempty: _, + never_break, + }) = *self + { + offset == 0 && blank_space == SIZE_INFINITY as usize && !never_break + } else { + false + } + } + + pub(crate) fn is_if_nonempty(&self) -> bool { + matches!(self, Token::Break(b) if b.if_nonempty) } } diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 4a8f1df24820c..f98b65c081905 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -65,7 +65,7 @@ pub(crate) enum Token { End, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] enum PrintFrame { Fits(Breaks), Broken(usize, Breaks), @@ -73,6 +73,7 @@ enum PrintFrame { const SIZE_INFINITY: isize = 0xffff; +#[derive(Debug)] pub struct Printer { out: String, /// Number of spaces left on line diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 57c56e9efa022..16b4904eb456b 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -457,7 +457,7 @@ impl<'ast> State<'_, 'ast> { self.s.cbox(self.ind); self.word("struct "); self.print_ident(*name); - self.word("{"); + self.word(" {"); self.hardbreak_if_nonempty(); for var in fields.iter() { self.print_var_def(var); @@ -475,12 +475,8 @@ impl<'ast> State<'_, 'ast> { self.hardbreak_if_nonempty(); for (pos, ident) in variants.iter().delimited() { self.print_ident(*ident); - if !pos.is_last { - self.word(","); - self.space(); - } + self.trailing_comma(pos.is_last); } - self.zerobreak(); self.s.offset(-self.ind); self.end(); self.word("}"); @@ -620,6 +616,7 @@ impl<'ast> State<'_, 'ast> { fn print_var_def(&mut self, var: &'ast ast::VariableDefinition<'ast>) { self.print_var(var); self.word(";"); + self.maybe_print_trailing_comment(var.span, None); self.hardbreak(); } @@ -1146,6 +1143,7 @@ impl<'ast> State<'_, 'ast> { if stmt_needs_semi(kind) { self.word(";"); } + self.maybe_print_comments(stmt.span.hi()); self.maybe_print_trailing_comment(stmt.span, None); } From 1858c046e0b5449c7149be4babefa81112165a41 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:06:59 +0200 Subject: [PATCH 34/54] test: fix StructDefinition; empty structs are not allowed anyway --- crates/fmt-2/src/state.rs | 1 + crates/fmt-2/tests/formatter.rs | 9 +++++---- .../testdata/StructDefinition/bracket-spacing.fmt.sol | 3 +-- crates/fmt/testdata/StructDefinition/fmt.sol | 1 - 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 16b4904eb456b..afc5dc8936586 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -464,6 +464,7 @@ impl<'ast> State<'_, 'ast> { } self.s.offset(-self.ind); self.end(); + self.maybe_print_comments(span.hi()); self.word("}"); self.hardbreak(); } diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index 47b7cc6b20ece..94cff73860c5d 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -101,16 +101,17 @@ fn test_formatter( comments_end: usize, ) { let path = &*expected_path.with_file_name("original.sol"); - let expected_data = Data::read_from(expected_path, None).raw(); + let expected_data = || Data::read_from(expected_path, None).raw(); let mut source_formatted = format(source, path, config.clone()); // Inject `expected`'s comments, if any, so we can use the expected file as a snapshot. source_formatted.insert_str(0, &expected_source[..comments_end]); - assert_data_eq!(&source_formatted, &expected_data); + assert_data_eq!(&source_formatted, expected_data()); assert_eof(&source_formatted); - let expected_formatted = format(expected_source, expected_path, config); - assert_data_eq!(&expected_formatted, expected_data); + let expected_formatted = + format(&std::fs::read_to_string(expected_path).unwrap(), expected_path, config); + assert_data_eq!(&expected_formatted, expected_data()); assert_eof(expected_source); assert_eof(&expected_formatted); } diff --git a/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol index 3e1c8ea4e3ff1..d178199a57712 100644 --- a/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol @@ -1,6 +1,5 @@ // config: bracket_spacing = true -struct Foo { } - +struct Foo {} struct Bar { uint256 foo; string bar; diff --git a/crates/fmt/testdata/StructDefinition/fmt.sol b/crates/fmt/testdata/StructDefinition/fmt.sol index 78c5079cd37e9..84effdf30b032 100644 --- a/crates/fmt/testdata/StructDefinition/fmt.sol +++ b/crates/fmt/testdata/StructDefinition/fmt.sol @@ -1,5 +1,4 @@ struct Foo {} - struct Bar { uint256 foo; string bar; From 79bc94cdd2901346a9d3079f23b32fbae9bc933b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:16:27 +0200 Subject: [PATCH 35/54] test: update EventDefinition; matches Solidity style guide --- crates/fmt/testdata/EventDefinition/fmt.sol | 26 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/fmt/testdata/EventDefinition/fmt.sol b/crates/fmt/testdata/EventDefinition/fmt.sol index 11d3d8256865a..1e404c14acaa9 100644 --- a/crates/fmt/testdata/EventDefinition/fmt.sol +++ b/crates/fmt/testdata/EventDefinition/fmt.sol @@ -18,14 +18,34 @@ contract Events { event Event1(uint256, uint256, uint256, uint256, uint256, uint256, uint256); event Event1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256 + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 ) anonymous; event Event1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 ); event Event1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 ) anonymous; event Event1( From d00bc3f57cc8e3020718136fa4e36e5d558529fb Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:21:45 +0200 Subject: [PATCH 36/54] test: update EnumDefinition; same as StructDefinition --- a.sol | 7 ++++++- crates/fmt-2/src/state.rs | 1 + crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol | 4 +++- crates/fmt/testdata/EnumDefinition/fmt.sol | 4 +++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/a.sol b/a.sol index a5e71077532cf..c02da7f874f52 100644 --- a/a.sol +++ b/a.sol @@ -1,2 +1,7 @@ -enum A {A, B, C +contract EnumDefinitions { + enum Empty { + + } + enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } + enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index afc5dc8936586..5c2ae36a91cba 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -480,6 +480,7 @@ impl<'ast> State<'_, 'ast> { } self.s.offset(-self.ind); self.end(); + self.maybe_print_comments(span.hi()); self.word("}"); self.hardbreak(); } diff --git a/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol index a4ae0f0192051..9d1a8091d0ffb 100644 --- a/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol @@ -1,6 +1,8 @@ // config: bracket_spacing = true contract EnumDefinitions { - enum Empty { } + enum Empty { + + } enum ActionChoices { GoLeft, GoRight, diff --git a/crates/fmt/testdata/EnumDefinition/fmt.sol b/crates/fmt/testdata/EnumDefinition/fmt.sol index 437268aff6423..bf364c34df9e3 100644 --- a/crates/fmt/testdata/EnumDefinition/fmt.sol +++ b/crates/fmt/testdata/EnumDefinition/fmt.sol @@ -1,5 +1,7 @@ contract EnumDefinitions { - enum Empty {} + enum Empty { + + } enum ActionChoices { GoLeft, GoRight, From 2979b00e155fad08a4e38c663ad82c0f2dd432ff Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:22:37 +0200 Subject: [PATCH 37/54] test: update StructDefinition 2 --- crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol | 4 +++- crates/fmt/testdata/StructDefinition/fmt.sol | 4 +++- crates/fmt/testdata/StructDefinition/original.sol | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol index d178199a57712..354480d0de98c 100644 --- a/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol @@ -1,5 +1,7 @@ // config: bracket_spacing = true -struct Foo {} +struct Foo { + +} struct Bar { uint256 foo; string bar; diff --git a/crates/fmt/testdata/StructDefinition/fmt.sol b/crates/fmt/testdata/StructDefinition/fmt.sol index 84effdf30b032..914e9cc67342d 100644 --- a/crates/fmt/testdata/StructDefinition/fmt.sol +++ b/crates/fmt/testdata/StructDefinition/fmt.sol @@ -1,4 +1,6 @@ -struct Foo {} +struct Foo { + +} struct Bar { uint256 foo; string bar; diff --git a/crates/fmt/testdata/StructDefinition/original.sol b/crates/fmt/testdata/StructDefinition/original.sol index a82d7a92e7d7a..cb3d3e11dbb0b 100644 --- a/crates/fmt/testdata/StructDefinition/original.sol +++ b/crates/fmt/testdata/StructDefinition/original.sol @@ -1,4 +1,5 @@ struct Foo { + } struct Bar { uint foo ;string bar ; } struct MyStruct { From 4f287e734beb371a44deaea32acaba2d58fee9b1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:02:45 +0200 Subject: [PATCH 38/54] fix: comments in structs/enums --- a.sol | 12 ++++---- crates/fmt-2/src/pp/convenience.rs | 4 --- crates/fmt-2/src/state.rs | 49 ++++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/a.sol b/a.sol index c02da7f874f52..389edc97f6850 100644 --- a/a.sol +++ b/a.sol @@ -1,7 +1,7 @@ -contract EnumDefinitions { - enum Empty { - - } - enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } - enum States { State1, State2, State3, State4, State5, State6, State7, State8, State9 } +enum A { + //a + _0, //b + //c + _1 //d + //e } diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 1c248c8f15ab8..e46b1d9db24d5 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -125,8 +125,4 @@ impl Token { false } } - - pub(crate) fn is_if_nonempty(&self) -> bool { - matches!(self, Token::Break(b) if b.if_nonempty) - } } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 5c2ae36a91cba..6d3f192d2895f 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -462,9 +462,9 @@ impl<'ast> State<'_, 'ast> { for var in fields.iter() { self.print_var_def(var); } + self.maybe_print_comments(span.hi()); self.s.offset(-self.ind); self.end(); - self.maybe_print_comments(span.hi()); self.word("}"); self.hardbreak(); } @@ -476,11 +476,15 @@ impl<'ast> State<'_, 'ast> { self.hardbreak_if_nonempty(); for (pos, ident) in variants.iter().delimited() { self.print_ident(*ident); - self.trailing_comma(pos.is_last); + if !pos.is_last { + self.word(","); + } + self.maybe_print_trailing_comment(ident.span, None); + self.hardbreak_if_not_bol(); } + self.maybe_print_comments(span.hi()); self.s.offset(-self.ind); self.end(); - self.maybe_print_comments(span.hi()); self.word("}"); self.hardbreak(); } @@ -619,7 +623,7 @@ impl<'ast> State<'_, 'ast> { self.print_var(var); self.word(";"); self.maybe_print_trailing_comment(var.span, None); - self.hardbreak(); + self.hardbreak_if_not_bol(); } fn print_var(&mut self, var: &'ast ast::VariableDefinition<'ast>) { @@ -1109,16 +1113,16 @@ impl<'ast> State<'_, 'ast> { #[expect(unused_variables)] fn print_stmt(&mut self, stmt: &'ast ast::Stmt<'ast>) { // TODO(dani) - let ast::Stmt { docs, span, kind } = stmt; + let &ast::Stmt { ref docs, span, ref kind } = stmt; self.print_docs(docs); - if self.handle_span(*span) { + if self.handle_span(span) { return; } match kind { ast::StmtKind::Assembly(stmt_assembly) => todo!(), ast::StmtKind::DeclSingle(variable_definition) => todo!(), ast::StmtKind::DeclMulti(variable_definitions, expr) => todo!(), - ast::StmtKind::Block(stmts) => todo!(), + ast::StmtKind::Block(stmts) => self.print_block(stmts, span), ast::StmtKind::Break => self.word("break"), ast::StmtKind::Continue => self.word("continue"), ast::StmtKind::DoWhile(stmt, expr) => todo!(), @@ -1149,26 +1153,45 @@ impl<'ast> State<'_, 'ast> { self.maybe_print_trailing_comment(stmt.span, None); } + // Body of a if/loop. + fn print_stmt_as_block(&mut self, stmt: &'ast ast::Stmt<'ast>, attempt_single_line: bool) { + if let ast::StmtKind::Block(stmts) = &stmt.kind { + self.print_block_inner(stmts, stmt.span, attempt_single_line, true) + } else { + self.print_block_inner(std::slice::from_ref(stmt), stmt.span, attempt_single_line, true) + } + } + fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { - // TODO: attempt_single_line, attempt_omit_braces + self.print_block_inner(block, span, false, false); + } + + fn print_block_inner( + &mut self, + block: &'ast [ast::Stmt<'ast>], + span: Span, + attempt_single_line: bool, + attempt_omit_braces: bool, + ) { + // TODO(dani): might need to adjust span for `single_line_block` to include the if condition + // TODO(dani): attempt_omit_braces + let _ = attempt_omit_braces; self.word("{"); - if block.is_empty() { - self.maybe_print_comments(span.hi()); - } else if self.single_line_block(block, span) { + if attempt_single_line && self.single_line_block(block, span) { self.space(); self.print_stmt(&block[0]); self.maybe_print_comments(span.hi()); self.space(); } else { self.s.cbox(self.ind); - self.hardbreak(); + self.hardbreak_if_nonempty(); for stmt in block { self.print_stmt(stmt); self.hardbreak_if_not_bol(); } + self.maybe_print_comments(span.hi()); self.s.offset(-self.ind); self.end(); - self.maybe_print_comments(span.hi()); } self.word("}"); self.hardbreak(); From dc4758407e125a6f6d06ac62506c36a16d6e2584 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:03:13 +0200 Subject: [PATCH 39/54] test: update ErrorDefinition; matches Solidity style guide --- crates/fmt/testdata/ErrorDefinition/fmt.sol | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/fmt/testdata/ErrorDefinition/fmt.sol b/crates/fmt/testdata/ErrorDefinition/fmt.sol index b94bbe45d52b0..0c4ff092df36d 100644 --- a/crates/fmt/testdata/ErrorDefinition/fmt.sol +++ b/crates/fmt/testdata/ErrorDefinition/fmt.sol @@ -4,7 +4,14 @@ error TopLevelCustomError(); error TopLevelCustomErrorWithArg(uint256 x); error TopLevelCustomErrorArgWithoutName(string); error Error1( - uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256 + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 ); contract Errors { From cf5e82638aa0c41e3b00e85fda7f5cbfd2274471 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:26:16 +0200 Subject: [PATCH 40/54] feat: print docs with other comments; update EnumVariants --- crates/fmt-2/src/comment.rs | 1 + crates/fmt-2/src/comments.rs | 58 +++++++++++----------- crates/fmt-2/src/state.rs | 61 ++++++++++++------------ crates/fmt/testdata/EnumVariants/fmt.sol | 4 +- 4 files changed, 61 insertions(+), 63 deletions(-) diff --git a/crates/fmt-2/src/comment.rs b/crates/fmt-2/src/comment.rs index aab3116191eac..6d6d7e57d64de 100644 --- a/crates/fmt-2/src/comment.rs +++ b/crates/fmt-2/src/comment.rs @@ -19,6 +19,7 @@ pub enum CommentStyle { #[derive(Clone, Debug)] pub struct Comment { + pub is_doc: bool, pub style: CommentStyle, pub lines: Vec, pub span: Span, diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index cddddca9a5a30..b2d8e694376f5 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -101,6 +101,7 @@ fn gather_comments(sf: &SourceFile) -> Vec { idx += 1 + next_newline; let pos = pos + idx; comments.push(Comment { + is_doc: false, style: CommentStyle::BlankLine, lines: vec![], span: make_span(pos..pos), @@ -109,39 +110,34 @@ fn gather_comments(sf: &SourceFile) -> Vec { } } TokenKind::BlockComment { is_doc, .. } => { - if !is_doc { - let code_to_the_right = !matches!( - text[pos + token.len as usize..].chars().next(), - Some('\r' | '\n') - ); - let style = match (code_to_the_left, code_to_the_right) { - (_, true) => CommentStyle::Mixed, - (false, false) => CommentStyle::Isolated, - (true, false) => CommentStyle::Trailing, - }; - - // Count the number of chars since the start of the line by rescanning. - let pos_in_file = start_bpos + BytePos(pos as u32); - let line_begin_in_file = line_begin_pos(sf, pos_in_file); - let line_begin_pos = (line_begin_in_file - start_bpos).to_usize(); - let col = CharPos(text[line_begin_pos..pos].chars().count()); - - let lines = split_block_comment_into_lines(token_text, col); - comments.push(Comment { style, lines, span }) - } + let code_to_the_right = + !matches!(text[pos + token.len as usize..].chars().next(), Some('\r' | '\n')); + let style = match (code_to_the_left, code_to_the_right) { + (_, true) => CommentStyle::Mixed, + (false, false) => CommentStyle::Isolated, + (true, false) => CommentStyle::Trailing, + }; + + // Count the number of chars since the start of the line by rescanning. + let pos_in_file = start_bpos + BytePos(pos as u32); + let line_begin_in_file = line_begin_pos(sf, pos_in_file); + let line_begin_pos = (line_begin_in_file - start_bpos).to_usize(); + let col = CharPos(text[line_begin_pos..pos].chars().count()); + + let lines = split_block_comment_into_lines(token_text, col); + comments.push(Comment { is_doc, style, lines, span }) } TokenKind::LineComment { is_doc } => { - if !is_doc { - comments.push(Comment { - style: if code_to_the_left { - CommentStyle::Trailing - } else { - CommentStyle::Isolated - }, - lines: vec![token_text.to_string()], - span, - }) - } + comments.push(Comment { + is_doc, + style: if code_to_the_left { + CommentStyle::Trailing + } else { + CommentStyle::Isolated + }, + lines: vec![token_text.to_string()], + span, + }); } _ => { code_to_the_left = true; diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 6d3f192d2895f..dd34dd6d1fd74 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -74,13 +74,24 @@ impl<'sess> State<'sess, '_> { /// /// Returns `Some` with the style of the last comment printed, or `None` if no comment was /// printed. - fn maybe_print_comments(&mut self, pos: BytePos) -> Option { + fn print_comments(&mut self, pos: BytePos) -> Option { + self.print_comments_inner(pos, false) + } + + fn print_comments_skip_ws(&mut self, pos: BytePos) -> Option { + self.print_comments_inner(pos, true) + } + + fn print_comments_inner(&mut self, pos: BytePos, skip_ws: bool) -> Option { let mut has_comment = None; while let Some(cmnt) = self.peek_comment() { if cmnt.pos() >= pos { break; } let cmnt = self.next_comment().unwrap(); + if skip_ws && cmnt.style == CommentStyle::BlankLine { + continue; + } has_comment = Some(cmnt.style); self.print_comment(cmnt); } @@ -189,7 +200,7 @@ impl<'sess> State<'sess, '_> { } fn bclose_maybe_open(&mut self, span: Span, empty: bool, close_box: bool) { - let comment = self.maybe_print_comments(span.hi()); + let comment = self.print_comments(span.hi()); if !empty || comment.is_some() { self.break_offset_if_not_bol(1, -self.ind); } @@ -238,7 +249,7 @@ impl State<'_, '_> { /// Returns `true` if the span is disabled and has been printed as-is. #[must_use] fn handle_span(&mut self, span: Span) -> bool { - self.maybe_print_comments(span.lo()); + self.print_comments(span.lo()); self.print_span_if_disabled(span) } @@ -301,7 +312,7 @@ impl<'ast> State<'_, 'ast> { fn print_item(&mut self, item: &'ast ast::Item<'ast>) { let ast::Item { docs, span, kind } = item; self.print_docs(docs); - self.maybe_print_comments(span.lo()); + self.print_comments(span.lo()); match kind { ast::ItemKind::Pragma(ast::PragmaDirective { tokens }) => { self.word("pragma "); @@ -429,19 +440,19 @@ impl<'ast> State<'_, 'ast> { self.word("{"); if !body.is_empty() { self.hardbreak(); - let comment = self.maybe_print_comments(body[0].span.lo()); + let comment = self.print_comments(body[0].span.lo()); if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { self.hardbreak(); } for item in body.iter() { self.print_item(item); } - let comment = self.maybe_print_comments(span.hi()); + let comment = self.print_comments(span.hi()); if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { self.hardbreak(); } } else { - self.maybe_print_comments(span.hi()); + self.print_comments(span.hi()); self.zerobreak(); } self.s.offset(-self.ind); @@ -462,7 +473,7 @@ impl<'ast> State<'_, 'ast> { for var in fields.iter() { self.print_var_def(var); } - self.maybe_print_comments(span.hi()); + self.print_comments_skip_ws(span.hi()); self.s.offset(-self.ind); self.end(); self.word("}"); @@ -482,7 +493,7 @@ impl<'ast> State<'_, 'ast> { self.maybe_print_trailing_comment(ident.span, None); self.hardbreak_if_not_bol(); } - self.maybe_print_comments(span.hi()); + self.print_comments_skip_ws(span.hi()); self.s.offset(-self.ind); self.end(); self.word("}"); @@ -697,18 +708,8 @@ impl<'ast> State<'_, 'ast> { } fn print_docs(&mut self, docs: &'ast ast::DocComments<'ast>) { - for &ast::DocComment { kind, span, symbol } in docs.iter() { - self.maybe_print_comments(span.lo()); - self.word(match kind { - ast::CommentKind::Line => { - format!("///{symbol}") - } - ast::CommentKind::Block => { - format!("/**{symbol}*/") - } - }); - self.hardbreak(); - } + // Handled with `self.comments`. + let _ = docs; } fn print_ident_or_strlit(&mut self, value: &'ast ast::IdentOrStrLit) { @@ -725,7 +726,7 @@ impl<'ast> State<'_, 'ast> { } fn print_ident(&mut self, ident: ast::Ident) { - self.maybe_print_comments(ident.span.lo()); + self.print_comments(ident.span.lo()); self.word(ident.to_string()); } @@ -863,7 +864,7 @@ impl<'ast> State<'_, 'ast> { /// `s` should be the *unescaped contents of the string literal*. fn print_str_lit(&mut self, kind: ast::StrKind, quote_pos: BytePos, s: &str) { - self.maybe_print_comments(quote_pos); + self.print_comments(quote_pos); let s = self.str_lit_to_string(kind, quote_pos, s); self.word(s); } @@ -1149,10 +1150,14 @@ impl<'ast> State<'_, 'ast> { if stmt_needs_semi(kind) { self.word(";"); } - self.maybe_print_comments(stmt.span.hi()); + self.print_comments(stmt.span.hi()); self.maybe_print_trailing_comment(stmt.span, None); } + fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { + self.print_block_inner(block, span, false, false); + } + // Body of a if/loop. fn print_stmt_as_block(&mut self, stmt: &'ast ast::Stmt<'ast>, attempt_single_line: bool) { if let ast::StmtKind::Block(stmts) = &stmt.kind { @@ -1162,10 +1167,6 @@ impl<'ast> State<'_, 'ast> { } } - fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { - self.print_block_inner(block, span, false, false); - } - fn print_block_inner( &mut self, block: &'ast [ast::Stmt<'ast>], @@ -1180,7 +1181,7 @@ impl<'ast> State<'_, 'ast> { if attempt_single_line && self.single_line_block(block, span) { self.space(); self.print_stmt(&block[0]); - self.maybe_print_comments(span.hi()); + self.print_comments(span.hi()); self.space(); } else { self.s.cbox(self.ind); @@ -1189,7 +1190,7 @@ impl<'ast> State<'_, 'ast> { self.print_stmt(stmt); self.hardbreak_if_not_bol(); } - self.maybe_print_comments(span.hi()); + self.print_comments_skip_ws(span.hi()); self.s.offset(-self.ind); self.end(); } diff --git a/crates/fmt/testdata/EnumVariants/fmt.sol b/crates/fmt/testdata/EnumVariants/fmt.sol index b33b8846984d2..3f1c59b8f2595 100644 --- a/crates/fmt/testdata/EnumVariants/fmt.sol +++ b/crates/fmt/testdata/EnumVariants/fmt.sol @@ -10,8 +10,8 @@ interface I { /// A modification applied to either `msg.sender` or `tx.origin`. Returned by `readCallers`. enum CallerMode2 { /// No caller modification is currently active. - None, - /// No caller modification is currently active2. + None, /// No caller modification is currently active2. + Some } From 91b608a2f589c89182082adee14e0a52dae9f587 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:39:38 +0200 Subject: [PATCH 41/54] chore: update EnumDefinition, StructDefinition --- crates/fmt-2/src/state.rs | 6 ++---- crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol | 4 +--- crates/fmt/testdata/EnumDefinition/fmt.sol | 4 +--- .../fmt/testdata/StructDefinition/bracket-spacing.fmt.sol | 4 +--- crates/fmt/testdata/StructDefinition/fmt.sol | 4 +--- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index dd34dd6d1fd74..f47a26a8690c0 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -13,8 +13,6 @@ use solar_parse::{ }; use std::borrow::Cow; -// TODO(dani): bunch docs into `Comments` since misplaced docs get ignore - pub(super) struct State<'sess, 'ast> { pub(crate) s: pp::Printer, ind: isize, @@ -1177,13 +1175,13 @@ impl<'ast> State<'_, 'ast> { // TODO(dani): might need to adjust span for `single_line_block` to include the if condition // TODO(dani): attempt_omit_braces let _ = attempt_omit_braces; - self.word("{"); if attempt_single_line && self.single_line_block(block, span) { self.space(); self.print_stmt(&block[0]); self.print_comments(span.hi()); self.space(); } else { + self.word("{"); self.s.cbox(self.ind); self.hardbreak_if_nonempty(); for stmt in block { @@ -1193,8 +1191,8 @@ impl<'ast> State<'_, 'ast> { self.print_comments_skip_ws(span.hi()); self.s.offset(-self.ind); self.end(); + self.word("}"); } - self.word("}"); self.hardbreak(); } diff --git a/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol index 9d1a8091d0ffb..10483822a86b8 100644 --- a/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/EnumDefinition/bracket-spacing.fmt.sol @@ -1,8 +1,6 @@ // config: bracket_spacing = true contract EnumDefinitions { - enum Empty { - - } + enum Empty {} enum ActionChoices { GoLeft, GoRight, diff --git a/crates/fmt/testdata/EnumDefinition/fmt.sol b/crates/fmt/testdata/EnumDefinition/fmt.sol index bf364c34df9e3..437268aff6423 100644 --- a/crates/fmt/testdata/EnumDefinition/fmt.sol +++ b/crates/fmt/testdata/EnumDefinition/fmt.sol @@ -1,7 +1,5 @@ contract EnumDefinitions { - enum Empty { - - } + enum Empty {} enum ActionChoices { GoLeft, GoRight, diff --git a/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol b/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol index 354480d0de98c..d178199a57712 100644 --- a/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol +++ b/crates/fmt/testdata/StructDefinition/bracket-spacing.fmt.sol @@ -1,7 +1,5 @@ // config: bracket_spacing = true -struct Foo { - -} +struct Foo {} struct Bar { uint256 foo; string bar; diff --git a/crates/fmt/testdata/StructDefinition/fmt.sol b/crates/fmt/testdata/StructDefinition/fmt.sol index 914e9cc67342d..84effdf30b032 100644 --- a/crates/fmt/testdata/StructDefinition/fmt.sol +++ b/crates/fmt/testdata/StructDefinition/fmt.sol @@ -1,6 +1,4 @@ -struct Foo { - -} +struct Foo {} struct Bar { uint256 foo; string bar; From 15bbbf2eebf31c971518b35f043b3d26f650438e Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:34:25 +0200 Subject: [PATCH 42/54] chore: readd post_break --- crates/fmt-2/src/pp/convenience.rs | 1 + crates/fmt-2/src/pp/mod.rs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index e46b1d9db24d5..97dd24f2d72c5 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -116,6 +116,7 @@ impl Token { offset, blank_space, pre_break: _, + post_break: _, if_nonempty: _, never_break, }) = *self diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index f98b65c081905..c082114c08cf6 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -41,11 +41,12 @@ enum IndentStyle { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub(crate) struct BreakToken { - offset: isize, - blank_space: usize, - pre_break: Option, - if_nonempty: bool, - never_break: bool, + pub(crate) offset: isize, + pub(crate) blank_space: usize, + pub(crate) pre_break: Option, + pub(crate) post_break: Option, + pub(crate) if_nonempty: bool, + pub(crate) never_break: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -191,7 +192,7 @@ impl Printer { } } - fn scan_break(&mut self, token: BreakToken) { + pub(crate) fn scan_break(&mut self, token: BreakToken) { if self.scan_stack.is_empty() { self.left_total = 1; self.right_total = 1; @@ -369,6 +370,11 @@ impl Printer { let indent = self.indent as isize + token.offset; self.pending_indentation = usize::try_from(indent).unwrap(); self.space = cmp::max(self.margin - indent, MIN_SPACE); + if let Some(post_break) = token.post_break { + self.print_indent(); + self.out.push(post_break); + self.space -= post_break.len_utf8() as isize; + } } } From d3318969a015e56b2892cabca3f17d66d855742c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 28 Apr 2025 23:37:31 +0200 Subject: [PATCH 43/54] chore: rename is_hardbreak_tok --- .gitignore | 1 + a.sol | 7 ------- crates/fmt-2/src/pp/convenience.rs | 4 ++-- crates/fmt-2/src/pp/mod.rs | 2 +- crates/fmt-2/src/state.rs | 2 +- 5 files changed, 5 insertions(+), 11 deletions(-) delete mode 100644 a.sol diff --git a/.gitignore b/.gitignore index 9297bbbc7d4f2..fe8e13360dff4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_STORE /target* +/*.sol out/ snapshots/ out.json diff --git a/a.sol b/a.sol deleted file mode 100644 index 389edc97f6850..0000000000000 --- a/a.sol +++ /dev/null @@ -1,7 +0,0 @@ -enum A { - //a - _0, //b - //c - _1 //d - //e -} diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 97dd24f2d72c5..06dab55d452cf 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -56,7 +56,7 @@ impl Printer { pub fn is_beginning_of_line(&self) -> bool { match self.last_token() { - Some(last_token) => last_token.is_hardbreak_tok(), + Some(last_token) => last_token.is_hardbreak(), None => true, } } @@ -111,7 +111,7 @@ impl Printer { } impl Token { - pub(crate) fn is_hardbreak_tok(&self) -> bool { + pub(crate) fn is_hardbreak(&self) -> bool { if let Token::Break(BreakToken { offset, blank_space, diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index c082114c08cf6..ec2e4a51ecfb3 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -368,7 +368,7 @@ impl Printer { } self.out.push('\n'); let indent = self.indent as isize + token.offset; - self.pending_indentation = usize::try_from(indent).unwrap(); + self.pending_indentation = usize::try_from(indent).expect("negative indentation"); self.space = cmp::max(self.margin - indent, MIN_SPACE); if let Some(post_break) = token.post_break { self.print_indent(); diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index f47a26a8690c0..0f827bcca9907 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -218,7 +218,7 @@ impl<'sess> State<'sess, '_> { self.break_offset(n, off) } else if off != 0 { if let Some(last_token) = self.last_token_still_buffered() { - if last_token.is_hardbreak_tok() { + if last_token.is_hardbreak() { // We do something pretty sketchy here: tuck the nonzero // offset-adjustment we were going to deposit along with the // break into the previous hardbreak. From 2ed2a070bbb68ba2dcf1968f44f6b26cd4c36f2c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:54:38 +0200 Subject: [PATCH 44/54] feat: cleanups, more impls --- crates/fmt-2/src/state.rs | 407 ++++++++++++++++++++++++++------------ 1 file changed, 282 insertions(+), 125 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 0f827bcca9907..1c04d5e2c46fc 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -3,7 +3,7 @@ use super::{ comments::Comments, pp::{self, Token}, }; -use crate::{iter::IterDelimited, FormatterConfig, InlineConfig}; +use crate::{iter::IterDelimited, pp::BreakToken, FormatterConfig, InlineConfig}; use foundry_config::fmt as config; use itertools::{Either, Itertools}; use solar_parse::{ @@ -60,14 +60,6 @@ impl<'sess> State<'sess, '_> { } } - fn comments(&self) -> &Comments { - &self.comments - } - - fn comments_mut(&mut self) -> &mut Comments { - &mut self.comments - } - /// Prints comments that are before the given position. /// /// Returns `Some` with the style of the last comment printed, or `None` if no comment was @@ -168,11 +160,11 @@ impl<'sess> State<'sess, '_> { where 'sess: 'b, { - self.comments().peek() + self.comments.peek() } fn next_comment(&mut self) -> Option { - self.comments_mut().next() + self.comments.next() } fn maybe_print_trailing_comment(&mut self, span: Span, next_pos: Option) { @@ -192,27 +184,6 @@ impl<'sess> State<'sess, '_> { } } - fn bopen(&mut self) { - self.word("{"); - self.end(); // Close the head-box. - } - - fn bclose_maybe_open(&mut self, span: Span, empty: bool, close_box: bool) { - let comment = self.print_comments(span.hi()); - if !empty || comment.is_some() { - self.break_offset_if_not_bol(1, -self.ind); - } - self.word("}"); - if close_box { - self.end(); // Close the outer-box. - } - } - - fn bclose(&mut self, span: Span, empty: bool) { - let close_box = true; - self.bclose_maybe_open(span, empty, close_box) - } - fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { if !self.is_beginning_of_line() { self.break_offset(n, off) @@ -235,6 +206,39 @@ impl<'sess> State<'sess, '_> { self.zerobreak(); } } + + fn print_tuple(&mut self, values: I, print: F) + where + I: IntoIterator, + F: FnMut(&mut Self, T), + { + self.commasep(token::Delimiter::Parenthesis, values, print); + } + + fn commasep(&mut self, delim: token::Delimiter, values: I, mut print: F) + where + I: IntoIterator, + F: FnMut(&mut Self, T), + { + self.word(match delim { + token::Delimiter::Parenthesis => "(", + token::Delimiter::Brace => "{", + token::Delimiter::Bracket => "[", + }); + self.s.cbox(self.ind); + self.zerobreak(); + for (pos, value) in values.into_iter().delimited() { + print(self, value); + self.trailing_comma(pos.is_last); + } + self.s.offset(-self.ind); + self.end(); + self.word(match delim { + token::Delimiter::Parenthesis => ")", + token::Delimiter::Brace => "}", + token::Delimiter::Bracket => "]", + }); + } } /// Span to source. @@ -612,7 +616,7 @@ impl<'ast> State<'_, 'ast> { } if !returns.is_empty() { self.nbsp(); - self.word("returns"); + self.word("returns "); self.print_parameter_list(returns); } if anonymous { @@ -685,24 +689,7 @@ impl<'ast> State<'_, 'ast> { } fn print_parameter_list(&mut self, parameters: &'ast [ast::VariableDefinition<'ast>]) { - if parameters.is_empty() { - self.word("()"); - return; - } - self.s.cbox(self.ind); - self.word("("); - self.zerobreak(); - for (pos, var) in parameters.iter().delimited() { - self.print_var(var); - if !pos.is_last { - self.word(","); - self.space(); - } - } - self.zerobreak(); - self.s.offset(-self.ind); - self.word(")"); - self.end(); + self.print_tuple(parameters, Self::print_var); } fn print_docs(&mut self, docs: &'ast ast::DocComments<'ast>) { @@ -739,7 +726,7 @@ impl<'ast> State<'_, 'ast> { // TODO: Yul literals are slightly different than normal solidity ones fn print_lit(&mut self, lit: &'ast ast::Lit) { - let &ast::Lit { span, symbol, ref kind } = lit; + let ast::Lit { span, symbol, ref kind } = *lit; if self.handle_span(span) { return; } @@ -917,7 +904,10 @@ impl<'ast> State<'_, 'ast> { &ast::TypeKind::Elementary(ty) => 'b: { match ty { // `address payable` is normalized to `address`. - ast::ElementaryType::Address(true) => self.word("address payable"), + ast::ElementaryType::Address(true) => { + self.word("address payable"); + break 'b; + } // Integers are normalized to long form. ast::ElementaryType::Int(size) | ast::ElementaryType::UInt(size) => { match (self.config.int_types, size.bits_raw()) { @@ -948,7 +938,19 @@ impl<'ast> State<'_, 'ast> { self.word("[]"); } } - ast::TypeKind::Function(_func) => todo!(), + ast::TypeKind::Function(ast::TypeFunction { + parameters, + visibility, + state_mutability, + returns, + }) => self.print_function_like(FunctionLike { + kind: "function", + parameters, + visibility: *visibility, + state_mutability: *state_mutability, + returns, + ..Default::default() + }), ast::TypeKind::Mapping(ast::TypeMapping { key, key_name, value, value_name }) => { self.word("mapping("); self.print_ty(key); @@ -997,7 +999,6 @@ impl<'ast> State<'_, 'ast> { /* --- Expressions --- */ - #[expect(unused_variables)] fn print_expr(&mut self, expr: &'ast ast::Expr<'ast>) { let ast::Expr { span, ref kind } = *expr; if self.handle_span(span) { @@ -1005,25 +1006,67 @@ impl<'ast> State<'_, 'ast> { } match kind { - ast::ExprKind::Array(exprs) => { - self.word("["); - self.s.cbox(self.ind); - self.zerobreak(); - for (pos, elem) in exprs.iter().delimited() { - self.print_expr(elem); - self.trailing_comma(pos.is_last); + ast::ExprKind::Array(exprs) => self.commasep( + token::Delimiter::Bracket, + exprs.iter().map(std::ops::Deref::deref), + Self::print_expr, + ), + ast::ExprKind::Assign(lhs, None, rhs) => { + self.ibox(0); + self.print_expr(lhs); + self.word(" = "); + self.neverbreak(); + self.print_expr(rhs); + self.end(); + } + ast::ExprKind::Assign(lhs, Some(bin_op), rhs) | + ast::ExprKind::Binary(lhs, bin_op, rhs) => { + self.s.ibox(self.ind); + self.s.ibox(-self.ind); + self.print_expr(lhs); + self.end(); + self.space(); + if matches!(kind, ast::ExprKind::Assign(..)) { + self.word("="); } - self.s.offset(-self.ind); + self.word_nbsp(bin_op.kind.to_str()); + self.print_expr(rhs); self.end(); - self.word("]"); } - ast::ExprKind::Assign(expr, bin_op, expr1) => todo!(), - ast::ExprKind::Binary(expr, bin_op, expr1) => todo!(), - ast::ExprKind::Call(expr, call_args) => todo!(), - ast::ExprKind::CallOptions(expr, named_args) => todo!(), - ast::ExprKind::Delete(expr) => todo!(), + ast::ExprKind::Call(expr, call_args) => { + self.print_expr(expr); + self.print_call_args(call_args); + } + ast::ExprKind::CallOptions(expr, named_args) => { + self.print_expr(expr); + self.print_named_args(named_args); + } + ast::ExprKind::Delete(expr) => { + self.word("delete "); + self.print_expr(expr); + } ast::ExprKind::Ident(ident) => self.print_ident(*ident), - ast::ExprKind::Index(expr, index_kind) => todo!(), + ast::ExprKind::Index(expr, kind) => { + self.print_expr(expr); + self.word("["); + match kind { + ast::IndexKind::Index(expr) => { + if let Some(expr) = expr { + self.print_expr(expr); + } + } + ast::IndexKind::Range(expr, expr1) => { + if let Some(expr) = expr { + self.print_expr(expr); + } + self.word(":"); + if let Some(expr1) = expr1 { + self.print_expr(expr1); + } + } + } + self.word("]"); + } ast::ExprKind::Lit(lit, unit) => { self.print_lit(lit); if let Some(unit) = unit { @@ -1031,13 +1074,39 @@ impl<'ast> State<'_, 'ast> { self.word(unit.to_str()); } } - ast::ExprKind::Member(expr, ident) => todo!(), - ast::ExprKind::New(_) => todo!(), - ast::ExprKind::Payable(call_args) => todo!(), - ast::ExprKind::Ternary(expr, expr1, expr2) => todo!(), - ast::ExprKind::Tuple(exprs) => todo!(), - ast::ExprKind::TypeCall(_) => todo!(), - ast::ExprKind::Type(_) => todo!(), + ast::ExprKind::Member(expr, ident) => { + self.print_expr(expr); + self.word("."); + self.print_ident(*ident); + } + ast::ExprKind::New(ty) => { + self.word("new "); + self.print_ty(ty); + } + ast::ExprKind::Payable(args) => { + self.word("payable"); + self.print_call_args(args); + } + ast::ExprKind::Ternary(cond, then, els) => { + self.s.cbox(self.ind); + self.print_expr(cond); + self.space(); + self.word("? "); + self.print_expr(then); + self.space(); + self.word(": "); + self.print_expr(els); + } + ast::ExprKind::Tuple(exprs) => self.print_tuple(exprs.iter(), |this, expr| { + if let Some(expr) = expr { + this.print_expr(expr); + } + }), + ast::ExprKind::TypeCall(ty) => { + self.word("type"); + self.print_tuple(std::slice::from_ref(ty), Self::print_ty); + } + ast::ExprKind::Type(ty) => self.print_ty(ty), ast::ExprKind::Unary(un_op, expr) => { let prefix = un_op.kind.is_prefix(); let op = un_op.kind.to_str(); @@ -1067,68 +1136,117 @@ impl<'ast> State<'_, 'ast> { } fn print_call_args(&mut self, args: &'ast ast::CallArgs<'ast>) { - let ast::CallArgs { span, kind } = args; - if self.handle_span(*span) { + let ast::CallArgs { span, ref kind } = *args; + if self.handle_span(span) { return; } - self.s.cbox(self.ind); - self.word("("); match kind { ast::CallArgsKind::Unnamed(exprs) => { - self.zerobreak(); - for (pos, expr) in exprs.iter().delimited() { - self.print_expr(expr); - if !pos.is_last { - self.word(","); - self.space(); - } - } - self.zerobreak(); + self.print_tuple(exprs.iter().map(std::ops::Deref::deref), Self::print_expr); } ast::CallArgsKind::Named(named_args) => { - self.word("{"); - self.braces_break(); - for (pos, ast::NamedArg { name, value }) in named_args.iter().delimited() { - self.print_ident(*name); - self.word(": "); - self.print_expr(value); - if !pos.is_last { - self.word(","); - self.space(); - } - } + self.word("("); + self.print_named_args(named_args); + self.word(")"); + } + } + } + + fn print_named_args(&mut self, args: &'ast [ast::NamedArg<'ast>]) { + self.word("{"); + self.s.cbox(self.ind); + self.braces_break(); + for (pos, ast::NamedArg { name, value }) in args.iter().delimited() { + self.print_ident(*name); + self.word(": "); + self.print_expr(value); + if pos.is_last { self.braces_break(); - self.word("}"); + } else { + self.word(","); + self.space(); } } self.s.offset(-self.ind); self.end(); - self.word(")"); + self.word("}"); } /* --- Statements --- */ #[expect(unused_variables)] fn print_stmt(&mut self, stmt: &'ast ast::Stmt<'ast>) { - // TODO(dani) - let &ast::Stmt { ref docs, span, ref kind } = stmt; + let ast::Stmt { ref docs, span, ref kind } = *stmt; self.print_docs(docs); if self.handle_span(span) { return; } match kind { - ast::StmtKind::Assembly(stmt_assembly) => todo!(), - ast::StmtKind::DeclSingle(variable_definition) => todo!(), - ast::StmtKind::DeclMulti(variable_definitions, expr) => todo!(), + ast::StmtKind::Assembly(asm) => todo!(), + ast::StmtKind::DeclSingle(var) => self.print_var(var), + ast::StmtKind::DeclMulti(vars, expr) => { + self.print_tuple(vars.iter(), |this, var| { + if let Some(var) = var { + this.print_var(var); + } + }); + self.word(" = "); + self.neverbreak(); + self.print_expr(expr); + } ast::StmtKind::Block(stmts) => self.print_block(stmts, span), ast::StmtKind::Break => self.word("break"), ast::StmtKind::Continue => self.word("continue"), - ast::StmtKind::DoWhile(stmt, expr) => todo!(), - ast::StmtKind::Emit(path_slice, call_args) => todo!(), + ast::StmtKind::DoWhile(stmt, cond) => { + self.word("do "); + self.print_stmt_as_block(stmt, false); + self.nbsp(); + self.print_if_cond("while", cond); + } + ast::StmtKind::Emit(path, args) => self.print_emit_revert("emit", path, args), ast::StmtKind::Expr(expr) => self.print_expr(expr), - ast::StmtKind::For { init, cond, next, body } => todo!(), - ast::StmtKind::If(expr, stmt, stmt1) => todo!(), + ast::StmtKind::For { init, cond, next, body } => { + self.word("for ("); + self.s.cbox(self.ind); + self.zerobreak(); + if let Some(init) = init { + self.print_stmt(init); + } else { + self.word(";"); + } + if let Some(cond) = cond { + self.space(); + self.print_expr(cond); + } else { + self.zerobreak(); + } + self.word(";"); + if let Some(next) = next { + self.space(); + self.print_expr(next); + } + self.zerobreak(); + self.s.offset(-self.ind); + self.end(); + self.word(") "); + self.print_stmt_as_block(body, false); + } + ast::StmtKind::If(cond, then, els) => { + self.s.cbox(0); + + self.print_if_cond("if", cond); + self.nbsp(); + + self.print_stmt_as_block(then, true); + if let Some(els) = els { + self.word("else "); + self.print_stmt_as_block(els, true); + todo!() + } + + self.end(); + } ast::StmtKind::Return(expr) => { self.word("return"); if let Some(expr) = expr { @@ -1136,13 +1254,16 @@ impl<'ast> State<'_, 'ast> { self.print_expr(expr); } } - ast::StmtKind::Revert(path_slice, call_args) => todo!(), + ast::StmtKind::Revert(path, args) => self.print_emit_revert("revert", path, args), ast::StmtKind::Try(stmt_try) => todo!(), ast::StmtKind::UncheckedBlock(block) => { self.word("unchecked "); self.print_block(block, stmt.span); } - ast::StmtKind::While(expr, stmt) => todo!(), + ast::StmtKind::While(cond, stmt) => { + self.print_if_cond("while", cond); + self.print_stmt_as_block(stmt, true); + } ast::StmtKind::Placeholder => self.word("_"), } if stmt_needs_semi(kind) { @@ -1152,17 +1273,42 @@ impl<'ast> State<'_, 'ast> { self.maybe_print_trailing_comment(stmt.span, None); } + fn print_if_cond(&mut self, kw: &'static str, cond: &'ast ast::Expr<'ast>) { + self.word(kw); + self.word(" ("); + self.s.cbox(self.ind); + self.zerobreak(); + self.print_expr(cond); + self.zerobreak(); + self.s.offset(-self.ind); + self.end(); + self.word(")"); + } + + fn print_emit_revert( + &mut self, + kw: &'static str, + path: &'ast ast::PathSlice, + args: &'ast ast::CallArgs<'ast>, + ) { + self.word_nbsp(kw); + self.print_path(path); + self.print_call_args(args); + } + fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { self.print_block_inner(block, span, false, false); + self.hardbreak(); } // Body of a if/loop. fn print_stmt_as_block(&mut self, stmt: &'ast ast::Stmt<'ast>, attempt_single_line: bool) { - if let ast::StmtKind::Block(stmts) = &stmt.kind { - self.print_block_inner(stmts, stmt.span, attempt_single_line, true) + let stmts = if let ast::StmtKind::Block(stmts) = &stmt.kind { + stmts } else { - self.print_block_inner(std::slice::from_ref(stmt), stmt.span, attempt_single_line, true) - } + std::slice::from_ref(stmt) + }; + self.print_block_inner(stmts, stmt.span, attempt_single_line, true) } fn print_block_inner( @@ -1173,27 +1319,38 @@ impl<'ast> State<'_, 'ast> { attempt_omit_braces: bool, ) { // TODO(dani): might need to adjust span for `single_line_block` to include the if condition - // TODO(dani): attempt_omit_braces - let _ = attempt_omit_braces; if attempt_single_line && self.single_line_block(block, span) { - self.space(); + self.s.cbox(self.ind); + if attempt_omit_braces { + self.scan_break(BreakToken { pre_break: Some('{'), ..Default::default() }); + } else { + self.word("{"); + self.space(); + } self.print_stmt(&block[0]); self.print_comments(span.hi()); - self.space(); + if attempt_omit_braces { + self.s.scan_break(BreakToken { post_break: Some('}'), ..Default::default() }); + self.s.offset(-self.ind); + } else { + self.space(); + self.s.offset(-self.ind); + self.word("}"); + } + self.end(); } else { self.word("{"); self.s.cbox(self.ind); self.hardbreak_if_nonempty(); for stmt in block { self.print_stmt(stmt); - self.hardbreak_if_not_bol(); + self.hardbreak(); } self.print_comments_skip_ws(span.hi()); self.s.offset(-self.ind); self.end(); self.word("}"); } - self.hardbreak(); } fn single_line_block(&self, block: &'ast [ast::Stmt<'_>], span: Span) -> bool { @@ -1201,7 +1358,7 @@ impl<'ast> State<'_, 'ast> { return false; } match self.config.single_line_statement_blocks { - config::SingleLineBlockStyle::Preserve => self.sm.is_multiline(span), + config::SingleLineBlockStyle::Preserve => !self.sm.is_multiline(span), config::SingleLineBlockStyle::Single => true, config::SingleLineBlockStyle::Multi => false, } From 8b350b978fe58494e4c995deff183e205bbe8c64 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Wed, 30 Apr 2025 23:58:00 +0200 Subject: [PATCH 45/54] test: fix some compile errors --- .../fmt/testdata/FunctionDefinition/original.sol | 4 ++-- .../fmt/testdata/VariableDefinition/original.sol | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/fmt/testdata/FunctionDefinition/original.sol b/crates/fmt/testdata/FunctionDefinition/original.sol index 97db649d55660..5ae2c3fdd92ce 100644 --- a/crates/fmt/testdata/FunctionDefinition/original.sol +++ b/crates/fmt/testdata/FunctionDefinition/original.sol @@ -91,10 +91,10 @@ interface FunctionInterfaces { } contract FunctionDefinitions { - function () external {} + function f() external {} fallback () external {} - function () external payable {} + function f() external payable {} fallback () external payable {} receive () external payable {} diff --git a/crates/fmt/testdata/VariableDefinition/original.sol b/crates/fmt/testdata/VariableDefinition/original.sol index bd15a6384601b..f00c82649621b 100644 --- a/crates/fmt/testdata/VariableDefinition/original.sol +++ b/crates/fmt/testdata/VariableDefinition/original.sol @@ -1,16 +1,16 @@ contract Contract { - bytes32 constant private BYTES; - bytes32 private constant override (Base1) BYTES; - bytes32 private constant override (Base1, Base2) BYTES; - bytes32 private constant override immutable BYTES; - bytes32 private constant override immutable BYTES_VERY_VERY_VERY_LONG; - bytes32 private constant override(Base1, Base2, SomeLongBaseContract, AndAnotherVeryLongBaseContract, Imported.Contract) BYTES_OVERRIDDEN; + bytes32 constant private BYTES = 0; + bytes32 private constant override (Base1) BYTES = 0; + bytes32 private constant override (Base1, Base2) BYTES = 0; + bytes32 private constant override BYTES = 0; + bytes32 private constant override BYTES_VERY_VERY_VERY_LONG = 0; + bytes32 private constant override(Base1, Base2, SomeLongBaseContract, AndAnotherVeryLongBaseContract, Imported.Contract) BYTES_OVERRIDDEN = 0; bytes32 constant private BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 private constant override immutable BYTES = + bytes32 private constant override BYTES = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; - bytes32 private constant override immutable BYTES_VERY_VERY_VERY_LONG = + bytes32 private constant override BYTES_VERY_VERY_VERY_LONG = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; bytes32 private constant BYTES_VERY_VERY_LONG = 0x035aff83d86937d35b32e04f0ddc6ff469290eef2f1b692d8a815c89404d4749; From 70097420258c4a9cecbfd7dc837e3c9d7cedc648 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 1 May 2025 02:18:47 +0200 Subject: [PATCH 46/54] feat: add FormatterResult with more variants --- Cargo.lock | 15 ++-- crates/fmt-2/Cargo.toml | 2 +- crates/fmt-2/src/comment.rs | 27 +++++- crates/fmt-2/src/comments.rs | 7 +- crates/fmt-2/src/inline_config.rs | 13 ++- crates/fmt-2/src/lib.rs | 131 ++++++++++++++++++++++------- crates/fmt-2/src/main.rs | 19 +++-- crates/fmt-2/src/pp/convenience.rs | 2 +- crates/fmt-2/src/state.rs | 17 +++- crates/fmt-2/tests/formatter.rs | 2 +- 10 files changed, 180 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b7d4cfc11f1d6..a34459c5a81e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3636,7 +3636,6 @@ dependencies = [ "similar-asserts", "snapbox", "solar-parse", - "thiserror 2.0.12", "toml 0.8.21", "tracing", "tracing-subscriber", @@ -8313,7 +8312,7 @@ dependencies = [ [[package]] name = "solar-ast" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" +source = "git+https://github.com/paradigmxyz/solar?branch=main#53ee2ccfa71eac49a8076ed1d3573ac6ef3a1719" dependencies = [ "alloy-primitives 1.0.0", "bumpalo", @@ -8331,7 +8330,7 @@ dependencies = [ [[package]] name = "solar-config" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" +source = "git+https://github.com/paradigmxyz/solar?branch=main#53ee2ccfa71eac49a8076ed1d3573ac6ef3a1719" dependencies = [ "strum 0.27.1", ] @@ -8339,7 +8338,7 @@ dependencies = [ [[package]] name = "solar-data-structures" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" +source = "git+https://github.com/paradigmxyz/solar?branch=main#53ee2ccfa71eac49a8076ed1d3573ac6ef3a1719" dependencies = [ "bumpalo", "index_vec", @@ -8353,7 +8352,7 @@ dependencies = [ [[package]] name = "solar-interface" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" +source = "git+https://github.com/paradigmxyz/solar?branch=main#53ee2ccfa71eac49a8076ed1d3573ac6ef3a1719" dependencies = [ "annotate-snippets", "anstream", @@ -8380,7 +8379,7 @@ dependencies = [ [[package]] name = "solar-macros" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" +source = "git+https://github.com/paradigmxyz/solar?branch=main#53ee2ccfa71eac49a8076ed1d3573ac6ef3a1719" dependencies = [ "proc-macro2", "quote", @@ -8390,7 +8389,7 @@ dependencies = [ [[package]] name = "solar-parse" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" +source = "git+https://github.com/paradigmxyz/solar?branch=main#53ee2ccfa71eac49a8076ed1d3573ac6ef3a1719" dependencies = [ "alloy-primitives 1.0.0", "bitflags 2.9.0", @@ -8410,7 +8409,7 @@ dependencies = [ [[package]] name = "solar-sema" version = "0.1.2" -source = "git+https://github.com/paradigmxyz/solar?branch=main#bb73ebd6587fc1f8405d1e0ae95e23fe79a4a0d5" +source = "git+https://github.com/paradigmxyz/solar?branch=main#53ee2ccfa71eac49a8076ed1d3573ac6ef3a1719" dependencies = [ "alloy-json-abi 1.0.0", "alloy-primitives 1.0.0", diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml index 01114a6af563f..5be214a7ba8f0 100644 --- a/crates/fmt-2/Cargo.toml +++ b/crates/fmt-2/Cargo.toml @@ -19,8 +19,8 @@ solar-parse.workspace = true # alloy-primitives.workspace = true itertools.workspace = true -thiserror.workspace = true tracing.workspace = true +tracing-subscriber.workspace = true [dev-dependencies] itertools.workspace = true diff --git a/crates/fmt-2/src/comment.rs b/crates/fmt-2/src/comment.rs index 6d6d7e57d64de..96e79c8cfaf60 100644 --- a/crates/fmt-2/src/comment.rs +++ b/crates/fmt-2/src/comment.rs @@ -19,16 +19,39 @@ pub enum CommentStyle { #[derive(Clone, Debug)] pub struct Comment { - pub is_doc: bool, - pub style: CommentStyle, pub lines: Vec, pub span: Span, + pub style: CommentStyle, + pub is_doc: bool, + pub kind: CommentKind, } impl Comment { pub fn pos(&self) -> BytePos { self.span.lo() } + + pub fn prefix(&self) -> Option<&'static str> { + if self.lines.is_empty() { + return None; + } + Some(match (self.kind, self.is_doc) { + (CommentKind::Line, false) => "//", + (CommentKind::Line, true) => "///", + (CommentKind::Block, false) => "/*", + (CommentKind::Block, true) => "/**", + }) + } + + pub fn suffix(&self) -> Option<&'static str> { + if self.lines.is_empty() { + return None; + } + match self.kind { + CommentKind::Line => None, + CommentKind::Block => Some("*/"), + } + } } /// A fast conservative estimate on whether the string can contain documentation links. diff --git a/crates/fmt-2/src/comments.rs b/crates/fmt-2/src/comments.rs index b2d8e694376f5..fb13654f811fc 100644 --- a/crates/fmt-2/src/comments.rs +++ b/crates/fmt-2/src/comments.rs @@ -1,6 +1,6 @@ use super::comment::{Comment, CommentStyle}; use solar_parse::{ - ast::Span, + ast::{CommentKind, Span}, interface::{source_map::SourceFile, BytePos, CharPos, SourceMap}, lexer::token::RawTokenKind as TokenKind, }; @@ -102,6 +102,7 @@ fn gather_comments(sf: &SourceFile) -> Vec { let pos = pos + idx; comments.push(Comment { is_doc: false, + kind: CommentKind::Line, style: CommentStyle::BlankLine, lines: vec![], span: make_span(pos..pos), @@ -117,6 +118,7 @@ fn gather_comments(sf: &SourceFile) -> Vec { (false, false) => CommentStyle::Isolated, (true, false) => CommentStyle::Trailing, }; + let kind = CommentKind::Block; // Count the number of chars since the start of the line by rescanning. let pos_in_file = start_bpos + BytePos(pos as u32); @@ -125,11 +127,12 @@ fn gather_comments(sf: &SourceFile) -> Vec { let col = CharPos(text[line_begin_pos..pos].chars().count()); let lines = split_block_comment_into_lines(token_text, col); - comments.push(Comment { is_doc, style, lines, span }) + comments.push(Comment { is_doc, kind, style, lines, span }) } TokenKind::LineComment { is_doc } => { comments.push(Comment { is_doc, + kind: CommentKind::Line, style: if code_to_the_left { CommentStyle::Trailing } else { diff --git a/crates/fmt-2/src/inline_config.rs b/crates/fmt-2/src/inline_config.rs index b61877cd9a483..27a89b0ae51c8 100644 --- a/crates/fmt-2/src/inline_config.rs +++ b/crates/fmt-2/src/inline_config.rs @@ -1,4 +1,3 @@ -use itertools::Itertools; use solar_parse::{ast::Span, lexer::token::RawTokenKind}; use std::{fmt, str::FromStr}; @@ -65,11 +64,21 @@ pub struct InlineConfig { impl InlineConfig { /// Build a new inline config with an iterator of inline config items and their locations in a /// source file. + /// + /// # Panics + /// + /// Panics if `items` is not sorted in ascending order of [`Span`]s. pub fn new(items: impl IntoIterator, src: &str) -> Self { let mut disabled_ranges = vec![]; let mut disabled_range_start = None; let mut disabled_depth = 0usize; - for (sp, item) in items.into_iter().sorted_by_key(|(loc, _)| loc.lo().to_usize()) { + let mut prev_sp = Span::DUMMY; + for (sp, item) in items { + if cfg!(debug_assertions) { + assert!(sp >= prev_sp, "InlineConfig::new: unsorted items: {sp:?} < {prev_sp:?}"); + prev_sp = sp; + } + match item { InlineConfigItem::DisableNextItem => { use RawTokenKind::*; diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 0c44560ac56cb..629c5cafdf55e 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -7,6 +7,7 @@ // #[macro_use] // extern crate tracing; use tracing as _; +use tracing_subscriber as _; pub mod inline_config; pub use inline_config::InlineConfig; @@ -22,65 +23,137 @@ pub(crate) mod iter; mod pp; -use solar_parse::interface::Session; -use std::path::Path; +use solar_parse::interface::{diagnostics::EmittedDiagnostics, source_map::SourceFile, Session}; +use std::{path::Path, sync::Arc}; pub use foundry_config::fmt::*; -type Result = std::result::Result; +/// The result of the formatter. +pub type FormatterResult = DiagnosticsResult; -#[derive(Debug, thiserror::Error)] -pub enum FormatterError { - #[error(transparent)] - Io(#[from] std::io::Error), - #[error("\n{0}")] - Parse(#[from] solar_parse::interface::diagnostics::EmittedDiagnostics), +/// The result of the formatter. +pub enum DiagnosticsResult { + /// Everything went well. + Ok(T), + /// No errors encountered, but warnings or other non-error diagnostics were emitted. + OkWithDiagnostics(T, E), + /// Errors encountered, but a result was produced anyway. + ErrRecovered(T, E), + /// Fatal errors encountered. + Err(E), } -pub fn format_file(path: &Path, config: FormatterConfig) -> Result { - let source = std::fs::read_to_string(path)?; - format_source(&source, Some(path), config) +impl DiagnosticsResult { + /// Converts the formatter result into a standard result. + /// + /// This ignores any non-error diagnostics if `Ok`, and any valid result if `Err`. + pub fn into_result(self) -> Result { + match self { + Self::Ok(s) | Self::OkWithDiagnostics(s, _) => Ok(s), + Self::ErrRecovered(_, d) | Self::Err(d) => Err(d), + } + } + + /// Returns the result, even if it was produced with errors. + pub fn into_ok(self) -> Result { + match self { + Self::Ok(s) | Self::OkWithDiagnostics(s, _) | Self::ErrRecovered(s, _) => Ok(s), + Self::Err(e) => Err(e), + } + } + + /// Returns any result produced. + pub fn ok_ref(&self) -> Option<&T> { + match self { + Self::Ok(s) | Self::OkWithDiagnostics(s, _) | Self::ErrRecovered(s, _) => Some(s), + Self::Err(_) => None, + } + } + + /// Returns any diagnostics emitted. + pub fn err_ref(&self) -> Option<&E> { + match self { + Self::Ok(_) => None, + Self::OkWithDiagnostics(_, d) | Self::ErrRecovered(_, d) | Self::Err(d) => Some(d), + } + } + + /// Returns `true` if the result is `Ok`. + pub fn is_ok(&self) -> bool { + matches!(self, Self::Ok(_) | Self::OkWithDiagnostics(_, _)) + } + + /// Returns `true` if the result is `Err`. + pub fn is_err(&self) -> bool { + !self.is_ok() + } } -pub fn format_source(source: &str, path: Option<&Path>, config: FormatterConfig) -> Result { - let sess = - solar_parse::interface::Session::builder().with_buffer_emitter(Default::default()).build(); - let res = sess.enter(|| -> solar_parse::interface::Result<_> { +pub fn format_file(path: &Path, config: FormatterConfig) -> FormatterResult { + format_inner(config, &|sess| { + sess.source_map().load_file(path).map_err(|e| sess.dcx.err(e.to_string()).emit()) + }) +} + +pub fn format_source( + source: &str, + path: Option<&Path>, + config: FormatterConfig, +) -> FormatterResult { + format_inner(config, &|sess| { let name = match path { Some(path) => solar_parse::interface::source_map::FileName::Real(path.to_path_buf()), - None => solar_parse::interface::source_map::FileName::Custom("fmt".to_string()), + None => solar_parse::interface::source_map::FileName::Stdin, }; - let arena = solar_parse::ast::Arena::new(); - let file = sess - .source_map() + sess.source_map() .new_source_file(name, source) - .map_err(|e| sess.dcx.err(e.to_string()).emit())?; + .map_err(|e| sess.dcx.err(e.to_string()).emit()) + }) +} + +fn format_inner( + config: FormatterConfig, + mk_file: &dyn Fn(&Session) -> solar_parse::interface::Result>, +) -> FormatterResult { + let sess = + solar_parse::interface::Session::builder().with_buffer_emitter(Default::default()).build(); + let res = sess.enter(|| -> solar_parse::interface::Result<_> { + let file = mk_file(&sess)?; + let source = file.src.as_str(); + let arena = solar_parse::ast::Arena::new(); let mut parser = solar_parse::Parser::from_source_file(&sess, &arena, &file); - let ast = parser.parse_file().map_err(|e| e.emit())?; let comments = Comments::new(&file); + let ast = parser.parse_file().map_err(|e| e.emit())?; let inline_config = parse_inline_config(&sess, &comments, source); let mut state = state::State::new(sess.source_map(), config, inline_config, comments); state.print_source_unit(&ast); Ok(state.s.eof()) }); - match (res, sess.emitted_errors().unwrap()) { - (Ok(s), Ok(())) => Ok(s), - // TODO(dani): add a non-fatal error that returns the formatted source with the errors - (Ok(_s), Err(err)) => Err(err.into()), + let diagnostics = sess.emitted_diagnostics().unwrap(); + match (res, sess.dcx.has_errors()) { + (Ok(s), Ok(())) if diagnostics.is_empty() => FormatterResult::Ok(s), + (Ok(s), Ok(())) => FormatterResult::OkWithDiagnostics(s, diagnostics), + (Ok(s), Err(_)) => FormatterResult::ErrRecovered(s, diagnostics), (Err(_), Ok(_)) => unreachable!(), - (Err(_), Err(err)) => Err(err.into()), + (Err(_), Err(_)) => FormatterResult::Err(diagnostics), } } fn parse_inline_config(sess: &Session, comments: &Comments, src: &str) -> InlineConfig { let items = comments.iter().filter_map(|comment| { - let item = comment.lines.first()?.trim_start().strip_prefix("forgefmt:")?.trim(); + let item = comment + .lines + .first()? + .strip_prefix(comment.prefix()?)? + .trim_start() + .strip_prefix("forgefmt:")? + .trim(); let span = comment.span; match item.parse::() { Ok(item) => Some((span, item)), Err(e) => { - let _ = sess.dcx.err(e.to_string()).span(span).emit(); + sess.dcx.warn(e.to_string()).span(span).emit(); None } } diff --git a/crates/fmt-2/src/main.rs b/crates/fmt-2/src/main.rs index 34d840f48e188..e476b4fb6a939 100644 --- a/crates/fmt-2/src/main.rs +++ b/crates/fmt-2/src/main.rs @@ -5,6 +5,10 @@ use std::{io::Read, path::PathBuf}; fn main() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + let args = std::env::args().collect::>(); let (src, path) = if args.len() < 2 || args[1] == "-" { let mut s = String::new(); @@ -15,13 +19,16 @@ fn main() { (std::fs::read_to_string(&path).unwrap(), Some(path)) }; let config = foundry_config::Config::load().unwrap(); - match forge_fmt_2::format_source(&src, path.as_deref(), config.fmt) { - Ok(formatted) => { - print!("{formatted}"); - } - Err(e) => { - eprintln!("failed formatting: {e}"); + let result = forge_fmt_2::format_source(&src, path.as_deref(), config.fmt); + if let Some(formatted) = result.ok_ref() { + print!("{formatted}"); + } + if let Some(diagnostics) = result.err_ref() { + if result.is_err() { + eprintln!("failed formatting:\n{diagnostics}"); std::process::exit(1); + } else { + eprintln!("formatted with output:\n{diagnostics}"); } } } diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 06dab55d452cf..120307dda6b7c 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -112,7 +112,7 @@ impl Printer { impl Token { pub(crate) fn is_hardbreak(&self) -> bool { - if let Token::Break(BreakToken { + if let Self::Break(BreakToken { offset, blank_space, pre_break: _, diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 1c04d5e2c46fc..c41584b3151e1 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -13,6 +13,8 @@ use solar_parse::{ }; use std::borrow::Cow; +// TODO(dani): trailing comments should always be passed Some + pub(super) struct State<'sess, 'ast> { pub(crate) s: pp::Printer, ind: isize, @@ -1096,6 +1098,9 @@ impl<'ast> State<'_, 'ast> { self.space(); self.word(": "); self.print_expr(els); + self.neverbreak(); + self.s.offset(-self.ind); + self.end(); } ast::ExprKind::Tuple(exprs) => self.print_tuple(exprs.iter(), |this, expr| { if let Some(expr) = expr { @@ -1183,7 +1188,10 @@ impl<'ast> State<'_, 'ast> { return; } match kind { - ast::StmtKind::Assembly(asm) => todo!(), + ast::StmtKind::Assembly(asm) => { + // TODO(dani): implement yul + self.word(""); + } ast::StmtKind::DeclSingle(var) => self.print_var(var), ast::StmtKind::DeclMulti(vars, expr) => { self.print_tuple(vars.iter(), |this, var| { @@ -1242,7 +1250,7 @@ impl<'ast> State<'_, 'ast> { if let Some(els) = els { self.word("else "); self.print_stmt_as_block(els, true); - todo!() + // TODO(dani): handle nested else if correctly } self.end(); @@ -1255,7 +1263,10 @@ impl<'ast> State<'_, 'ast> { } } ast::StmtKind::Revert(path, args) => self.print_emit_revert("revert", path, args), - ast::StmtKind::Try(stmt_try) => todo!(), + ast::StmtKind::Try(stmt_try) => { + // TODO(dani): implement try + self.word(""); + } ast::StmtKind::UncheckedBlock(block) => { self.word("unchecked "); self.print_block(block, stmt.span); diff --git a/crates/fmt-2/tests/formatter.rs b/crates/fmt-2/tests/formatter.rs index 94cff73860c5d..d5dac0b381620 100644 --- a/crates/fmt-2/tests/formatter.rs +++ b/crates/fmt-2/tests/formatter.rs @@ -8,7 +8,7 @@ use tracing_subscriber::{EnvFilter, FmtSubscriber}; #[track_caller] fn format(source: &str, path: &Path, config: FormatterConfig) -> String { - match forge_fmt_2::format_source(source, Some(path), config) { + match forge_fmt_2::format_source(source, Some(path), config).into_result() { Ok(formatted) => formatted, Err(e) => panic!("failed to format {path:?}: {e}"), } From ecef2bd5ea6f8b18d25bd15603d943e6441b0c10 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 2 May 2025 20:06:19 +0200 Subject: [PATCH 47/54] stuff --- crates/fmt-2/Cargo.toml | 2 +- crates/fmt-2/src/state.rs | 46 ++++++++++++++------------------------- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml index 5be214a7ba8f0..de4768164efb7 100644 --- a/crates/fmt-2/Cargo.toml +++ b/crates/fmt-2/Cargo.toml @@ -20,7 +20,7 @@ solar-parse.workspace = true # alloy-primitives.workspace = true itertools.workspace = true tracing.workspace = true -tracing-subscriber.workspace = true +tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } [dev-dependencies] itertools.workspace = true diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index c41584b3151e1..698fc911eaed3 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -11,7 +11,7 @@ use solar_parse::{ interface::{BytePos, SourceMap}, Cursor, }; -use std::borrow::Cow; +use std::{borrow::Cow, ops::Deref}; // TODO(dani): trailing comments should always be passed Some @@ -217,6 +217,14 @@ impl<'sess> State<'sess, '_> { self.commasep(token::Delimiter::Parenthesis, values, print); } + fn print_array(&mut self, values: I, print: F) + where + I: IntoIterator, + F: FnMut(&mut Self, T), + { + self.commasep(token::Delimiter::Bracket, values, print); + } + fn commasep(&mut self, delim: token::Delimiter, values: I, mut print: F) where I: IntoIterator, @@ -982,20 +990,7 @@ impl<'ast> State<'_, 'ast> { if self.config.override_spacing { self.nbsp(); } - self.s.cbox(self.ind); - self.word("("); - self.zerobreak(); - for (pos, path) in paths.iter().delimited() { - self.print_path(path); - if !pos.is_last { - self.word(","); - self.space(); - } - } - self.zerobreak(); - self.s.offset(-self.ind); - self.word(")"); - self.end(); + self.print_tuple(paths.iter().map(Deref::deref), Self::print_path); } } @@ -1008,11 +1003,9 @@ impl<'ast> State<'_, 'ast> { } match kind { - ast::ExprKind::Array(exprs) => self.commasep( - token::Delimiter::Bracket, - exprs.iter().map(std::ops::Deref::deref), - Self::print_expr, - ), + ast::ExprKind::Array(exprs) => { + self.print_array(exprs.iter().map(Deref::deref), Self::print_expr) + } ast::ExprKind::Assign(lhs, None, rhs) => { self.ibox(0); self.print_expr(lhs); @@ -1148,7 +1141,7 @@ impl<'ast> State<'_, 'ast> { match kind { ast::CallArgsKind::Unnamed(exprs) => { - self.print_tuple(exprs.iter().map(std::ops::Deref::deref), Self::print_expr); + self.print_tuple(exprs.iter().map(Deref::deref), Self::print_expr); } ast::CallArgsKind::Named(named_args) => { self.word("("); @@ -1285,15 +1278,8 @@ impl<'ast> State<'_, 'ast> { } fn print_if_cond(&mut self, kw: &'static str, cond: &'ast ast::Expr<'ast>) { - self.word(kw); - self.word(" ("); - self.s.cbox(self.ind); - self.zerobreak(); - self.print_expr(cond); - self.zerobreak(); - self.s.offset(-self.ind); - self.end(); - self.word(")"); + self.word_nbsp(kw); + self.print_tuple(std::slice::from_ref(cond), Self::print_expr); } fn print_emit_revert( From 1788a637faa002fb0ac42d7688e8b12bc2bb3af3 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 2 May 2025 20:11:37 +0200 Subject: [PATCH 48/54] refactor: move print_item arms into their own functions --- crates/fmt-2/src/state.rs | 396 ++++++++++++++++++++------------------ 1 file changed, 211 insertions(+), 185 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 698fc911eaed3..d84cbdb14d7a3 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -322,221 +322,226 @@ impl<'ast> State<'_, 'ast> { } fn print_item(&mut self, item: &'ast ast::Item<'ast>) { - let ast::Item { docs, span, kind } = item; + let ast::Item { ref docs, span, ref kind } = *item; self.print_docs(docs); self.print_comments(span.lo()); match kind { - ast::ItemKind::Pragma(ast::PragmaDirective { tokens }) => { - self.word("pragma "); - match tokens { - ast::PragmaTokens::Version(ident, semver_req) => { - self.print_ident(*ident); - self.nbsp(); - self.word(semver_req.to_string()); - } - ast::PragmaTokens::Custom(a, b) => { - self.print_ident_or_strlit(a); - if let Some(b) = b { - self.nbsp(); - self.print_ident_or_strlit(b); - } - } - ast::PragmaTokens::Verbatim(tokens) => { - self.print_tokens(tokens); - } - } - self.word(";"); - self.hardbreak(); + ast::ItemKind::Pragma(pragma) => self.print_pragma(pragma), + ast::ItemKind::Import(import) => self.print_import(import), + ast::ItemKind::Using(using) => self.print_using(using), + ast::ItemKind::Contract(contract) => self.print_contract(contract, span), + ast::ItemKind::Function(func) => self.print_function(func), + ast::ItemKind::Variable(var) => self.print_var_def(var), + ast::ItemKind::Struct(strukt) => self.print_struct(strukt, span), + ast::ItemKind::Enum(enm) => self.print_enum(enm, span), + ast::ItemKind::Udvt(udvt) => self.print_udvt(udvt), + ast::ItemKind::Error(err) => self.print_error(err), + ast::ItemKind::Event(event) => self.print_event(event), + } + } + + fn print_pragma(&mut self, pragma: &'ast ast::PragmaDirective<'ast>) { + self.word("pragma "); + match &pragma.tokens { + ast::PragmaTokens::Version(ident, semver_req) => { + self.print_ident(*ident); + self.nbsp(); + self.word(semver_req.to_string()); } - ast::ItemKind::Import(ast::ImportDirective { path, items }) => { - self.word("import "); - match items { - ast::ImportItems::Plain(_) | ast::ImportItems::Glob(_) => { - self.print_ast_str_lit(path); - if let Some(ident) = items.source_alias() { - self.word(" as "); - self.print_ident(ident); - } - } - ast::ImportItems::Aliases(aliases) => { - self.s.cbox(self.ind); - self.word("{"); - self.braces_break(); - for (pos, (ident, alias)) in aliases.iter().delimited() { - self.print_ident(*ident); - if let Some(alias) = alias { - self.word(" as "); - self.print_ident(*alias); - } - if !pos.is_last { - self.word(","); - self.space(); - } - } - self.braces_break(); - self.s.offset(-self.ind); - self.word("}"); - self.end(); - self.word(" from "); - self.print_ast_str_lit(path); - } + ast::PragmaTokens::Custom(a, b) => { + self.print_ident_or_strlit(a); + if let Some(b) = b { + self.nbsp(); + self.print_ident_or_strlit(b); } - self.word(";"); - self.hardbreak(); } - ast::ItemKind::Using(ast::UsingDirective { list, ty, global }) => { - self.word("using "); - match list { - ast::UsingList::Single(path) => self.print_path(path), - ast::UsingList::Multiple(items) => { - self.s.cbox(self.ind); - self.word("{"); - self.braces_break(); - for (pos, (path, op)) in items.iter().delimited() { - self.print_path(path); - if let Some(op) = op { - self.word(" as "); - self.word(op.to_str()); - } - if !pos.is_last { - self.word(","); - self.space(); - } - } - self.braces_break(); - self.s.offset(-self.ind); - self.word("}"); - self.end(); - } - } - self.word(" for "); - if let Some(ty) = ty { - self.print_ty(ty); - } else { - self.word("*"); - } - if *global { - self.word(" global"); - } - self.word(";"); - self.hardbreak(); + ast::PragmaTokens::Verbatim(tokens) => { + self.print_tokens(tokens); } - ast::ItemKind::Contract(c @ ast::ItemContract { kind, name, layout, bases, body }) => { - self.contract = Some(c); + } + self.word(";"); + self.hardbreak(); + } - self.s.cbox(self.ind); - self.s.cbox(0); - self.word_nbsp(kind.to_str()); - self.print_ident(*name); - self.nbsp(); - if !bases.is_empty() { - self.word("is"); - self.space(); - for (pos, base) in bases.iter().delimited() { - self.print_modifier_call(base, false); - if !pos.is_last { - self.word(","); - self.space(); - } - } - self.space(); - self.s.offset(-self.ind); + fn print_import(&mut self, import: &'ast ast::ImportDirective<'ast>) { + let ast::ImportDirective { path, items } = import; + self.word("import "); + match items { + ast::ImportItems::Plain(_) | ast::ImportItems::Glob(_) => { + self.print_ast_str_lit(path); + if let Some(ident) = items.source_alias() { + self.word(" as "); + self.print_ident(ident); } - self.end(); - if let Some(layout) = layout { - self.word("layout at "); - self.print_expr(layout.slot); - self.space(); - } - + } + ast::ImportItems::Aliases(aliases) => { + self.s.cbox(self.ind); self.word("{"); - if !body.is_empty() { - self.hardbreak(); - let comment = self.print_comments(body[0].span.lo()); - if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { - self.hardbreak(); - } - for item in body.iter() { - self.print_item(item); + self.braces_break(); + for (pos, (ident, alias)) in aliases.iter().delimited() { + self.print_ident(*ident); + if let Some(alias) = alias { + self.word(" as "); + self.print_ident(*alias); } - let comment = self.print_comments(span.hi()); - if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { - self.hardbreak(); + if !pos.is_last { + self.word(","); + self.space(); } - } else { - self.print_comments(span.hi()); - self.zerobreak(); } + self.braces_break(); self.s.offset(-self.ind); - self.end(); self.word("}"); - self.hardbreak(); - - self.contract = None; - } - ast::ItemKind::Function(func) => self.print_function(func), - ast::ItemKind::Variable(var) => self.print_var_def(var), - ast::ItemKind::Struct(ast::ItemStruct { name, fields }) => { - self.s.cbox(self.ind); - self.word("struct "); - self.print_ident(*name); - self.word(" {"); - self.hardbreak_if_nonempty(); - for var in fields.iter() { - self.print_var_def(var); - } - self.print_comments_skip_ws(span.hi()); - self.s.offset(-self.ind); self.end(); - self.word("}"); - self.hardbreak(); + self.word(" from "); + self.print_ast_str_lit(path); } - ast::ItemKind::Enum(ast::ItemEnum { name, variants }) => { + } + self.word(";"); + self.hardbreak(); + } + + fn print_using(&mut self, using: &'ast ast::UsingDirective<'ast>) { + let ast::UsingDirective { list, ty, global } = using; + self.word("using "); + match list { + ast::UsingList::Single(path) => self.print_path(path), + ast::UsingList::Multiple(items) => { self.s.cbox(self.ind); - self.word("enum "); - self.print_ident(*name); - self.word(" {"); - self.hardbreak_if_nonempty(); - for (pos, ident) in variants.iter().delimited() { - self.print_ident(*ident); + self.word("{"); + self.braces_break(); + for (pos, (path, op)) in items.iter().delimited() { + self.print_path(path); + if let Some(op) = op { + self.word(" as "); + self.word(op.to_str()); + } if !pos.is_last { self.word(","); + self.space(); } - self.maybe_print_trailing_comment(ident.span, None); - self.hardbreak_if_not_bol(); } - self.print_comments_skip_ws(span.hi()); + self.braces_break(); self.s.offset(-self.ind); - self.end(); self.word("}"); - self.hardbreak(); + self.end(); } - ast::ItemKind::Udvt(ast::ItemUdvt { name, ty }) => { - self.word("type "); - self.print_ident(*name); - self.word(" is "); - self.print_ty(ty); - self.word(";"); + } + self.word(" for "); + if let Some(ty) = ty { + self.print_ty(ty); + } else { + self.word("*"); + } + if *global { + self.word(" global"); + } + self.word(";"); + self.hardbreak(); + } + + fn print_contract(&mut self, c: &'ast ast::ItemContract<'ast>, span: Span) { + let ast::ItemContract { kind, name, layout, bases, body } = c; + self.contract = Some(c); + + self.s.cbox(self.ind); + self.s.cbox(0); + self.word_nbsp(kind.to_str()); + self.print_ident(*name); + self.nbsp(); + if !bases.is_empty() { + self.word("is"); + self.space(); + for (pos, base) in bases.iter().delimited() { + self.print_modifier_call(base, false); + if !pos.is_last { + self.word(","); + self.space(); + } + } + self.space(); + self.s.offset(-self.ind); + } + self.end(); + if let Some(layout) = layout { + self.word("layout at "); + self.print_expr(layout.slot); + self.space(); + } + + self.word("{"); + if !body.is_empty() { + self.hardbreak(); + let comment = self.print_comments(body[0].span.lo()); + if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { self.hardbreak(); } - ast::ItemKind::Error(ast::ItemError { name, parameters }) => { - self.print_function_like(FunctionLike { - kind: "error", - name: Some(*name), - parameters, - ..Default::default() - }); + for item in body.iter() { + self.print_item(item); } - ast::ItemKind::Event(ast::ItemEvent { name, parameters, anonymous }) => { - self.print_function_like(FunctionLike { - kind: "event", - name: Some(*name), - parameters, - anonymous: *anonymous, - ..Default::default() - }); + let comment = self.print_comments(span.hi()); + if self.config.contract_new_lines && comment != Some(CommentStyle::BlankLine) { + self.hardbreak(); + } + } else { + self.print_comments(span.hi()); + self.zerobreak(); + } + self.s.offset(-self.ind); + self.end(); + self.word("}"); + self.hardbreak(); + + self.contract = None; + } + + fn print_struct(&mut self, strukt: &'ast ast::ItemStruct<'ast>, span: Span) { + let ast::ItemStruct { name, fields } = strukt; + self.s.cbox(self.ind); + self.word("struct "); + self.print_ident(*name); + self.word(" {"); + self.hardbreak_if_nonempty(); + for var in fields.iter() { + self.print_var_def(var); + } + self.print_comments_skip_ws(span.hi()); + self.s.offset(-self.ind); + self.end(); + self.word("}"); + self.hardbreak(); + } + + fn print_enum(&mut self, enm: &'ast ast::ItemEnum<'ast>, span: Span) { + let ast::ItemEnum { name, variants } = enm; + self.s.cbox(self.ind); + self.word("enum "); + self.print_ident(*name); + self.word(" {"); + self.hardbreak_if_nonempty(); + for (pos, ident) in variants.iter().delimited() { + self.print_ident(*ident); + if !pos.is_last { + self.word(","); } + self.maybe_print_trailing_comment(ident.span, None); + self.hardbreak_if_not_bol(); } + self.print_comments_skip_ws(span.hi()); + self.s.offset(-self.ind); + self.end(); + self.word("}"); + self.hardbreak(); + } + + fn print_udvt(&mut self, udvt: &'ast ast::ItemUdvt<'ast>) { + let ast::ItemUdvt { name, ty } = udvt; + self.word("type "); + self.print_ident(*name); + self.word(" is "); + self.print_ty(ty); + self.word(";"); + self.hardbreak(); } fn print_function(&mut self, func: &'ast ast::ItemFunction<'ast>) { @@ -567,6 +572,27 @@ impl<'ast> State<'_, 'ast> { }); } + fn print_error(&mut self, err: &'ast ast::ItemError<'ast>) { + let ast::ItemError { name, parameters } = err; + self.print_function_like(FunctionLike { + kind: "error", + name: Some(*name), + parameters, + ..Default::default() + }); + } + + fn print_event(&mut self, event: &'ast ast::ItemEvent<'ast>) { + let ast::ItemEvent { name, parameters, anonymous } = event; + self.print_function_like(FunctionLike { + kind: "event", + name: Some(*name), + parameters, + anonymous: *anonymous, + ..Default::default() + }); + } + fn print_function_like(&mut self, args: FunctionLike<'ast, 'ast>) { let FunctionLike { kind, From 4a5ff36bb08c35122510b940aac16cdfa1a1349f Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 2 May 2025 22:26:48 +0200 Subject: [PATCH 49/54] chore: consolidate item hardbreaks --- crates/fmt-2/src/state.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index d84cbdb14d7a3..7dc0831bf43e7 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -338,6 +338,9 @@ impl<'ast> State<'_, 'ast> { ast::ItemKind::Error(err) => self.print_error(err), ast::ItemKind::Event(event) => self.print_event(event), } + self.print_comments(span.hi()); + self.maybe_print_trailing_comment(span, None); + self.hardbreak_if_not_bol(); } fn print_pragma(&mut self, pragma: &'ast ast::PragmaDirective<'ast>) { @@ -360,7 +363,6 @@ impl<'ast> State<'_, 'ast> { } } self.word(";"); - self.hardbreak(); } fn print_import(&mut self, import: &'ast ast::ImportDirective<'ast>) { @@ -398,7 +400,6 @@ impl<'ast> State<'_, 'ast> { } } self.word(";"); - self.hardbreak(); } fn print_using(&mut self, using: &'ast ast::UsingDirective<'ast>) { @@ -437,7 +438,6 @@ impl<'ast> State<'_, 'ast> { self.word(" global"); } self.word(";"); - self.hardbreak(); } fn print_contract(&mut self, c: &'ast ast::ItemContract<'ast>, span: Span) { @@ -490,7 +490,6 @@ impl<'ast> State<'_, 'ast> { self.s.offset(-self.ind); self.end(); self.word("}"); - self.hardbreak(); self.contract = None; } @@ -509,7 +508,6 @@ impl<'ast> State<'_, 'ast> { self.s.offset(-self.ind); self.end(); self.word("}"); - self.hardbreak(); } fn print_enum(&mut self, enm: &'ast ast::ItemEnum<'ast>, span: Span) { @@ -531,7 +529,6 @@ impl<'ast> State<'_, 'ast> { self.s.offset(-self.ind); self.end(); self.word("}"); - self.hardbreak(); } fn print_udvt(&mut self, udvt: &'ast ast::ItemUdvt<'ast>) { @@ -541,7 +538,6 @@ impl<'ast> State<'_, 'ast> { self.word(" is "); self.print_ty(ty); self.word(";"); - self.hardbreak(); } fn print_function(&mut self, func: &'ast ast::ItemFunction<'ast>) { @@ -662,9 +658,9 @@ impl<'ast> State<'_, 'ast> { if let Some(body) = body { self.nbsp(); self.print_block(body, body_span); - } else { + } else if !(kind == "function" && name.is_none()) { + // Don't print semicolon if this is a function type. self.word(";"); - self.hardbreak(); } } @@ -1321,7 +1317,6 @@ impl<'ast> State<'_, 'ast> { fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { self.print_block_inner(block, span, false, false); - self.hardbreak(); } // Body of a if/loop. From b9f2cbec30f136a548cd74ef6f28cab2d9ba1356 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 5 May 2025 23:11:03 +0200 Subject: [PATCH 50/54] fix: inline config parsing for block comments --- crates/fmt-2/src/lib.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index 629c5cafdf55e..a9b6d75896452 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -142,13 +142,14 @@ fn format_inner( fn parse_inline_config(sess: &Session, comments: &Comments, src: &str) -> InlineConfig { let items = comments.iter().filter_map(|comment| { - let item = comment - .lines - .first()? - .strip_prefix(comment.prefix()?)? - .trim_start() - .strip_prefix("forgefmt:")? - .trim(); + let mut item = comment.lines.first()?.as_str(); + if let Some(prefix) = comment.prefix() { + item = item.strip_prefix(prefix).unwrap_or(item); + } + if let Some(suffix) = comment.suffix() { + item = item.strip_suffix(suffix).unwrap_or(item); + } + let item = item.trim_start().strip_prefix("forgefmt:")?.trim(); let span = comment.span; match item.parse::() { Ok(item) => Some((span, item)), From bc4093ca66551692eb813b871e8c4e4a2deff01d Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 6 May 2025 00:01:05 +0200 Subject: [PATCH 51/54] wip: rm FunctionLike, wip functions --- crates/fmt-2/src/pp/mod.rs | 9 + crates/fmt-2/src/state.rs | 358 +++++++++++++++++++------------------ 2 files changed, 194 insertions(+), 173 deletions(-) diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index ec2e4a51ecfb3..0b91cbf5071f9 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -225,6 +225,15 @@ impl Printer { } } + pub(crate) fn ends_with(&self, ch: char) -> bool { + for i in self.buf.index_range().rev() { + if let Token::String(token) = &self.buf[i].token { + return token.ends_with(ch); + } + } + self.out.ends_with(ch) + } + fn check_stream(&mut self) { while self.right_total - self.left_total > self.space { if *self.scan_stack.front().unwrap() == self.buf.index_range().start { diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 7dc0831bf43e7..c8425c836e4db 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -11,7 +11,7 @@ use solar_parse::{ interface::{BytePos, SourceMap}, Cursor, }; -use std::{borrow::Cow, ops::Deref}; +use std::borrow::Cow; // TODO(dani): trailing comments should always be passed Some @@ -93,10 +93,10 @@ impl<'sess> State<'sess, '_> { fn print_comment(&mut self, mut cmnt: Comment) { match cmnt.style { CommentStyle::Mixed => { + // TODO(dani): ? if !self.is_beginning_of_line() { - // TODO(dani): ? - // self.zerobreak(); - self.space(); + self.zerobreak(); + // self.space(); } if let Some(last) = cmnt.lines.pop() { self.ibox(0); @@ -169,7 +169,7 @@ impl<'sess> State<'sess, '_> { self.comments.next() } - fn maybe_print_trailing_comment(&mut self, span: Span, next_pos: Option) { + fn print_trailing_comment(&mut self, span: Span, next_pos: Option) { if let Some(cmnt) = self.comments.trailing_comment(self.sm, span, next_pos) { self.print_comment(cmnt); } @@ -209,45 +209,61 @@ impl<'sess> State<'sess, '_> { } } - fn print_tuple(&mut self, values: I, print: F) + fn print_tuple<'a, T, P, S>(&mut self, values: &'a [T], print: P, get_span: S) where - I: IntoIterator, - F: FnMut(&mut Self, T), + P: FnMut(&mut Self, &'a T), + S: FnMut(&T) -> Option, { - self.commasep(token::Delimiter::Parenthesis, values, print); + self.word("("); + self.commasep(values, print, get_span); + self.word(")"); } - fn print_array(&mut self, values: I, print: F) + fn print_array<'a, T, P, S>(&mut self, values: &'a [T], print: P, get_span: S) where - I: IntoIterator, - F: FnMut(&mut Self, T), + P: FnMut(&mut Self, &'a T), + S: FnMut(&T) -> Option, { - self.commasep(token::Delimiter::Bracket, values, print); + self.word("["); + self.commasep(values, print, get_span); + self.word("]"); } - fn commasep(&mut self, delim: token::Delimiter, values: I, mut print: F) + fn commasep<'a, T, P, S>(&mut self, values: &'a [T], mut print: P, mut get_span: S) where - I: IntoIterator, - F: FnMut(&mut Self, T), + P: FnMut(&mut Self, &'a T), + S: FnMut(&T) -> Option, { - self.word(match delim { - token::Delimiter::Parenthesis => "(", - token::Delimiter::Brace => "{", - token::Delimiter::Bracket => "[", - }); + if values.is_empty() { + return; + } + self.s.cbox(self.ind); self.zerobreak(); - for (pos, value) in values.into_iter().delimited() { + for (i, value) in values.iter().enumerate() { + let span = get_span(value); + if let Some(span) = span { + self.print_comments(span.lo()); + } print(self, value); - self.trailing_comma(pos.is_last); + let is_last = i == values.len() - 1; + if !is_last { + self.word(","); + } + if let Some(span) = span { + let next_pos = if is_last { None } else { get_span(&values[i + 1]).map(Span::lo) }; + self.print_trailing_comment(span, next_pos); + } + if !self.is_beginning_of_line() { + if is_last { + self.zerobreak(); + } else { + self.space(); + } + } } self.s.offset(-self.ind); self.end(); - self.word(match delim { - token::Delimiter::Parenthesis => ")", - token::Delimiter::Brace => "}", - token::Delimiter::Bracket => "]", - }); } } @@ -296,20 +312,10 @@ impl State<'_, '_> { } } -#[derive(Default)] -struct FunctionLike<'a, 'b> { - kind: &'static str, - name: Option, - parameters: &'a [ast::VariableDefinition<'b>], - visibility: Option, - state_mutability: ast::StateMutability, - virtual_: bool, - override_: Option<&'a ast::Override<'b>>, - modifiers: &'a [ast::Modifier<'b>], - returns: &'a [ast::VariableDefinition<'b>], - anonymous: bool, - body: Option<&'a [ast::Stmt<'b>]>, - body_span: Span, +#[rustfmt::skip] +macro_rules! get_span { + () => { |value| Some(value.span) }; + (()) => { |value| Some(value.span()) }; } /// Language-specific pretty printing. @@ -339,7 +345,7 @@ impl<'ast> State<'_, 'ast> { ast::ItemKind::Event(event) => self.print_event(event), } self.print_comments(span.hi()); - self.maybe_print_trailing_comment(span, None); + self.print_trailing_comment(span, None); self.hardbreak_if_not_bol(); } @@ -503,6 +509,8 @@ impl<'ast> State<'_, 'ast> { self.hardbreak_if_nonempty(); for var in fields.iter() { self.print_var_def(var); + self.print_trailing_comment(var.span, None); + self.hardbreak_if_not_bol(); } self.print_comments_skip_ws(span.hi()); self.s.offset(-self.ind); @@ -522,7 +530,7 @@ impl<'ast> State<'_, 'ast> { if !pos.is_last { self.word(","); } - self.maybe_print_trailing_comment(ident.span, None); + self.print_trailing_comment(ident.span, None); self.hardbreak_if_not_bol(); } self.print_comments_skip_ws(span.hi()); @@ -541,7 +549,7 @@ impl<'ast> State<'_, 'ast> { } fn print_function(&mut self, func: &'ast ast::ItemFunction<'ast>) { - let ast::ItemFunction { kind, header, body, body_span } = func; + let ast::ItemFunction { kind, ref header, ref body, body_span } = *func; let ast::FunctionHeader { name, ref parameters, @@ -552,123 +560,104 @@ impl<'ast> State<'_, 'ast> { ref override_, ref returns, } = *header; - self.print_function_like(FunctionLike { - kind: kind.to_str(), - name, - parameters, - visibility, - state_mutability, - virtual_, - override_: override_.as_ref(), - modifiers, - returns, - anonymous: false, - body: body.as_deref(), - body_span: *body_span, - }); - } + self.cbox(0); - fn print_error(&mut self, err: &'ast ast::ItemError<'ast>) { - let ast::ItemError { name, parameters } = err; - self.print_function_like(FunctionLike { - kind: "error", - name: Some(*name), - parameters, - ..Default::default() - }); - } - - fn print_event(&mut self, event: &'ast ast::ItemEvent<'ast>) { - let ast::ItemEvent { name, parameters, anonymous } = event; - self.print_function_like(FunctionLike { - kind: "event", - name: Some(*name), - parameters, - anonymous: *anonymous, - ..Default::default() - }); - } - - fn print_function_like(&mut self, args: FunctionLike<'ast, 'ast>) { - let FunctionLike { - kind, - name, - parameters, - visibility, - state_mutability, - virtual_, - override_, - modifiers, - returns, - anonymous, - body, - body_span, - } = args; - self.word(kind); + self.ibox(0); + self.word(kind.to_str()); if let Some(name) = name { self.nbsp(); self.print_ident(name); } self.print_parameter_list(parameters); + self.neverbreak(); + self.end(); + + // Attributes. + self.s.cbox(self.ind); if let Some(visibility) = visibility { - self.nbsp(); + self.space(); self.word(visibility.to_str()); } if state_mutability != ast::StateMutability::NonPayable { - self.nbsp(); + self.space(); self.word(state_mutability.to_str()); } if virtual_ { - self.nbsp(); + self.space(); self.word("virtual"); } if let Some(override_) = override_ { - self.nbsp(); + self.space(); self.print_override(override_); } - for modifier in modifiers { - self.nbsp(); - - // Add `()` in functions when the modifier is a base contract. - // HACK: heuristics: - // 1. exactly matches the name of a base contract as declared in the `contract is`; - // this does not account for inheritance; - let is_contract_base = self.contract.is_some_and(|contract| { - contract.bases.iter().any(|contract_base| contract_base.name == modifier.name) - }); - // 2. assume that title case names in constructors are bases. - // LEGACY: constructors used to also be `function NameOfContract...`; not checked. - let is_constructor = args.kind == "constructor"; - // LEGACY: we are checking the beginning of the path, not the last segment. - let is_base_contract = is_contract_base || - (is_constructor && - modifier.name.first().name.as_str().starts_with(char::is_uppercase)); - - self.print_modifier_call(modifier, is_base_contract); + for modifier in modifiers.iter() { + self.space(); + self.print_modifier_call(modifier, self.is_modifier_a_base_contract(kind, modifier)); } if !returns.is_empty() { - self.nbsp(); + self.space(); + self.ibox(0); self.word("returns "); self.print_parameter_list(returns); + self.neverbreak(); + self.end(); } - if anonymous { - self.nbsp(); - self.word("anonymous"); - } + self.neverbreak(); + self.s.offset(-self.ind); + self.end(); + if let Some(body) = body { - self.nbsp(); + self.space(); self.print_block(body, body_span); - } else if !(kind == "function" && name.is_none()) { - // Don't print semicolon if this is a function type. + } else { self.word(";"); } + self.end(); + } + + fn is_modifier_a_base_contract( + &self, + kind: ast::FunctionKind, + modifier: &'ast ast::Modifier<'ast>, + ) -> bool { + // Add `()` in functions when the modifier is a base contract. + // HACK: heuristics: + // 1. exactly matches the name of a base contract as declared in the `contract is`; + // this does not account for inheritance; + let is_contract_base = self.contract.is_some_and(|contract| { + contract.bases.iter().any(|contract_base| contract_base.name == modifier.name) + }); + // 2. assume that title case names in constructors are bases. + // LEGACY: constructors used to also be `function NameOfContract...`; not checked. + let is_constructor = matches!(kind, ast::FunctionKind::Constructor); + // LEGACY: we are checking the beginning of the path, not the last segment. + is_contract_base || + (is_constructor && + modifier.name.first().name.as_str().starts_with(char::is_uppercase)) + } + + fn print_error(&mut self, err: &'ast ast::ItemError<'ast>) { + let ast::ItemError { name, parameters } = err; + self.word("error "); + self.print_ident(*name); + self.print_parameter_list(parameters); + self.word(";"); + } + + fn print_event(&mut self, event: &'ast ast::ItemEvent<'ast>) { + let ast::ItemEvent { name, parameters, anonymous } = event; + self.word("event "); + self.print_ident(*name); + self.print_parameter_list(parameters); + if *anonymous { + self.word(" anonymous"); + } + self.word(";"); } fn print_var_def(&mut self, var: &'ast ast::VariableDefinition<'ast>) { self.print_var(var); self.word(";"); - self.maybe_print_trailing_comment(var.span, None); - self.hardbreak_if_not_bol(); } fn print_var(&mut self, var: &'ast ast::VariableDefinition<'ast>) { @@ -721,7 +710,7 @@ impl<'ast> State<'_, 'ast> { } fn print_parameter_list(&mut self, parameters: &'ast [ast::VariableDefinition<'ast>]) { - self.print_tuple(parameters, Self::print_var); + self.print_tuple(parameters, Self::print_var, get_span!()); } fn print_docs(&mut self, docs: &'ast ast::DocComments<'ast>) { @@ -773,7 +762,7 @@ impl<'ast> State<'_, 'ast> { } if !pos.is_last { self.space_if_not_bol(); - self.maybe_print_trailing_comment(span, None); + self.print_trailing_comment(span, None); } else { self.neverbreak(); } @@ -971,18 +960,14 @@ impl<'ast> State<'_, 'ast> { } } ast::TypeKind::Function(ast::TypeFunction { - parameters, - visibility, - state_mutability, - returns, - }) => self.print_function_like(FunctionLike { - kind: "function", - parameters, - visibility: *visibility, - state_mutability: *state_mutability, - returns, - ..Default::default() - }), + parameters: _, + visibility: _, + state_mutability: _, + returns: _, + }) => { + // LEGACY: not implemented. + self.print_span(ty.span); + } ast::TypeKind::Mapping(ast::TypeMapping { key, key_name, value, value_name }) => { self.word("mapping("); self.print_ty(key); @@ -1012,7 +997,7 @@ impl<'ast> State<'_, 'ast> { if self.config.override_spacing { self.nbsp(); } - self.print_tuple(paths.iter().map(Deref::deref), Self::print_path); + self.print_tuple(paths, |this, path| this.print_path(path), get_span!(())); } } @@ -1026,7 +1011,7 @@ impl<'ast> State<'_, 'ast> { match kind { ast::ExprKind::Array(exprs) => { - self.print_array(exprs.iter().map(Deref::deref), Self::print_expr) + self.print_array(exprs, |this, e| this.print_expr(e), get_span!()) } ast::ExprKind::Assign(lhs, None, rhs) => { self.ibox(0); @@ -1117,14 +1102,18 @@ impl<'ast> State<'_, 'ast> { self.s.offset(-self.ind); self.end(); } - ast::ExprKind::Tuple(exprs) => self.print_tuple(exprs.iter(), |this, expr| { - if let Some(expr) = expr { - this.print_expr(expr); - } - }), + ast::ExprKind::Tuple(exprs) => self.print_tuple( + exprs, + |this, expr| { + if let Some(expr) = expr { + this.print_expr(expr); + } + }, + |e| e.as_deref().map(|e| e.span), + ), ast::ExprKind::TypeCall(ty) => { self.word("type"); - self.print_tuple(std::slice::from_ref(ty), Self::print_ty); + self.print_tuple(std::slice::from_ref(ty), Self::print_ty, get_span!()); } ast::ExprKind::Type(ty) => self.print_ty(ty), ast::ExprKind::Unary(un_op, expr) => { @@ -1163,7 +1152,7 @@ impl<'ast> State<'_, 'ast> { match kind { ast::CallArgsKind::Unnamed(exprs) => { - self.print_tuple(exprs.iter().map(Deref::deref), Self::print_expr); + self.print_tuple(exprs, |this, e| this.print_expr(e), get_span!()); } ast::CallArgsKind::Named(named_args) => { self.word("("); @@ -1209,11 +1198,15 @@ impl<'ast> State<'_, 'ast> { } ast::StmtKind::DeclSingle(var) => self.print_var(var), ast::StmtKind::DeclMulti(vars, expr) => { - self.print_tuple(vars.iter(), |this, var| { - if let Some(var) = var { - this.print_var(var); - } - }); + self.print_tuple( + vars, + |this, var| { + if let Some(var) = var { + this.print_var(var); + } + }, + |v| v.as_ref().map(|v| v.span), + ); self.word(" = "); self.neverbreak(); self.print_expr(expr); @@ -1230,8 +1223,9 @@ impl<'ast> State<'_, 'ast> { ast::StmtKind::Emit(path, args) => self.print_emit_revert("emit", path, args), ast::StmtKind::Expr(expr) => self.print_expr(expr), ast::StmtKind::For { init, cond, next, body } => { + self.s.cbox(0); + self.s.ibox(0); self.word("for ("); - self.s.cbox(self.ind); self.zerobreak(); if let Some(init) = init { self.print_stmt(init); @@ -1250,24 +1244,32 @@ impl<'ast> State<'_, 'ast> { self.print_expr(next); } self.zerobreak(); - self.s.offset(-self.ind); - self.end(); self.word(") "); + self.neverbreak(); + self.end(); self.print_stmt_as_block(body, false); + self.end(); } - ast::StmtKind::If(cond, then, els) => { + ast::StmtKind::If(cond, then, els_opt) => { self.s.cbox(0); - - self.print_if_cond("if", cond); - self.nbsp(); - - self.print_stmt_as_block(then, true); - if let Some(els) = els { + self.print_if_no_else(cond, then); + let mut els_opt = els_opt.as_deref(); + while let Some(els) = els_opt { + if self.ends_with('}') { + self.nbsp(); + } else { + self.hardbreak(); + } self.word("else "); - self.print_stmt_as_block(els, true); - // TODO(dani): handle nested else if correctly + if let ast::StmtKind::If(cond, then, els) = &els.kind { + self.print_if_no_else(cond, then); + els_opt = els.as_deref(); + continue; + } else { + self.print_stmt_as_block(els, true); + } + break; } - self.end(); } ast::StmtKind::Return(expr) => { @@ -1288,6 +1290,7 @@ impl<'ast> State<'_, 'ast> { } ast::StmtKind::While(cond, stmt) => { self.print_if_cond("while", cond); + self.nbsp(); self.print_stmt_as_block(stmt, true); } ast::StmtKind::Placeholder => self.word("_"), @@ -1296,12 +1299,21 @@ impl<'ast> State<'_, 'ast> { self.word(";"); } self.print_comments(stmt.span.hi()); - self.maybe_print_trailing_comment(stmt.span, None); + self.print_trailing_comment(stmt.span, None); + } + + fn print_if_no_else(&mut self, cond: &'ast ast::Expr<'ast>, then: &'ast ast::Stmt<'ast>) { + self.print_if_cond("if", cond); + self.nbsp(); + self.print_stmt_as_block(then, true); } fn print_if_cond(&mut self, kw: &'static str, cond: &'ast ast::Expr<'ast>) { + self.ibox(0); self.word_nbsp(kw); - self.print_tuple(std::slice::from_ref(cond), Self::print_expr); + self.print_tuple(std::slice::from_ref(cond), |this, e| this.print_expr(e), get_span!()); + self.neverbreak(); + self.end(); } fn print_emit_revert( From d6ff6281d0534d634858d8b1e94b6b7d863f81d7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 6 May 2025 00:25:29 +0200 Subject: [PATCH 52/54] fix: clamp margin to max as well --- crates/fmt-2/src/pp/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 0b91cbf5071f9..645b79c8ee7b8 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -114,7 +114,7 @@ struct BufEntry { impl Printer { pub fn new(margin: usize) -> Self { - let margin = (margin as isize).max(MIN_SPACE); + let margin = (margin as isize).clamp(MIN_SPACE, SIZE_INFINITY - 1); Self { out: String::new(), space: margin, From 1a827c5e34d1f84d3d7d129cd601d16cf0a16326 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 8 May 2025 10:57:18 +0200 Subject: [PATCH 53/54] megawip --- Cargo.lock | 48 ++------------------------------------- Cargo.toml | 2 ++ crates/fmt-2/src/state.rs | 31 +++++++++++++------------ 3 files changed, 20 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a34459c5a81e4..d5c5e14fe0aa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3442,15 +3442,13 @@ dependencies = [ [[package]] name = "figment" version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +source = "git+https://github.com/DaniPopes/Figment?branch=less-mono#2208a0f5948607cd50d551334f9aa099a3730cd8" dependencies = [ "atomic", "parking_lot", - "pear", "serde", "tempfile", - "toml 0.8.21", + "toml_edit", "uncased", "version_check", ] @@ -5340,12 +5338,6 @@ dependencies = [ "str_stack", ] -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - [[package]] name = "inotify" version = "0.11.0" @@ -6494,29 +6486,6 @@ dependencies = [ "hmac", ] -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.101", -] - [[package]] name = "pem" version = "3.0.5" @@ -6859,19 +6828,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "version_check", - "yansi", -] - [[package]] name = "process-wrap" version = "8.2.0" diff --git a/Cargo.toml b/Cargo.toml index f336292c043b0..ebefa9ace48f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -331,6 +331,8 @@ idna_adapter = "=1.1.0" solar-parse = { git = "https://github.com/paradigmxyz/solar", branch = "main" } solar-sema = { git = "https://github.com/paradigmxyz/solar", branch = "main" } +figment = { git = "https://github.com/DaniPopes/Figment", branch = "less-mono" } + ## alloy-core # alloy-dyn-abi = { path = "../../alloy-rs/core/crates/dyn-abi" } # alloy-json-abi = { path = "../../alloy-rs/core/crates/json-abi" } diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index c8425c836e4db..7d6f8e5545144 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -451,7 +451,7 @@ impl<'ast> State<'_, 'ast> { self.contract = Some(c); self.s.cbox(self.ind); - self.s.cbox(0); + self.cbox(0); self.word_nbsp(kind.to_str()); self.print_ident(*name); self.nbsp(); @@ -569,7 +569,6 @@ impl<'ast> State<'_, 'ast> { self.print_ident(name); } self.print_parameter_list(parameters); - self.neverbreak(); self.end(); // Attributes. @@ -596,20 +595,20 @@ impl<'ast> State<'_, 'ast> { } if !returns.is_empty() { self.space(); - self.ibox(0); self.word("returns "); self.print_parameter_list(returns); - self.neverbreak(); - self.end(); } - self.neverbreak(); - self.s.offset(-self.ind); - self.end(); if let Some(body) = body { self.space(); + self.s.offset(-self.ind); + self.word(""); + self.end(); self.print_block(body, body_span); } else { + self.neverbreak(); + self.s.offset(-self.ind); + self.end(); self.word(";"); } self.end(); @@ -754,7 +753,7 @@ impl<'ast> State<'_, 'ast> { match *kind { ast::LitKind::Str(kind, ..) => { - self.s.cbox(0); + self.cbox(0); for (pos, (span, symbol)) in lit.literals().delimited() { if !self.handle_span(span) { let quote_pos = span.lo() + kind.prefix().len() as u32; @@ -1223,8 +1222,8 @@ impl<'ast> State<'_, 'ast> { ast::StmtKind::Emit(path, args) => self.print_emit_revert("emit", path, args), ast::StmtKind::Expr(expr) => self.print_expr(expr), ast::StmtKind::For { init, cond, next, body } => { - self.s.cbox(0); - self.s.ibox(0); + self.cbox(0); + self.ibox(0); self.word("for ("); self.zerobreak(); if let Some(init) = init { @@ -1251,7 +1250,8 @@ impl<'ast> State<'_, 'ast> { self.end(); } ast::StmtKind::If(cond, then, els_opt) => { - self.s.cbox(0); + self.cbox(0); + self.ibox(0); self.print_if_no_else(cond, then); let mut els_opt = els_opt.as_deref(); while let Some(els) = els_opt { @@ -1260,6 +1260,7 @@ impl<'ast> State<'_, 'ast> { } else { self.hardbreak(); } + self.ibox(0); self.word("else "); if let ast::StmtKind::If(cond, then, els) = &els.kind { self.print_if_no_else(cond, then); @@ -1289,8 +1290,10 @@ impl<'ast> State<'_, 'ast> { self.print_block(block, stmt.span); } ast::StmtKind::While(cond, stmt) => { + self.ibox(0); self.print_if_cond("while", cond); self.nbsp(); + self.end(); self.print_stmt_as_block(stmt, true); } ast::StmtKind::Placeholder => self.word("_"), @@ -1305,15 +1308,13 @@ impl<'ast> State<'_, 'ast> { fn print_if_no_else(&mut self, cond: &'ast ast::Expr<'ast>, then: &'ast ast::Stmt<'ast>) { self.print_if_cond("if", cond); self.nbsp(); + self.end(); self.print_stmt_as_block(then, true); } fn print_if_cond(&mut self, kw: &'static str, cond: &'ast ast::Expr<'ast>) { - self.ibox(0); self.word_nbsp(kw); self.print_tuple(std::slice::from_ref(cond), |this, e| this.print_expr(e), get_span!()); - self.neverbreak(); - self.end(); } fn print_emit_revert( From 1b2a400ea809507da1b35615e98f1abd2fc70b15 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Tue, 13 May 2025 09:34:41 +0200 Subject: [PATCH 54/54] feat: most of yul --- Cargo.lock | 1 + crates/fmt-2/Cargo.toml | 1 + crates/fmt-2/src/lib.rs | 58 +++++++ crates/fmt-2/src/pp/convenience.rs | 2 +- crates/fmt-2/src/pp/mod.rs | 4 +- crates/fmt-2/src/state.rs | 242 +++++++++++++++++++++++------ 6 files changed, 259 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5c5e14fe0aa2..00dccbb789c71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3631,6 +3631,7 @@ version = "1.1.0" dependencies = [ "foundry-config", "itertools 0.14.0", + "similar", "similar-asserts", "snapbox", "solar-parse", diff --git a/crates/fmt-2/Cargo.toml b/crates/fmt-2/Cargo.toml index de4768164efb7..a07099b8b19f5 100644 --- a/crates/fmt-2/Cargo.toml +++ b/crates/fmt-2/Cargo.toml @@ -19,6 +19,7 @@ solar-parse.workspace = true # alloy-primitives.workspace = true itertools.workspace = true +similar = { version = "2", features = ["inline"] } tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } diff --git a/crates/fmt-2/src/lib.rs b/crates/fmt-2/src/lib.rs index a9b6d75896452..53d0242e0a87e 100644 --- a/crates/fmt-2/src/lib.rs +++ b/crates/fmt-2/src/lib.rs @@ -3,6 +3,9 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow(dead_code)] // TODO(dani) +const DEBUG: bool = false || option_env!("FMT_DEBUG").is_some(); +const DEBUG_INDENT: bool = false; + // TODO(dani) // #[macro_use] // extern crate tracing; @@ -32,6 +35,7 @@ pub use foundry_config::fmt::*; pub type FormatterResult = DiagnosticsResult; /// The result of the formatter. +#[derive(Debug)] pub enum DiagnosticsResult { /// Everything went well. Ok(T), @@ -114,6 +118,60 @@ pub fn format_source( fn format_inner( config: FormatterConfig, mk_file: &dyn Fn(&Session) -> solar_parse::interface::Result>, +) -> FormatterResult { + // First pass formatting + let first_result = format_once(config.clone(), mk_file); + + // If first pass was not successful, return the result + if first_result.is_err() { + return first_result; + } + let Some(first_formatted) = first_result.ok_ref() else { return first_result }; + + // Second pass formatting + let second_result = format_once(config, &|sess| { + sess.source_map() + .new_source_file( + solar_parse::interface::source_map::FileName::Custom("format-again".to_string()), + first_formatted, + ) + .map_err(|e| sess.dcx.err(e.to_string()).emit()) + }); + + // Check if the two passes produce the same output (idempotency) + match (first_result.ok_ref(), second_result.ok_ref()) { + (Some(first), Some(second)) if first != second => { + panic!("formatter is not idempotent:\n{}", diff(first, second)); + } + _ => {} + } + + if first_result.is_ok() && second_result.is_err() && !DEBUG { + panic!("failed to format a second time:\nfirst_result={first_result:#?}\nsecond_result={second_result:#?}"); + // second_result + } else { + first_result + } +} + +fn diff(first: &str, second: &str) -> impl std::fmt::Display { + use std::fmt::Write; + let diff = similar::TextDiff::from_lines(first, second); + let mut s = String::new(); + for change in diff.iter_all_changes() { + let tag = match change.tag() { + similar::ChangeTag::Delete => "-", + similar::ChangeTag::Insert => "+", + similar::ChangeTag::Equal => " ", + }; + write!(s, "{tag}{change}").unwrap(); + } + s +} + +fn format_once( + config: FormatterConfig, + mk_file: &dyn Fn(&Session) -> solar_parse::interface::Result>, ) -> FormatterResult { let sess = solar_parse::interface::Session::builder().with_buffer_emitter(Default::default()).build(); diff --git a/crates/fmt-2/src/pp/convenience.rs b/crates/fmt-2/src/pp/convenience.rs index 120307dda6b7c..8cd7cc1ce5b59 100644 --- a/crates/fmt-2/src/pp/convenience.rs +++ b/crates/fmt-2/src/pp/convenience.rs @@ -57,7 +57,7 @@ impl Printer { pub fn is_beginning_of_line(&self) -> bool { match self.last_token() { Some(last_token) => last_token.is_hardbreak(), - None => true, + None => self.out.is_empty() || self.out.ends_with('\n'), } } diff --git a/crates/fmt-2/src/pp/mod.rs b/crates/fmt-2/src/pp/mod.rs index 645b79c8ee7b8..3e998f43b1d0b 100644 --- a/crates/fmt-2/src/pp/mod.rs +++ b/crates/fmt-2/src/pp/mod.rs @@ -1,6 +1,7 @@ //! Adapted from [`rustc_ast_pretty`](https://github.com/rust-lang/rust/blob/07d3fd1d9b9c1f07475b96a9d168564bf528db68/compiler/rustc_ast_pretty/src/pp.rs) //! and [`prettyplease`](https://github.com/dtolnay/prettyplease/blob/8eb8c14649aea32e810732bd4d64fe519e6b752a/src/algorithm.rs). +use crate::{DEBUG, DEBUG_INDENT}; use ring::RingBuffer; use std::{borrow::Cow, cmp, collections::VecDeque, iter}; @@ -8,9 +9,6 @@ mod convenience; mod helpers; mod ring; -const DEBUG: bool = false || option_env!("FMT_DEBUG").is_some(); -const DEBUG_INDENT: bool = false; - // Every line is allowed at least this much space, even if highly indented. const MIN_SPACE: isize = 60; diff --git a/crates/fmt-2/src/state.rs b/crates/fmt-2/src/state.rs index 7d6f8e5545144..b493c6f4db140 100644 --- a/crates/fmt-2/src/state.rs +++ b/crates/fmt-2/src/state.rs @@ -7,7 +7,7 @@ use crate::{iter::IterDelimited, pp::BreakToken, FormatterConfig, InlineConfig}; use foundry_config::fmt as config; use itertools::{Either, Itertools}; use solar_parse::{ - ast::{self, token, Span}, + ast::{self, token, yul, Span}, interface::{BytePos, SourceMap}, Cursor, }; @@ -353,7 +353,7 @@ impl<'ast> State<'_, 'ast> { self.word("pragma "); match &pragma.tokens { ast::PragmaTokens::Version(ident, semver_req) => { - self.print_ident(*ident); + self.print_ident(ident); self.nbsp(); self.word(semver_req.to_string()); } @@ -379,7 +379,7 @@ impl<'ast> State<'_, 'ast> { self.print_ast_str_lit(path); if let Some(ident) = items.source_alias() { self.word(" as "); - self.print_ident(ident); + self.print_ident(&ident); } } ast::ImportItems::Aliases(aliases) => { @@ -387,10 +387,10 @@ impl<'ast> State<'_, 'ast> { self.word("{"); self.braces_break(); for (pos, (ident, alias)) in aliases.iter().delimited() { - self.print_ident(*ident); + self.print_ident(ident); if let Some(alias) = alias { self.word(" as "); - self.print_ident(*alias); + self.print_ident(alias); } if !pos.is_last { self.word(","); @@ -453,7 +453,7 @@ impl<'ast> State<'_, 'ast> { self.s.cbox(self.ind); self.cbox(0); self.word_nbsp(kind.to_str()); - self.print_ident(*name); + self.print_ident(name); self.nbsp(); if !bases.is_empty() { self.word("is"); @@ -504,7 +504,7 @@ impl<'ast> State<'_, 'ast> { let ast::ItemStruct { name, fields } = strukt; self.s.cbox(self.ind); self.word("struct "); - self.print_ident(*name); + self.print_ident(name); self.word(" {"); self.hardbreak_if_nonempty(); for var in fields.iter() { @@ -522,11 +522,11 @@ impl<'ast> State<'_, 'ast> { let ast::ItemEnum { name, variants } = enm; self.s.cbox(self.ind); self.word("enum "); - self.print_ident(*name); + self.print_ident(name); self.word(" {"); self.hardbreak_if_nonempty(); for (pos, ident) in variants.iter().delimited() { - self.print_ident(*ident); + self.print_ident(ident); if !pos.is_last { self.word(","); } @@ -542,7 +542,7 @@ impl<'ast> State<'_, 'ast> { fn print_udvt(&mut self, udvt: &'ast ast::ItemUdvt<'ast>) { let ast::ItemUdvt { name, ty } = udvt; self.word("type "); - self.print_ident(*name); + self.print_ident(name); self.word(" is "); self.print_ty(ty); self.word(";"); @@ -566,7 +566,7 @@ impl<'ast> State<'_, 'ast> { self.word(kind.to_str()); if let Some(name) = name { self.nbsp(); - self.print_ident(name); + self.print_ident(&name); } self.print_parameter_list(parameters); self.end(); @@ -638,7 +638,7 @@ impl<'ast> State<'_, 'ast> { fn print_error(&mut self, err: &'ast ast::ItemError<'ast>) { let ast::ItemError { name, parameters } = err; self.word("error "); - self.print_ident(*name); + self.print_ident(name); self.print_parameter_list(parameters); self.word(";"); } @@ -646,7 +646,7 @@ impl<'ast> State<'_, 'ast> { fn print_event(&mut self, event: &'ast ast::ItemEvent<'ast>) { let ast::ItemEvent { name, parameters, anonymous } = event; self.word("event "); - self.print_ident(*name); + self.print_ident(name); self.print_parameter_list(parameters); if *anonymous { self.word(" anonymous"); @@ -699,7 +699,7 @@ impl<'ast> State<'_, 'ast> { } if let Some(ident) = name { self.nbsp(); - self.print_ident(*ident); + self.print_ident(ident); } if let Some(initializer) = initializer { self.word(" = "); @@ -719,7 +719,7 @@ impl<'ast> State<'_, 'ast> { fn print_ident_or_strlit(&mut self, value: &'ast ast::IdentOrStrLit) { match value { - ast::IdentOrStrLit::Ident(ident) => self.print_ident(*ident), + ast::IdentOrStrLit::Ident(ident) => self.print_ident(ident), ast::IdentOrStrLit::StrLit(strlit) => self.print_ast_str_lit(strlit), } } @@ -730,14 +730,14 @@ impl<'ast> State<'_, 'ast> { self.print_span(span); } - fn print_ident(&mut self, ident: ast::Ident) { + fn print_ident(&mut self, ident: &ast::Ident) { self.print_comments(ident.span.lo()); self.word(ident.to_string()); } fn print_path(&mut self, path: &'ast ast::PathSlice) { for (pos, ident) in path.segments().iter().delimited() { - self.print_ident(*ident); + self.print_ident(ident); if !pos.is_last { self.word("."); } @@ -972,13 +972,13 @@ impl<'ast> State<'_, 'ast> { self.print_ty(key); if let Some(ident) = key_name { self.nbsp(); - self.print_ident(*ident); + self.print_ident(ident); } self.word(" => "); self.print_ty(value); if let Some(ident) = value_name { self.nbsp(); - self.print_ident(*ident); + self.print_ident(ident); } self.word(")"); } @@ -1027,10 +1027,11 @@ impl<'ast> State<'_, 'ast> { self.print_expr(lhs); self.end(); self.space(); + self.word(bin_op.kind.to_str()); if matches!(kind, ast::ExprKind::Assign(..)) { self.word("="); } - self.word_nbsp(bin_op.kind.to_str()); + self.nbsp(); self.print_expr(rhs); self.end(); } @@ -1046,7 +1047,7 @@ impl<'ast> State<'_, 'ast> { self.word("delete "); self.print_expr(expr); } - ast::ExprKind::Ident(ident) => self.print_ident(*ident), + ast::ExprKind::Ident(ident) => self.print_ident(ident), ast::ExprKind::Index(expr, kind) => { self.print_expr(expr); self.word("["); @@ -1078,7 +1079,7 @@ impl<'ast> State<'_, 'ast> { ast::ExprKind::Member(expr, ident) => { self.print_expr(expr); self.word("."); - self.print_ident(*ident); + self.print_ident(ident); } ast::ExprKind::New(ty) => { self.word("new "); @@ -1166,7 +1167,7 @@ impl<'ast> State<'_, 'ast> { self.s.cbox(self.ind); self.braces_break(); for (pos, ast::NamedArg { name, value }) in args.iter().delimited() { - self.print_ident(*name); + self.print_ident(name); self.word(": "); self.print_expr(value); if pos.is_last { @@ -1183,7 +1184,6 @@ impl<'ast> State<'_, 'ast> { /* --- Statements --- */ - #[expect(unused_variables)] fn print_stmt(&mut self, stmt: &'ast ast::Stmt<'ast>) { let ast::Stmt { ref docs, span, ref kind } = *stmt; self.print_docs(docs); @@ -1191,9 +1191,16 @@ impl<'ast> State<'_, 'ast> { return; } match kind { - ast::StmtKind::Assembly(asm) => { - // TODO(dani): implement yul - self.word(""); + ast::StmtKind::Assembly(ast::StmtAssembly { dialect, flags, block }) => { + self.word("assembly "); + if let Some(dialect) = dialect { + self.print_ast_str_lit(dialect); + self.nbsp(); + } + if !flags.is_empty() { + self.print_tuple(flags, Self::print_ast_str_lit, get_span!()); + } + self.print_yul_block(block, span, false); } ast::StmtKind::DeclSingle(var) => self.print_var(var), ast::StmtKind::DeclMulti(vars, expr) => { @@ -1258,7 +1265,7 @@ impl<'ast> State<'_, 'ast> { if self.ends_with('}') { self.nbsp(); } else { - self.hardbreak(); + self.hardbreak_if_not_bol(); } self.ibox(0); self.word("else "); @@ -1281,9 +1288,19 @@ impl<'ast> State<'_, 'ast> { } } ast::StmtKind::Revert(path, args) => self.print_emit_revert("revert", path, args), - ast::StmtKind::Try(stmt_try) => { - // TODO(dani): implement try - self.word(""); + ast::StmtKind::Try(ast::StmtTry { expr, clauses }) => { + self.word("try "); + self.print_expr(expr); + for (pos, ast::TryCatchClause { name, args, block }) in clauses.iter().delimited() { + self.word(if pos.is_first { " returns" } else { " catch" }); + if let Some(name) = name { + self.nbsp(); + self.print_ident(name); + } + self.print_parameter_list(args); + self.nbsp(); + self.print_block(block, span); + } } ast::StmtKind::UncheckedBlock(block) => { self.word("unchecked "); @@ -1314,7 +1331,7 @@ impl<'ast> State<'_, 'ast> { fn print_if_cond(&mut self, kw: &'static str, cond: &'ast ast::Expr<'ast>) { self.word_nbsp(kw); - self.print_tuple(std::slice::from_ref(cond), |this, e| this.print_expr(e), get_span!()); + self.print_tuple(std::slice::from_ref(cond), Self::print_expr, get_span!()); } fn print_emit_revert( @@ -1329,7 +1346,7 @@ impl<'ast> State<'_, 'ast> { } fn print_block(&mut self, block: &'ast [ast::Stmt<'ast>], span: Span) { - self.print_block_inner(block, span, false, false); + self.print_block_inner(block, Self::print_stmt, span, false, false); } // Body of a if/loop. @@ -1339,18 +1356,28 @@ impl<'ast> State<'_, 'ast> { } else { std::slice::from_ref(stmt) }; - self.print_block_inner(stmts, stmt.span, attempt_single_line, true) + self.print_block_inner(stmts, Self::print_stmt, stmt.span, attempt_single_line, true) } - fn print_block_inner( + fn print_yul_block( &mut self, - block: &'ast [ast::Stmt<'ast>], + block: &'ast [yul::Stmt<'ast>], + span: Span, + attempt_single_line: bool, + ) { + self.print_block_inner(block, Self::print_yul_stmt, span, attempt_single_line, false); + } + + fn print_block_inner( + &mut self, + block: &'ast [T], + mut print: impl FnMut(&mut Self, &'ast T), span: Span, attempt_single_line: bool, attempt_omit_braces: bool, ) { // TODO(dani): might need to adjust span for `single_line_block` to include the if condition - if attempt_single_line && self.single_line_block(block, span) { + if attempt_single_line && block.len() == 1 && self.single_line_block(span) { self.s.cbox(self.ind); if attempt_omit_braces { self.scan_break(BreakToken { pre_break: Some('{'), ..Default::default() }); @@ -1358,13 +1385,13 @@ impl<'ast> State<'_, 'ast> { self.word("{"); self.space(); } - self.print_stmt(&block[0]); + print(self, &block[0]); self.print_comments(span.hi()); if attempt_omit_braces { self.s.scan_break(BreakToken { post_break: Some('}'), ..Default::default() }); self.s.offset(-self.ind); } else { - self.space(); + self.space_if_not_bol(); self.s.offset(-self.ind); self.word("}"); } @@ -1374,8 +1401,8 @@ impl<'ast> State<'_, 'ast> { self.s.cbox(self.ind); self.hardbreak_if_nonempty(); for stmt in block { - self.print_stmt(stmt); - self.hardbreak(); + print(self, stmt); + self.hardbreak_if_not_bol(); } self.print_comments_skip_ws(span.hi()); self.s.offset(-self.ind); @@ -1384,10 +1411,7 @@ impl<'ast> State<'_, 'ast> { } } - fn single_line_block(&self, block: &'ast [ast::Stmt<'_>], span: Span) -> bool { - if block.len() != 1 { - return false; - } + fn single_line_block(&self, span: Span) -> bool { match self.config.single_line_statement_blocks { config::SingleLineBlockStyle::Preserve => !self.sm.is_multiline(span), config::SingleLineBlockStyle::Single => true, @@ -1396,6 +1420,134 @@ impl<'ast> State<'_, 'ast> { } } +/// Yul. +impl<'ast> State<'_, 'ast> { + fn print_yul_stmt(&mut self, stmt: &'ast yul::Stmt<'ast>) { + let yul::Stmt { ref docs, span, ref kind } = *stmt; + self.print_docs(docs); + if self.handle_span(span) { + return; + } + + match kind { + yul::StmtKind::Block(stmts) => self.print_yul_block(stmts, span, false), + yul::StmtKind::AssignSingle(path, expr) => { + self.print_path(path); + self.word(" := "); + self.neverbreak(); + self.print_yul_expr(expr); + } + yul::StmtKind::AssignMulti(paths, expr_call) => { + self.commasep(paths, |this, path| this.print_path(path), get_span!(())); + self.word(" := "); + self.neverbreak(); + self.print_yul_expr_call(expr_call); + } + yul::StmtKind::Expr(expr_call) => self.print_yul_expr_call(expr_call), + yul::StmtKind::If(expr, stmts) => { + self.word("if "); + self.print_yul_expr(expr); + self.nbsp(); + self.print_yul_block(stmts, span, true); + } + yul::StmtKind::For { init, cond, step, body } => { + // TODO(dani): boxes + self.ibox(0); + + self.word("for "); + self.print_yul_block(init, span, true); + + self.space(); + self.print_yul_expr(cond); + + self.space(); + self.print_yul_block(step, span, true); + + self.space(); + self.print_yul_block(body, span, true); + + self.end(); + } + yul::StmtKind::Switch(yul::StmtSwitch { selector, branches, default_case }) => { + self.word("switch "); + self.print_yul_expr(selector); + + self.print_trailing_comment(selector.span, None); + + for yul::StmtSwitchCase { constant, body } in branches.iter() { + self.hardbreak_if_not_bol(); + self.word("case "); + self.print_lit(constant); + self.nbsp(); + self.print_yul_block(body, span, true); + + self.print_trailing_comment(selector.span, None); + } + + if let Some(default_case) = default_case { + self.hardbreak_if_not_bol(); + self.word("default "); + self.print_yul_block(default_case, span, true); + } + } + yul::StmtKind::Leave => self.word("leave"), + yul::StmtKind::Break => self.word("break"), + yul::StmtKind::Continue => self.word("continue"), + yul::StmtKind::FunctionDef(yul::Function { name, parameters, returns, body }) => { + self.cbox(0); + self.ibox(0); + self.word("function "); + self.print_ident(name); + self.print_tuple(parameters, Self::print_ident, get_span!()); + self.nbsp(); + if !returns.is_empty() { + self.word("-> "); + self.commasep(returns, Self::print_ident, get_span!()); + self.nbsp(); + } + self.end(); + self.print_yul_block(body, span, false); + self.end(); + } + yul::StmtKind::VarDecl(idents, expr) => { + self.ibox(0); + self.word("let "); + self.commasep(idents, Self::print_ident, get_span!()); + if let Some(expr) = expr { + self.word(" := "); + self.neverbreak(); + self.end(); + self.print_yul_expr(expr); + } else { + self.end(); + } + } + } + self.print_comments(span.hi()); + self.print_trailing_comment(span, None); + self.hardbreak_if_not_bol(); + } + + fn print_yul_expr(&mut self, expr: &'ast yul::Expr<'ast>) { + let yul::Expr { span, ref kind } = *expr; + if self.handle_span(span) { + return; + } + + match kind { + yul::ExprKind::Path(path) => self.print_path(path), + yul::ExprKind::Call(call) => self.print_yul_expr_call(call), + yul::ExprKind::Lit(lit) => self.print_lit(lit), + } + } + + fn print_yul_expr_call(&mut self, expr: &'ast yul::ExprCall<'ast>) { + let yul::ExprCall { name, arguments } = expr; + self.print_ident(name); + self.print_tuple(arguments, Self::print_yul_expr, get_span!()); + } +} + fn stmt_needs_semi<'ast>(stmt: &'ast ast::StmtKind<'ast>) -> bool { match stmt { ast::StmtKind::Assembly { .. } |