diff --git a/.clang-tidy b/.clang-tidy index a1b5e54..e9b020d 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -47,6 +47,7 @@ Checks: > -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-const-or-ref-data-members, + -misc-include-cleaner, WarningsAsErrors: '' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e859b1..7aff250 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,11 @@ jobs: echo "CC=gcc-11" >> $GITHUB_ENV echo "CXX=g++-11" >> $GITHUB_ENV + - name: Install cppcheck + run: | + sudo apt update && sudo apt install -y cppcheck + cppcheck --version + - name: Install clang-format and clang-tidy run: | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - @@ -34,7 +39,9 @@ jobs: - name: Run clang-format run: cmake -D FORMAT_COMMAND=clang-format -P cmake/lint.cmake - - name: Run clang-tidy + - name: Run cppcheck and clang-tidy + # Cppcheck's checks are disabled. They are mostly redundant to clang-tidy and there are too many false positives. + # But the parsing of cppcheck is kept to be compatible with its compiler frontend. if: ${{ !cancelled() }} run: | cmake --preset=ci-checks diff --git a/CMakePresets.json b/CMakePresets.json index 17c23a0..1d3fa04 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,7 +33,7 @@ "name": "cppcheck", "hidden": true, "cacheVariables": { - "CMAKE_CXX_CPPCHECK": "cppcheck;--inline-suppr" + "CMAKE_CXX_CPPCHECK": "cppcheck;--error-exitcode=13" } }, { @@ -171,6 +171,7 @@ "ci-build", "ci-unix", "clang-tidy", + "cppcheck", "dev-mode" ] }, diff --git a/include/emio/detail/format/formatter.hpp b/include/emio/detail/format/formatter.hpp index 26ae68f..5df1132 100644 --- a/include/emio/detail/format/formatter.hpp +++ b/include/emio/detail/format/formatter.hpp @@ -790,10 +790,16 @@ concept has_validate_function_v = requires { { formatter::validate(std::declval()) } -> std::same_as>; }; +template +concept has_static_validate_function_v = requires { &formatter::validate; }; + +template +concept has_member_validate_function_v = requires { std::declval>().validate(std::declval()); }; + template concept has_any_validate_function_v = - requires { &formatter::validate; } || std::is_member_function_pointer_v::validate)> || - requires { std::declval>().validate(std::declval()); }; + has_static_validate_function_v || std::is_member_function_pointer_v::validate)> || + has_member_validate_function_v; template inline constexpr bool is_core_type_v = diff --git a/include/emio/detail/parser.hpp b/include/emio/detail/parser.hpp index 73bc3d0..a06c0ee 100644 --- a/include/emio/detail/parser.hpp +++ b/include/emio/detail/parser.hpp @@ -185,6 +185,7 @@ int is_arg_span2(const args_span& t); bool is_arg_span2(...); template +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): only used within type traits constexpr bool is_args_span = sizeof(is_arg_span2(std::declval())) == sizeof(int); template diff --git a/include/emio/detail/scan/scanner.hpp b/include/emio/detail/scan/scanner.hpp index 7d05ead..f8fa1fd 100644 --- a/include/emio/detail/scan/scanner.hpp +++ b/include/emio/detail/scan/scanner.hpp @@ -331,10 +331,16 @@ concept has_validate_function_v = requires { { scanner::validate(std::declval()) } -> std::same_as>; }; +template +concept has_static_validate_function_v = requires { &scanner::validate; }; + +template +concept has_member_validate_function_v = requires { std::declval>().validate(std::declval()); }; + template concept has_any_validate_function_v = - requires { &scanner::validate; } || std::is_member_function_pointer_v::validate)> || - requires { std::declval>().validate(std::declval()); }; + has_static_validate_function_v || std::is_member_function_pointer_v::validate)> || + has_member_validate_function_v; template inline constexpr bool is_core_type_v = diff --git a/include/emio/detail/validated_string.hpp b/include/emio/detail/validated_string.hpp index b570dae..d60a354 100644 --- a/include/emio/detail/validated_string.hpp +++ b/include/emio/detail/validated_string.hpp @@ -71,6 +71,7 @@ class validated_string : public validated_string_storage { template requires(std::is_constructible_v) consteval validated_string(const S& s) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): can construct a std::string_view : validated_string_storage{validated_string_storage::from(s)} { if (get().has_error()) { std::terminate(); @@ -88,6 +89,7 @@ class validated_string : public validated_string_storage { * Returns format/scan string as valid one. * @return The valid format/scan string or invalid_format if the validation failed. */ + // NOLINTNEXTLINE(modernize-use-nodiscard): result<...> is already declared with nodiscard constexpr result> as_valid() const noexcept { if (get().has_value()) { return valid_string{*this}; diff --git a/include/emio/ranges.hpp b/include/emio/ranges.hpp index 6f6bdf7..3f8464d 100644 --- a/include/emio/ranges.hpp +++ b/include/emio/ranges.hpp @@ -171,9 +171,8 @@ class formatter { static constexpr result validate_for_each(std::index_sequence /*unused*/, reader& format_rdr) noexcept { size_t reader_pos = 0; result res = success; - const auto validate = [&reader_pos, &res](std::type_identity /*unused*/, - reader r /*copy!*/) noexcept { - res = U::validate(r); + const auto validate = [&reader_pos, &res](const auto type_identity, reader r /*copy!*/) noexcept { + res = decltype(type_identity)::type::validate(r); if (res.has_error()) { return false; } diff --git a/test/static_analysis/test_main.cpp b/test/static_analysis/test_main.cpp index e43223e..39749c4 100644 --- a/test/static_analysis/test_main.cpp +++ b/test/static_analysis/test_main.cpp @@ -1,6 +1,26 @@ // Include all. -#include // NOLINT(misc-include-cleaner): required to parse all headers with static analysers +#include + +consteval emio::result compile_time_test() { + emio::static_buffer<128> buf; + EMIO_TRYV(emio::format_to(buf, "abc")); + return emio::format_to(buf, "abc {}", 13); +} + +emio::result test() { + EMIO_TRYV(compile_time_test()); + if (emio::format("abc").size() != 3) { + return emio::err::invalid_data; + } + if (emio::format("{}", "abc").size() != 3) { + return emio::err::invalid_data; + } + return emio::format(emio::runtime("abc {}"), 13); +} int main() { + if (test().has_error()) { + return 1; + } return 0; }