From c9f1d2d6463bd19d4042aec07bd192b4470187fe Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 9 Sep 2026 09:50:39 +0200 Subject: [PATCH] Broaden JSON_HEDLEY_WARN_UNUSED_RESULT coverage to pure query functions (#5477) * Broaden JSON_HEDLEY_WARN_UNUSED_RESULT coverage to pure query functions Add JSON_HEDLEY_WARN_UNUSED_RESULT to the unambiguous, const, side-effect-free observer functions whose return value is the entire purpose of the call: - dump() - type(), type_name() - all is_* predicates (is_primitive, is_structured, is_null, is_boolean, is_number, is_number_integer, is_number_unsigned, is_number_float, is_object, is_array, is_string, is_binary, is_discarded) - empty(), size(), max_size() - count(...) (both overloads) and contains(...) (all overloads, including the deprecated json_pointer overload) This mirrors the direction the standard library has taken with [[nodiscard]] on the analogous std::vector/std::map members, and catches real bugs such as `j.empty();` (meant `j.clear();`) or `j.contains(k);` with the result thrown away. Deliberately out of scope (left for a separate, later policy decision, per the issue): at(), value(), get*(), flatten(), unflatten(), patch(), merge_patch(), begin()/end(), comparison operators, erase(), and emplace(). Compiling the full test suite (tests/src/unit-*.cpp) with -Wunused-result -Werror uncovered one real hit: a regression test in unit-regression2.cpp called dump() purely to check it does not throw, discarding the result. Fixed by explicitly casting to void, since the call is intentionally result-less there. Fixes #5410 Signed-off-by: Niels Lohmann * Fix discarded nodiscard results across the test suite for GCC's warn_unused_result A plain (void) cast on a call expression suppresses the C++17 [[nodiscard]] warning but not GCC's warning for functions annotated via the GNU __attribute__((warn_unused_result)) form -- which is what JSON_HEDLEY_WARN_UNUSED_RESULT expands to on GCC. Several existing tests that call a newly-annotated function (dump(), empty()) purely to check that it throws/does not throw, discarding the result via (void), newly warned (and failed -Werror builds) once the annotation was broadened. Route those discards through a small ignore_return_value() helper instead, which actually consumes the value and suppresses the warning on both attribute forms. Signed-off-by: Niels Lohmann * Use utils::ignore_return_value() for the issue #1445 dump() discard too Addresses review feedback from @gregmarr on PR #5477: this call site was still using the older "capture in a variable, then (void) it" pattern from before this PR introduced utils::ignore_return_value(), instead of the helper now used at every other discarded-nodiscard-result call site this PR touches. Signed-off-by: Niels Lohmann --------- Signed-off-by: Niels Lohmann --- include/nlohmann/json.hpp | 25 +++++++++++++++++++ single_include/nlohmann/json.hpp | 25 +++++++++++++++++++ tests/src/test_utils.hpp | 9 +++++++ tests/src/unit-class_parser.cpp | 5 +++- ...unit-class_parser_diagnostic_positions.cpp | 5 +++- tests/src/unit-regression1.cpp | 4 ++- tests/src/unit-regression2.cpp | 17 ++++++++----- tests/src/unit-serialization.cpp | 17 ++++++++----- tests/src/unit-unicode1.cpp | 7 ++++-- tests/src/unit-unicode2.cpp | 4 ++- tests/src/unit-unicode3.cpp | 4 ++- tests/src/unit-unicode4.cpp | 4 ++- tests/src/unit-unicode5.cpp | 4 ++- 13 files changed, 109 insertions(+), 21 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 9b7b9fe81..fea75d57a 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1336,6 +1336,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief serialization /// @sa https://json.nlohmann.me/api/basic_json/dump/ + JSON_HEDLEY_WARN_UNUSED_RESULT string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, @@ -1358,6 +1359,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type of the JSON value (explicit) /// @sa https://json.nlohmann.me/api/basic_json/type/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr value_t type() const noexcept { return m_data.m_type; @@ -1365,6 +1367,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is primitive /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_primitive() const noexcept { return is_null() || is_string() || is_boolean() || is_number() || is_binary(); @@ -1372,6 +1375,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is structured /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_structured() const noexcept { return is_array() || is_object(); @@ -1379,6 +1383,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is null /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_null() const noexcept { return m_data.m_type == value_t::null; @@ -1386,6 +1391,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a boolean /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_boolean() const noexcept { return m_data.m_type == value_t::boolean; @@ -1393,6 +1399,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a number /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number() const noexcept { return is_number_integer() || is_number_float(); @@ -1400,6 +1407,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_integer() const noexcept { return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned; @@ -1407,6 +1415,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an unsigned integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_unsigned() const noexcept { return m_data.m_type == value_t::number_unsigned; @@ -1414,6 +1423,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a floating-point number /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_float() const noexcept { return m_data.m_type == value_t::number_float; @@ -1421,6 +1431,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an object /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_object() const noexcept { return m_data.m_type == value_t::object; @@ -1428,6 +1439,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an array /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_array() const noexcept { return m_data.m_type == value_t::array; @@ -1435,6 +1447,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a string /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_string() const noexcept { return m_data.m_type == value_t::string; @@ -1442,6 +1455,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a binary array /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_binary() const noexcept { return m_data.m_type == value_t::binary; @@ -1449,6 +1463,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is discarded /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_discarded() const noexcept { return m_data.m_type == value_t::discarded; @@ -2780,6 +2795,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(const typename object_t::key_type& key) const { // return 0 for all nonobject types @@ -2790,6 +2806,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/count/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(KeyType && key) const { // return 0 for all nonobject types @@ -2798,6 +2815,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const typename object_t::key_type& key) const { return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end(); @@ -2807,6 +2825,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/contains/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(KeyType && key) const { return is_object() && m_data.m_value.object->find(std::forward(key)) != m_data.m_value.object->end(); @@ -2814,12 +2833,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object given a JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const json_pointer& ptr) const { return ptr.contains(this); } template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) bool contains(const typename ::nlohmann::json_pointer& ptr) const { @@ -2975,6 +2996,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief checks whether the container is empty. /// @sa https://json.nlohmann.me/api/basic_json/empty/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool empty() const noexcept { switch (m_data.m_type) @@ -3014,6 +3036,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of elements /// @sa https://json.nlohmann.me/api/basic_json/size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type size() const noexcept { switch (m_data.m_type) @@ -3053,6 +3076,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the maximum possible number of elements /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type max_size() const noexcept { switch (m_data.m_type) @@ -4242,6 +4266,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type as string /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_RETURNS_NON_NULL const char* type_name() const noexcept { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc455191a..3b36b2f89 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -22890,6 +22890,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief serialization /// @sa https://json.nlohmann.me/api/basic_json/dump/ + JSON_HEDLEY_WARN_UNUSED_RESULT string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, @@ -22912,6 +22913,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type of the JSON value (explicit) /// @sa https://json.nlohmann.me/api/basic_json/type/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr value_t type() const noexcept { return m_data.m_type; @@ -22919,6 +22921,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is primitive /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_primitive() const noexcept { return is_null() || is_string() || is_boolean() || is_number() || is_binary(); @@ -22926,6 +22929,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether type is structured /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_structured() const noexcept { return is_array() || is_object(); @@ -22933,6 +22937,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is null /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_null() const noexcept { return m_data.m_type == value_t::null; @@ -22940,6 +22945,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a boolean /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_boolean() const noexcept { return m_data.m_type == value_t::boolean; @@ -22947,6 +22953,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a number /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number() const noexcept { return is_number_integer() || is_number_float(); @@ -22954,6 +22961,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_integer() const noexcept { return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned; @@ -22961,6 +22969,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an unsigned integer number /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_unsigned() const noexcept { return m_data.m_type == value_t::number_unsigned; @@ -22968,6 +22977,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a floating-point number /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_number_float() const noexcept { return m_data.m_type == value_t::number_float; @@ -22975,6 +22985,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an object /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_object() const noexcept { return m_data.m_type == value_t::object; @@ -22982,6 +22993,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is an array /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_array() const noexcept { return m_data.m_type == value_t::array; @@ -22989,6 +23001,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a string /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_string() const noexcept { return m_data.m_type == value_t::string; @@ -22996,6 +23009,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is a binary array /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_binary() const noexcept { return m_data.m_type == value_t::binary; @@ -23003,6 +23017,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return whether value is discarded /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + JSON_HEDLEY_WARN_UNUSED_RESULT constexpr bool is_discarded() const noexcept { return m_data.m_type == value_t::discarded; @@ -24334,6 +24349,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of occurrences of a key in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/count/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(const typename object_t::key_type& key) const { // return 0 for all nonobject types @@ -24344,6 +24360,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/count/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT size_type count(KeyType && key) const { // return 0 for all nonobject types @@ -24352,6 +24369,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const typename object_t::key_type& key) const { return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end(); @@ -24361,6 +24379,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/contains/ template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(KeyType && key) const { return is_object() && m_data.m_value.object->find(std::forward(key)) != m_data.m_value.object->end(); @@ -24368,12 +24387,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief check the existence of an element in a JSON object given a JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/contains/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool contains(const json_pointer& ptr) const { return ptr.contains(this); } template::value, int> = 0> + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) bool contains(const typename ::nlohmann::json_pointer& ptr) const { @@ -24529,6 +24550,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief checks whether the container is empty. /// @sa https://json.nlohmann.me/api/basic_json/empty/ + JSON_HEDLEY_WARN_UNUSED_RESULT bool empty() const noexcept { switch (m_data.m_type) @@ -24568,6 +24590,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the number of elements /// @sa https://json.nlohmann.me/api/basic_json/size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type size() const noexcept { switch (m_data.m_type) @@ -24607,6 +24630,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief returns the maximum possible number of elements /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + JSON_HEDLEY_WARN_UNUSED_RESULT size_type max_size() const noexcept { switch (m_data.m_type) @@ -25796,6 +25820,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief return the type as string /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_RETURNS_NON_NULL const char* type_name() const noexcept { diff --git a/tests/src/test_utils.hpp b/tests/src/test_utils.hpp index baa802f71..4c81a8ef4 100644 --- a/tests/src/test_utils.hpp +++ b/tests/src/test_utils.hpp @@ -15,6 +15,15 @@ namespace utils { +// Some tests intentionally discard the [[nodiscard]]/JSON_HEDLEY_WARN_UNUSED_RESULT +// return value of a call they only make to exercise its side effects (e.g. checking +// that it does not throw). A plain (void) cast on the call expression does not +// suppress GCC's warning for functions using the GNU __attribute__((warn_unused_result)) +// form (as opposed to the C++17 [[nodiscard]] attribute) -- passing the value into an +// ordinary function call does. +template +inline void ignore_return_value(T&& /*unused*/) noexcept {} + inline std::vector read_binary_file(const std::string& filename) { std::ifstream file(filename, std::ios::binary); diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp index 7d86994e1..bb9cdcdc7 100644 --- a/tests/src/unit-class_parser.cpp +++ b/tests/src/unit-class_parser.cpp @@ -23,6 +23,8 @@ using nlohmann::json; #include #include +#include "test_utils.hpp" + namespace { class SaxEventLogger @@ -624,7 +626,8 @@ TEST_CASE("parser class") SECTION("overflow") { // overflows during parsing yield an exception - CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); + // empty() is nodiscard; the exception is thrown by parser_helper() itself, before empty() would run + CHECK_THROWS_WITH_AS(utils::ignore_return_value(parser_helper("1.18973e+4932").empty()), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); } SECTION("invalid numbers") diff --git a/tests/src/unit-class_parser_diagnostic_positions.cpp b/tests/src/unit-class_parser_diagnostic_positions.cpp index 2697ecf8a..794182f30 100644 --- a/tests/src/unit-class_parser_diagnostic_positions.cpp +++ b/tests/src/unit-class_parser_diagnostic_positions.cpp @@ -22,6 +22,8 @@ using nlohmann::json; #include +#include "test_utils.hpp" + namespace { class SaxEventLogger @@ -629,7 +631,8 @@ TEST_CASE("parser class") SECTION("overflow") { // overflows during parsing yield an exception - CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); + // empty() is nodiscard; the exception is thrown by parser_helper() itself, before empty() would run + CHECK_THROWS_WITH_AS(utils::ignore_return_value(parser_helper("1.18973e+4932").empty()), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); } SECTION("invalid numbers") diff --git a/tests/src/unit-regression1.cpp b/tests/src/unit-regression1.cpp index 475ef511f..fdc3fdd05 100644 --- a/tests/src/unit-regression1.cpp +++ b/tests/src/unit-regression1.cpp @@ -29,6 +29,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" #ifdef JSON_HAS_CPP_17 #include @@ -1373,7 +1374,8 @@ TEST_CASE("regression tests 1") std::array key1 = {{ 103, 92, 117, 48, 48, 48, 55, 92, 114, 215, 126, 214, 95, 92, 34, 174, 40, 71, 38, 174, 40, 71, 38, 223, 134, 247, 127, 0 }}; std::string const key1_str(reinterpret_cast(key1.data())); json const j = key1_str; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E", json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E", json::type_error&); } #if JSON_USE_IMPLICIT_CONVERSIONS diff --git a/tests/src/unit-regression2.cpp b/tests/src/unit-regression2.cpp index 2e7450e2e..6c30e3503 100644 --- a/tests/src/unit-regression2.cpp +++ b/tests/src/unit-regression2.cpp @@ -31,6 +31,8 @@ using ordered_json = nlohmann::ordered_json; #include #include +#include "test_utils.hpp" + #ifdef JSON_HAS_CPP_17 #include #include @@ -639,7 +641,8 @@ TEST_CASE("regression tests 2") s += static_cast(i); } dump_test["1"] = s; - dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace); + // dump() is nodiscard; this only checks that dumping does not throw/crash + utils::ignore_return_value(dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace)); } } @@ -731,12 +734,14 @@ TEST_CASE("regression tests 2") { const std::array data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}}; const json j = json::from_msgpack(data.data(), data.size()); + // dump() is nodiscard; this only checks that dumping does not throw CHECK_NOTHROW( - j.dump(4, // Indent - ' ', // Indent char - false, // Ensure ascii - json::error_handler_t::strict // Error - )); + utils::ignore_return_value( + j.dump(4, // Indent + ' ', // Indent char + false, // Ensure ascii + json::error_handler_t::strict // Error + ))); } SECTION("PR #2181 - regression bug with lvalue") diff --git a/tests/src/unit-serialization.cpp b/tests/src/unit-serialization.cpp index f55ed8470..caf720671 100644 --- a/tests/src/unit-serialization.cpp +++ b/tests/src/unit-serialization.cpp @@ -15,6 +15,8 @@ using nlohmann::json; #include #include +#include "test_utils.hpp" + TEST_CASE("serialization") { SECTION("operator<<") @@ -84,8 +86,9 @@ TEST_CASE("serialization") { const json j = "ä\xA9ü"; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); - CHECK_THROWS_WITH_AS(j.dump(1, ' ', false, json::error_handler_t::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump(1, ' ', false, json::error_handler_t::strict)), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\""); CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\""); @@ -95,8 +98,9 @@ TEST_CASE("serialization") { const json j = "123\xC2"; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); - CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j.dump(1, ' ', false, json::error_handler_t::strict)), json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\""); CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\""); @@ -106,8 +110,9 @@ TEST_CASE("serialization") { const json j = "123\xF1\xB0\x34\x35\x36"; - CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&); - CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j.dump(1, ' ', false, json::error_handler_t::strict)), json::type_error&); CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\""); CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\""); CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\""); diff --git a/tests/src/unit-unicode1.cpp b/tests/src/unit-unicode1.cpp index 174ce1395..2d744003a 100644 --- a/tests/src/unit-unicode1.cpp +++ b/tests/src/unit-unicode1.cpp @@ -17,6 +17,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" TEST_CASE("Unicode (1/5)" * doctest::skip()) { @@ -240,7 +241,8 @@ void roundtrip(bool success_expected, const std::string& s) if (success_expected) { // serialization succeeds - CHECK_NOTHROW(j.dump()); + // dump() is nodiscard; this only checks that dumping does not throw + CHECK_NOTHROW(utils::ignore_return_value(j.dump())); // exclude parse test for U+0000 if (s[0] != '\0') @@ -259,7 +261,8 @@ void roundtrip(bool success_expected, const std::string& s) else { // serialization fails - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // parsing JSON text fails CHECK_THROWS_AS(_ = json::parse(ps), json::parse_error&); diff --git a/tests/src/unit-unicode2.cpp b/tests/src/unit-unicode2.cpp index fb68815ba..a9649b4de 100644 --- a/tests/src/unit-unicode2.cpp +++ b/tests/src/unit-unicode2.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced); diff --git a/tests/src/unit-unicode3.cpp b/tests/src/unit-unicode3.cpp index 739a3dad3..d5627d8cc 100644 --- a/tests/src/unit-unicode3.cpp +++ b/tests/src/unit-unicode3.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced); diff --git a/tests/src/unit-unicode4.cpp b/tests/src/unit-unicode4.cpp index f7047201c..f15a1499f 100644 --- a/tests/src/unit-unicode4.cpp +++ b/tests/src/unit-unicode4.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced); diff --git a/tests/src/unit-unicode5.cpp b/tests/src/unit-unicode5.cpp index e4dcc2131..e35801823 100644 --- a/tests/src/unit-unicode5.cpp +++ b/tests/src/unit-unicode5.cpp @@ -19,6 +19,7 @@ using nlohmann::json; #include #include #include "make_test_data_available.hpp" +#include "test_utils.hpp" // this test suite uses static variables with non-trivial destructors DOCTEST_CLANG_SUPPRESS_WARNING_PUSH @@ -97,7 +98,8 @@ void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 else { // strict mode must throw if success is not expected - CHECK_THROWS_AS(j.dump(), json::type_error&); + // dump() is nodiscard; the exception is thrown by dump() itself before it would return + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // ignore and replace must create different dumps CHECK(s_ignored != s_replaced);