diff --git a/.arclint b/.arclint index 839365efe..710fcd0c1 100644 --- a/.arclint +++ b/.arclint @@ -1,293 +1,298 @@ { "linters": { "generated": { "type": "generated" }, "clang-format": { "type": "clang-format", "version": ">=11.0", "bin": ["clang-format-11", "clang-format"], "include": "(^src/.*\\.(h|c|cpp|mm)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "autopep8": { "type": "autopep8", "version": ">=1.3.4", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ], "flags": [ "--aggressive", "--ignore=W503,W504" ] }, "flake8": { "type": "flake8", "version": ">=3.0", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ], "flags": [ "--ignore=E303,E305,E501,E704,W503,W504" ] }, "lint-format-strings": { "type": "lint-format-strings", "include": "(^src/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/test/fuzz/strprintf.cpp$)" ] }, "check-doc": { "type": "check-doc", "include": "(^src/.*\\.(h|c|cpp)$)" }, "lint-tests": { "type": "lint-tests", "include": "(^src/(seeder/|rpc/|wallet/)?test/.*\\.(cpp)$)" }, "lint-python-format": { "type": "lint-python-format", "include": "(\\.py$)", "exclude": [ "(^test/lint/lint-python-format\\.py$)", "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "phpcs": { "type": "phpcs", "include": "(\\.php$)", "exclude": [ "(^arcanist/__phutil_library_.+\\.php$)" ], "phpcs.standard": "arcanist/phpcs.xml" }, "lint-locale-dependence": { "type": "lint-locale-dependence", "include": "(^src/.*\\.(h|cpp)$)", "exclude": [ "(^src/(crypto/ctaes/|leveldb/|secp256k1/|tinyformat.h|univalue/))", "(^src/bench/nanobench.h$)" ] }, "lint-cheader": { "type": "lint-cheader", "include": "(^src/.*\\.(h|cpp)$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "spelling": { "type": "spelling", "exclude": [ "(^build-aux/m4/)", "(^depends/)", "(^doc/release-notes/)", "(^contrib/gitian-builder/)", "(^src/(qt/locale|secp256k1|univalue|leveldb)/)", "(^test/lint/dictionary/)", "(package-lock.json)" ], "spelling.dictionaries": [ "test/lint/dictionary/english.json" ] }, "lint-assert-with-side-effects": { "type": "lint-assert-with-side-effects", "include": "(^src/.*\\.(h|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-include-quotes": { "type": "lint-include-quotes", "include": "(^src/.*\\.(h|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-include-guard": { "type": "lint-include-guard", "include": "(^src/.*\\.h$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/tinyformat.h$)" ] }, "lint-include-source": { "type": "lint-include-source", "include": "(^src/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-stdint": { "type": "lint-stdint", "include": "(^src/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/compat/assumptions.h$)" ] }, "lint-source-filename": { "type": "lint-source-filename", "include": "(^src/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-boost-dependencies": { "type": "lint-boost-dependencies", "include": "(^src/.*\\.(h|cpp)$)" }, "lint-python-encoding": { "type": "lint-python-encoding", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "lint-python-shebang": { "type": "lint-python-shebang", "include": "(\\.py$)", "exclude": [ "(__init__\\.py$)", "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "lint-bash-shebang": { "type": "lint-bash-shebang", "include": "(\\.sh$)", "exclude": [ "(^contrib/gitian-builder/)" ] }, "shellcheck": { "type": "shellcheck", "version": ">=0.7.0", "flags": [ "--external-sources", "--source-path=SCRIPTDIR" ], "include": "(\\.sh$)", "exclude": [ "(^contrib/gitian-builder/)", "(^src/(secp256k1|univalue)/)" ] }, "lint-shell-locale": { "type": "lint-shell-locale", "include": "(\\.sh$)", "exclude": [ "(^contrib/gitian-builder/)", "(^src/(secp256k1|univalue)/)", "(^cmake/utils/log-and-print-on-failure.sh)" ] }, "lint-cpp-void-parameters": { "type": "lint-cpp-void-parameters", "include": "(^src/.*\\.(h|cpp)$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)", "(^src/compat/glibc_compat.cpp$)" ] }, "lint-logs": { "type": "lint-logs", "include": "(^src/.*\\.(h|cpp)$)" }, "lint-qt": { "type": "lint-qt", "include": "(^src/qt/.*\\.(h|cpp)$)", "exclude": [ "(^src/qt/(locale|forms|res)/)" ] }, "lint-doxygen": { "type": "lint-doxygen", "include": "(^src/.*\\.(h|c|cpp)$)", "exclude": [ "(^src/(crypto/ctaes|secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "lint-whitespace": { "type": "lint-whitespace", "include": "(\\.(ac|am|cmake|conf|in|include|json|m4|md|openrc|php|pl|sh|txt|yml)$)", "exclude": [ "(^contrib/gitian-builder/)", "(^src/(secp256k1|univalue|leveldb)/)", "(^src/bench/nanobench.h$)" ] }, "yamllint": { "type": "yamllint", "include": "(\\.(yml|yaml)$)", "exclude": "(^src/(secp256k1|univalue|leveldb)/)" }, "lint-check-nonfatal": { "type": "lint-check-nonfatal", "include": [ "(^src/rpc/.*\\.(h|c|cpp)$)", "(^src/wallet/rpc*.*\\.(h|c|cpp)$)" ], "exclude": "(^src/rpc/server.cpp)" }, "lint-markdown": { "type": "lint-markdown", "include": [ "(\\.md$)" ], "exclude": "(^contrib/gitian-builder/)" }, "lint-python-mypy": { "type": "lint-python-mypy", "version": ">=0.780", "include": "(\\.py$)", "exclude": "(^contrib/)", "flags": [ "--ignore-missing-imports" ] }, "lint-python-mutable-default": { "type": "lint-python-mutable-default", "include": "(\\.py$)", "exclude": [ "(^contrib/gitian-builder/)", "(^contrib/apple-sdk-tools/)" ] }, "prettier": { "type": "prettier", "version":">=2.4.1", "include": "(^web/.*\\.(css|html|js|json|jsx|md|scss|ts|tsx)$)", "exclude": "(^web/.*/translations/.*\\.json$)" }, "lint-python-isort": { "type": "lint-python-isort", "version": ">=5.6.4", "include": "(\\.py$)", "exclude": "(^contrib/)" + }, + "rustfmt": { + "type": "rustfmt", + "version": ">=1.5.1", + "include": "(\\.rs$)" } } } diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 000000000..6b45a9888 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,13 @@ +# Note: We require nightly rustfmt + +comment_width = 80 +edition = "2021" +format_code_in_doc_comments = true +format_macro_bodies = true +format_strings = true +group_imports = "StdExternalCrate" +max_width = 80 +reorder_impl_items = true +unstable_features = true +use_field_init_shorthand = true +wrap_comments = true diff --git a/arcanist/__phutil_library_map__.php b/arcanist/__phutil_library_map__.php index 536f691e1..d43be6db4 100644 --- a/arcanist/__phutil_library_map__.php +++ b/arcanist/__phutil_library_map__.php @@ -1,94 +1,96 @@ 2, 'class' => array( 'AbstractGlobalExternalLinter' => 'linter/AbstractGlobalExternalLinter.php', 'ArcanistBitcoinABCConfiguration' => 'configuration/ArcanistBitcoinABCConfiguration.php', 'ArcanistLandBotWorkflow' => 'workflow/ArcanistLandBotWorkflow.php', 'AssertWithSideEffectsLinter' => 'linter/AssertWithSideEffectsLinter.php', 'AutoPEP8FormatLinter' => 'linter/AutoPEP8Linter.php', 'BashShebangLinter' => 'linter/BashShebangLinter.php', 'BoostDependenciesLinter' => 'linter/BoostDependenciesLinter.php', 'CHeaderLinter' => 'linter/CHeaderLinter.php', 'CheckDocLinter' => 'linter/CheckDocLinter.php', 'CheckNonFatalOverAssertInRpc' => 'linter/CheckNonFatalOverAssertInRpc.php', 'ClangFormatLinter' => 'linter/ClangFormatLinter.php', 'CppVoidParameterLinter' => 'linter/CppVoidParameterLinter.php', 'DoxygenLinter' => 'linter/DoxygenLinter.php', 'ExtendedConfigurationDrivenLintEngine' => 'engine/ExtendedConfigurationDrivenLintEngine.php', 'FileNameLinter' => 'linter/FileNameLinter.php', 'FormatStringLinter' => 'linter/FormatStringLinter.php', 'ISortFormatLinter' => 'linter/ISortLinter.php', 'IncludeGuardLinter' => 'linter/IncludeGuardLinter.php', 'IncludeQuotesLinter' => 'linter/IncludeQuotesLinter.php', 'IncludeSourceLinter' => 'linter/IncludeSourceLinter.php', 'LintOnceInterface' => 'linter/LintOnceInterface.php', 'LocaleDependenceLinter' => 'linter/LocaleDependenceLinter.php', 'LogLinter' => 'linter/LogLinter.php', 'MarkdownLinter' => 'linter/MarkdownLinter.php', 'MyPyLinter' => 'linter/MyPyLinter.php', 'PrettierLinter' => 'linter/PrettierLinter.php', 'PythonFileEncodingLinter' => 'linter/PythonFileEncodingLinter.php', 'PythonFormatLinter' => 'linter/PythonFormatLinter.php', 'PythonMutableDefaultLinter' => 'linter/PythonMutableDefaultLinter.php', 'PythonShebangLinter' => 'linter/PythonShebangLinter.php', 'Qt5Linter' => 'linter/Qt5Linter.php', + 'RustfmtLinter' => 'linter/RustfmtLinter.php', 'ShellCheckLinter' => 'linter/ShellCheckLinter.php', 'ShellLocaleLinter' => 'linter/ShellLocaleLinter.php', 'StdintLinter' => 'linter/StdintLinter.php', 'TestsLinter' => 'linter/TestsLinter.php', 'WhitespaceLinter' => 'linter/WhitespaceLinter.php', 'YamllintLinter' => 'linter/YamllintLinter.php', ), 'function' => array( 'startsWith' => 'linter/MarkdownLinter.php', ), 'xmap' => array( 'AbstractGlobalExternalLinter' => array( 'ArcanistExternalLinter', 'LintOnceInterface', ), 'ArcanistBitcoinABCConfiguration' => 'ArcanistConfiguration', 'ArcanistLandBotWorkflow' => 'ArcanistWorkflow', 'AssertWithSideEffectsLinter' => 'ArcanistLinter', 'AutoPEP8FormatLinter' => 'ArcanistExternalLinter', 'BashShebangLinter' => 'ArcanistLinter', 'BoostDependenciesLinter' => 'AbstractGlobalExternalLinter', 'CHeaderLinter' => 'ArcanistLinter', 'CheckDocLinter' => 'AbstractGlobalExternalLinter', 'CheckNonFatalOverAssertInRpc' => 'ArcanistLinter', 'ClangFormatLinter' => 'ArcanistExternalLinter', 'CppVoidParameterLinter' => 'ArcanistLinter', 'DoxygenLinter' => 'ArcanistLinter', 'ExtendedConfigurationDrivenLintEngine' => 'ArcanistLintEngine', 'FileNameLinter' => 'ArcanistLinter', 'FormatStringLinter' => 'ArcanistExternalLinter', 'ISortFormatLinter' => 'ArcanistExternalLinter', 'IncludeGuardLinter' => 'ArcanistLinter', 'IncludeQuotesLinter' => 'ArcanistLinter', 'IncludeSourceLinter' => 'ArcanistLinter', 'LocaleDependenceLinter' => 'ArcanistLinter', 'LogLinter' => 'ArcanistLinter', 'MarkdownLinter' => 'ArcanistLinter', 'MyPyLinter' => 'ArcanistExternalLinter', 'PrettierLinter' => 'ArcanistExternalLinter', 'PythonFileEncodingLinter' => 'ArcanistLinter', 'PythonFormatLinter' => 'ArcanistExternalLinter', 'PythonMutableDefaultLinter' => 'ArcanistLinter', 'PythonShebangLinter' => 'ArcanistLinter', 'Qt5Linter' => 'ArcanistLinter', + 'RustfmtLinter' => 'ArcanistExternalLinter', 'ShellCheckLinter' => 'ArcanistExternalLinter', 'ShellLocaleLinter' => 'ArcanistLinter', 'StdintLinter' => 'ArcanistLinter', 'TestsLinter' => 'ArcanistExternalLinter', 'WhitespaceLinter' => 'ArcanistLinter', 'YamllintLinter' => 'ArcanistExternalLinter', ), )); diff --git a/arcanist/linter/RustfmtLinter.php b/arcanist/linter/RustfmtLinter.php new file mode 100644 index 000000000..311631add --- /dev/null +++ b/arcanist/linter/RustfmtLinter.php @@ -0,0 +1,122 @@ +getExecutableCommand()); + + if ($err) { + throw new Exception(pht('Linter %s: %s', RustfmtLinter::class, $stderr)); + } + + $matches = array(); + // example output: rustfmt 1.5.1-nightly (83088064 2022-06-28) + $regex = '/^rustfmt (?P\d+\.\d+\.\d+)-.*$/'; + if (preg_match($regex, $stdout, $matches)) { + return $matches['version']; + } + + throw new Exception(pht('Linter %s unexpected output: %s', + RustfmtLinter::class, $stderr)); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) { + $root = $this->getProjectRoot(); + $path = Filesystem::resolvePath($path, $root); + + if ($err != 0) { + $message = id(new ArcanistLintMessage()) + ->setPath($path) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('RUSTFMT') + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Linter error') + ->setDescription($stderr); + + return array($message); + } + + $messages = array(); + $lines = preg_split("/\r\n|\n|\r/", $stdout); + // Sometimes it's impossible to wrap code, add an error in this case + foreach ($lines as $lineIdx => $line) { + if (strlen($line) > 80) { + $messages[] = id(new ArcanistLintMessage()) + ->setPath($path) + ->setLine($lineIdx + 1) + ->setChar(1) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('RUSTFMT') + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Line limit exceeded') + ->setDescription('Could not wrap this line with rustfmt'); + } + } + + $orig = file_get_contents($path); + + if ($orig == $stdout) { + return $messages; + } + + $messages[] = id(new ArcanistLintMessage()) + ->setPath($path) + ->setLine(1) + ->setChar(1) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('RUSTFMT') + ->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX) + ->setName('Code style violation') + ->setDescription("'$path' has code style errors.") + ->setOriginalText($orig) + ->setReplacementText($stdout); + + return $messages; + } +}