From bc64b31cea95148b384b82adfafc6d70d163f8b2 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 6 Sep 2026 11:42:01 +0200 Subject: [PATCH] Guard the JSON_THROW_USER test against JSON_NOEXCEPTION and GCC's -Wunused-result Two independent CI configurations failed to build/run this new test: - ci_test_noexceptions runs the whole suite with -DJSON_NOEXCEPTION and doctest's "--no-throw" filter, which compiles CHECK_THROWS_AS() down to a no-op that never even invokes the guarded expression. Since this test's whole point is to observe json_throw_user_call_count after json::parse()/at() actually throw, it can't be meaningfully run under that filter (our JSON_THROW_USER override still throws real exceptions regardless of JSON_NOEXCEPTION, but the assertion never gets a chance to run). Guard the TEST_CASE with #if !defined(JSON_NOEXCEPTION), mirroring the existing precedent in unit-json_patch.cpp. - ci_test_gcc and ci_test_standards_gcc(11) failed with -Werror=unused-result on the discarded json::parse() return value. json::parse() is marked warn_unused_result, and unlike a real [[nodiscard]] attribute, GCC does not consider that satisfied by doctest's (void)-cast around the expression in C++11 mode. Assign the result to a discarded local instead, matching the established `json _ = json::parse(...)` idiom already used throughout unit-class_parser.cpp. Signed-off-by: Niels Lohmann --- tests/src/unit-no_io_and_user_exceptions.cpp | 24 +++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/src/unit-no_io_and_user_exceptions.cpp b/tests/src/unit-no_io_and_user_exceptions.cpp index 42ad99d6e..63b73d8b3 100644 --- a/tests/src/unit-no_io_and_user_exceptions.cpp +++ b/tests/src/unit-no_io_and_user_exceptions.cpp @@ -50,13 +50,34 @@ TEST_CASE("JSON_NO_IO") CHECK(j.at("b").get() == true); } +// this test relies on CHECK_THROWS_AS() actually invoking the guarded +// expression so json_throw_user_call_count gets bumped and can be observed +// afterwards; doctest's "--no-throw" test filter (which ci_test_noexceptions +// passes, together with a global -DJSON_NOEXCEPTION added to CMAKE_CXX_FLAGS +// for every translation unit in that build, this file included) compiles +// CHECK_THROWS_AS() out to a no-op that never even invokes the given +// expression -- so json::parse()/at() below would never be called at all and +// the call-count assertions would fail even though our JSON_THROW_USER +// override (which always really throws, regardless of JSON_NOEXCEPTION) would +// have worked fine on its own +#if !defined(JSON_NOEXCEPTION) TEST_CASE("JSON_THROW_USER, JSON_TRY_USER, JSON_CATCH_USER") { json_throw_user_call_count = 0; + // json::parse() is [[nodiscard]] (JSON_HEDLEY_WARN_UNUSED_RESULT); under + // GCC in C++11 mode that expands to __attribute__((warn_unused_result)), + // which -- unlike a [[nodiscard]] attribute proper -- GCC does not + // consider satisfied by doctest's CHECK_THROWS_AS() wrapping the + // expression in a (void) cast, so the discarded return value would still + // be flagged under -Werror=unused-result; assign it to discard it instead, + // matching the established `json _ = json::parse(...)` pattern used + // elsewhere in the test suite (see unit-class_parser.cpp) + json _; // NOLINT(readability-identifier-naming) + // a parse error goes through JSON_THROW directly, i.e., through our // JSON_THROW_USER override - CHECK_THROWS_AS(json::parse("this is not JSON"), json::parse_error&); + CHECK_THROWS_AS(_ = json::parse("this is not JSON"), json::parse_error&); CHECK(json_throw_user_call_count > 0); // at() on an out-of-range array index internally catches std::out_of_range @@ -67,3 +88,4 @@ TEST_CASE("JSON_THROW_USER, JSON_TRY_USER, JSON_CATCH_USER") CHECK_THROWS_AS(arr.at(10), json::out_of_range&); CHECK(json_throw_user_call_count > count_before); } +#endif