diff --git a/.clang-tidy b/.clang-tidy index 727b7652..83eca345 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,6 +14,8 @@ Checks: | -bugprone-easily-swappable-parameters, -bugprone-forwarding-reference-overload, -bugprone-exception-escape, + -bugprone-crtp-constructor-accessibility, + -bugprone-chained-comparison, clang-analyzer-optin.cplusplus.VirtualCall, clang-analyzer-optin.performance.Padding, -clang-diagnostic-float-equal, @@ -40,9 +42,16 @@ Checks: | -modernize-make-unique, -modernize-type-traits, -modernize-macro-to-enum, + -modernize-use-constraints, + -modernize-use-ranges, + -modernize-use-starts-ends-with, + -modernize-use-integer-sign-comparison, + -modernize-use-designated-initializers, + -modernize-use-std-numbers, *performance*, -performance-unnecessary-value-param, -performance-inefficient-string-concatenation, + -performance-enum-size, readability-const-return-type, readability-container-size-empty, readability-delete-null-pointer, @@ -62,6 +71,7 @@ Checks: | readability-string-compare, readability-suspicious-call-argument, readability-uniqueptr-delete-release, + -clang-analyzer-optin.core.EnumCastOutOfRange CheckOptions: - key: google-readability-braces-around-statements.ShortStatementLines diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 307efbdf..97ba3067 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -22,7 +22,7 @@ jobs: - name: Configure run: | cmake -S . -B build \ - -DCMAKE_CXX_STANDARD=17 \ + -DCMAKE_CXX_STANDARD=20 \ -DCLI11_SINGLE_FILE_TESTS=OFF \ -DCLI11_BUILD_EXAMPLES=OFF \ -DCLI11_FUZZ_TARGET=ON \ diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8a49d6db..f63d4243 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -88,13 +88,13 @@ jobs: clang-tidy: name: Clang-Tidy runs-on: ubuntu-latest - container: silkeh/clang:17 + container: silkeh/clang:20 steps: - uses: actions/checkout@v4 - name: Configure run: > - cmake -S . -B build -DCMAKE_CXX_STANDARD=17 + cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color;--warnings-as-errors=*" @@ -133,7 +133,7 @@ jobs: boost-build: name: Boost build - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -233,9 +233,9 @@ jobs: run: ctest --output-on-failure -L Packaging working-directory: build - cmake-config-ubuntu-2004: - name: CMake config check (Ubuntu 20.04) - runs-on: ubuntu-20.04 + cmake-config-ubuntu-2204: + name: CMake config check (Ubuntu 22.04) + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -282,9 +282,9 @@ jobs: cmake-version: "3.16" if: success() || failure() - cmake-config-ubuntu-2204: - name: CMake config check (Ubuntu 22.04) - runs-on: ubuntu-22.04 + cmake-config-ubuntu-2404: + name: CMake config check (Ubuntu 24.04) + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4bae11a8..81d32323 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -178,8 +178,8 @@ jobs: containerImage: silkeh/clang:17 cli11.std: 23 cli11.options: -DCMAKE_CXX_FLAGS=-std=c++23 - clang19_26: - containerImage: silkeh/clang:19 + clang20_26: + containerImage: silkeh/clang:20 cli11.std: 26 cli11.options: -DCMAKE_CXX_FLAGS=-std=c++2c container: $[ variables['containerImage'] ] diff --git a/examples/enum.cpp b/examples/enum.cpp index d51a7519..f3603245 100644 --- a/examples/enum.cpp +++ b/examples/enum.cpp @@ -9,6 +9,7 @@ #include #include +// NOLINTNEXTLINE enum class Level : int { High, Medium, Low }; int main(int argc, char **argv) { diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 0df94897..ffbe986f 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -46,7 +46,15 @@ namespace CLI { #endif namespace detail { -enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS_STYLE, SUBCOMMAND, SUBCOMMAND_TERMINATOR }; +enum class Classifier : std::uint8_t { + NONE, + POSITIONAL_MARK, + SHORT, + LONG, + WINDOWS_STYLE, + SUBCOMMAND, + SUBCOMMAND_TERMINATOR +}; struct AppFriend; } // namespace detail @@ -60,7 +68,7 @@ CLI11_INLINE std::string help(const App *app, const Error &e); /// enumeration of modes of how to deal with extras in config files -enum class config_extras_mode : char { error = 0, ignore, ignore_all, capture }; +enum class config_extras_mode : std::uint8_t { error = 0, ignore, ignore_all, capture }; class App; @@ -242,7 +250,7 @@ class App { /// specify that positional arguments come at the end of the argument sequence not inheritable bool positionals_at_end_{false}; - enum class startup_mode : char { stable, enabled, disabled }; + enum class startup_mode : std::uint8_t { stable, enabled, disabled }; /// specify the startup mode for the app /// stable=no change, enabled= startup enabled, disabled=startup disabled startup_mode default_startup{startup_mode::stable}; diff --git a/include/CLI/Error.hpp b/include/CLI/Error.hpp index 25d8d959..56779c1d 100644 --- a/include/CLI/Error.hpp +++ b/include/CLI/Error.hpp @@ -41,7 +41,7 @@ namespace CLI { /// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, /// int values from e.get_error_code(). -enum class ExitCodes { +enum class ExitCodes : int { Success = 0, IncorrectConstruction = 100, BadNameString, diff --git a/include/CLI/FormatterFwd.hpp b/include/CLI/FormatterFwd.hpp index f09465d5..5382fb75 100644 --- a/include/CLI/FormatterFwd.hpp +++ b/include/CLI/FormatterFwd.hpp @@ -29,7 +29,7 @@ class App; /// This is passed in by App; all user classes must accept this as /// the second argument. -enum class AppFormatMode { +enum class AppFormatMode : std::uint8_t { Normal, ///< The normal, detailed help All, ///< A fully expanded help Sub, ///< Used when printed as part of expanded subcommand diff --git a/include/CLI/TypeTools.hpp b/include/CLI/TypeTools.hpp index 8bc3ba71..88b37300 100644 --- a/include/CLI/TypeTools.hpp +++ b/include/CLI/TypeTools.hpp @@ -33,7 +33,7 @@ namespace CLI { namespace detail { // Based generally on https://rmf.io/cxx11/almost-static-if /// Simple empty scoped class -enum class enabler {}; +enum class enabler : std::uint8_t {}; /// An instance to use in EnableIf constexpr enabler dummy = {}; @@ -628,7 +628,7 @@ struct expected_count::value }; // Enumeration of the different supported categorizations of objects -enum class object_category : int { +enum class object_category : std::uint8_t { char_value = 1, integral_value = 2, unsigned_integral = 4, diff --git a/include/CLI/Validators.hpp b/include/CLI/Validators.hpp index 484faddf..e4f00641 100644 --- a/include/CLI/Validators.hpp +++ b/include/CLI/Validators.hpp @@ -185,7 +185,7 @@ class CustomValidator : public Validator { namespace detail { /// CLI enumeration of different file types -enum class path_type { nonexistent, file, directory }; +enum class path_type : std::uint8_t { nonexistent, file, directory }; /// get the type of the path from a file name CLI11_INLINE path_type check_path(const char *file) noexcept; @@ -356,6 +356,7 @@ template < typename T, enable_if_t::type>::value, detail::enabler> = detail::dummy> typename std::remove_reference::type &smart_deref(T &value) { + // NOLINTNEXTLINE return value; } /// Generate a string representation of a set @@ -721,7 +722,7 @@ class AsNumberWithUnit : public Validator { /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched. /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError /// if UNIT_REQUIRED is set and unit literal is not found. - enum Options { + enum Options : std::uint8_t { CASE_SENSITIVE = 0, CASE_INSENSITIVE = 1, UNIT_OPTIONAL = 0, diff --git a/include/CLI/impl/Formatter_inl.hpp b/include/CLI/impl/Formatter_inl.hpp index 0e253cbd..62571964 100644 --- a/include/CLI/impl/Formatter_inl.hpp +++ b/include/CLI/impl/Formatter_inl.hpp @@ -291,11 +291,11 @@ CLI11_INLINE std::string Formatter::make_option(const Option *opt, bool is_posit int shortNamesOverSize = 0; // Print short names - if(shortNames.length() > 0) { + if(!shortNames.empty()) { shortNames = " " + shortNames; // Indent - if(longNames.length() == 0 && opts.length() > 0) + if(longNames.empty() && !opts.empty()) shortNames += opts; // Add opts if only short names and no long names - if(longNames.length() > 0) + if(!longNames.empty()) shortNames += ","; if(static_cast(shortNames.length()) >= shortNamesColumnWidth) { shortNames += " "; @@ -312,8 +312,8 @@ CLI11_INLINE std::string Formatter::make_option(const Option *opt, bool is_posit const auto adjustedLongNamesColumnWidth = longNamesColumnWidth - shortNamesOverSize; // Print long names - if(longNames.length() > 0) { - if(opts.length() > 0) + if(!longNames.empty()) { + if(!opts.empty()) longNames += opts; if(static_cast(longNames.length()) >= adjustedLongNamesColumnWidth) longNames += " "; diff --git a/include/CLI/impl/Split_inl.hpp b/include/CLI/impl/Split_inl.hpp index 025a023b..9bf1a22a 100644 --- a/include/CLI/impl/Split_inl.hpp +++ b/include/CLI/impl/Split_inl.hpp @@ -109,7 +109,7 @@ get_names(const std::vector &input, bool allow_non_standard) { std::vector long_names; std::string pos_name; for(std::string name : input) { - if(name.length() == 0) { + if(name.empty()) { continue; } if(name.length() > 1 && name[0] == '-' && name[1] != '-') { diff --git a/tests/AppTest.cpp b/tests/AppTest.cpp index 29aff27c..94658ca3 100644 --- a/tests/AppTest.cpp +++ b/tests/AppTest.cpp @@ -2615,7 +2615,6 @@ TEST_CASE_METHOD(TApp, "EmptyOptionEach", "[app]") { // #122 TEST_CASE_METHOD(TApp, "EmptyOptionFail", "[app]") { - std::string q; app.add_option("--each"); args = {"--each", "that"}; diff --git a/tests/ConfigFileTest.cpp b/tests/ConfigFileTest.cpp index 96e4db21..23f40e6c 100644 --- a/tests/ConfigFileTest.cpp +++ b/tests/ConfigFileTest.cpp @@ -1590,7 +1590,6 @@ TEST_CASE_METHOD(TApp, "TOMLVectorVector", "[config]") { run(); - auto str = app.config_to_str(); CHECK(two == std::vector>({{1, 2, 3}, {4, 5, 6}})); CHECK(three == std::vector({1, 2, 3, 4, 5, 6})); CHECK(four == std::vector({1, 2, 3, 4, 5, 6, 7, 8})); @@ -1621,7 +1620,6 @@ TEST_CASE_METHOD(TApp, "TOMLVectorVectorSeparated", "[config]") { run(); - auto str = app.config_to_str(); CHECK(two == std::vector>({{1, 2, 3}, {4, 5, 6}})); CHECK(three == std::vector({1, 2, 3, 4, 5, 6})); } @@ -1653,7 +1651,6 @@ TEST_CASE_METHOD(TApp, "TOMLVectorVectorSeparatedSingleElement", "[config]") { run(); - auto str = app.config_to_str(); CHECK(two == std::vector>({{1}, {2}, {3}})); CHECK(three == std::vector({1, 4, 5})); } diff --git a/tests/FuzzFailTest.cpp b/tests/FuzzFailTest.cpp index 09ed65d9..e03e2aea 100644 --- a/tests/FuzzFailTest.cpp +++ b/tests/FuzzFailTest.cpp @@ -342,7 +342,6 @@ TEST_CASE("app_roundtrip_parse_normal_fail") { CLI::FuzzApp fuzzdata; auto app = fuzzdata.generateApp(); int index = GENERATE(range(1, 4)); - std::string optionString, flagString; auto parseData = loadFailureFile("parse_fail_check", index); std::size_t pstring_start{0}; pstring_start = fuzzdata.add_custom_options(app.get(), parseData); diff --git a/tests/HelpersTest.cpp b/tests/HelpersTest.cpp index 58ca468c..feb64cb1 100644 --- a/tests/HelpersTest.cpp +++ b/tests/HelpersTest.cpp @@ -1320,7 +1320,7 @@ TEST_CASE("Types: TypeName", "[helpers]") { std::string text2_name = CLI::detail::type_name(); CHECK(text2_name == "TEXT"); - enum class test { test1, test2, test3 }; + enum class test : std::uint8_t { test1, test2, test3 }; std::string enum_name = CLI::detail::type_name(); CHECK(enum_name == "ENUM"); diff --git a/tests/OptionTypeTest.cpp b/tests/OptionTypeTest.cpp index 219dbbd7..8098b2bd 100644 --- a/tests/OptionTypeTest.cpp +++ b/tests/OptionTypeTest.cpp @@ -29,7 +29,7 @@ #include #include -using Catch::literals::operator"" _a; +using Catch::Matchers::WithinRel; TEST_CASE_METHOD(TApp, "OneStringAgain", "[optiontype]") { std::string str; @@ -56,9 +56,9 @@ TEST_CASE_METHOD(TApp, "doubleFunction", "[optiontype]") { app.add_option_function("--val", [&res](double val) { res = std::abs(val + 54); }); args = {"--val", "-354.356"}; run(); - CHECK(300.356_a == res); + CHECK_THAT(res, WithinRel(300.356)); // get the original value as entered as an integer - CHECK(-354.356_a == app["--val"]->as()); + CHECK_THAT(app["--val"]->as(), WithinRel(-354.356f)); } TEST_CASE_METHOD(TApp, "doubleFunctionFail", "[optiontype]") { @@ -77,8 +77,8 @@ TEST_CASE_METHOD(TApp, "doubleVectorFunction", "[optiontype]") { args = {"--val", "5", "--val", "6", "--val", "7"}; run(); CHECK(3u == res.size()); - CHECK(10.0_a == res[0]); - CHECK(12.0_a == res[2]); + CHECK_THAT(res[0], WithinRel(10.0)); + CHECK_THAT(res[2], WithinRel(12.0)); } TEST_CASE_METHOD(TApp, "doubleVectorFunctionFail", "[optiontype]") {