Use and extend the detail helpers to remove duplicated code (#5783)

* Use and extend the detail helpers to remove duplicated code

Library:
- binary_reader: format bytes with hex_byte() instead of snprintf
- add throw_type_must_be() for the 20 copies of type_error.302
- binary_reader: add last_byte_error()/unexpected_byte() for the 32
  "parse error at the last read byte" sites (replaces bon8_error)
- json_sax: add check_container_size() for out_of_range.408 and
  diagnostic_positions::set_container_start/_end()
- json_pointer: add throw_no_parent() (405) and throw_unresolved() (404)

Tests:
- unit-class_parser uses the shared utils::SaxCountdown
- move SaxEventLogger (and its ExitAfter* variants) from unit-class_parser
  and unit-deserialization into the new tests/src/test_sax.hpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Factor out more repeated error paths and test boilerplate

Library:
- add throw_cannot_use_with() for the 34 copies of type_error.304-312
  "cannot use X with Y"
- iter_impl: add throw_cannot_get_value() (invalid_iterator.214)
- parser: add syntax_error() for the 12 parse_error.101 sites
- ordered_map: share the four at() bodies via at_impl()
- json_sax_dom_callback_parser: add pop_container() for end_object()
  and end_array()
- binary_reader: build the two UBJSON/BJData length-type messages with
  concat() and last_byte_error()

Tests:
- move same_value(), the NDEBUG guard, and step 0 (parse without
  exceptions) of the seven fuzzer drivers into tests/src/fuzzer_common.hpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Name the exception ids and address review comments

Add detail::exception_id, a scoped enum with one named enumerator per
documented exception id, and use it for every id in the library. The
create() functions get an overload for it; the int overloads stay for
user code.

Following the review of #5783: add binary_reader::invalid_byte() and
length_type_error(), basic_json::throw_subscript_wrong_type(), move the
fuzzer includes and the using-declaration into fuzzer_common.hpp, and
rename test_sax.hpp to sax_event_loggers.hpp.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Silence -Wweak-vtables for the SAX event loggers

The loggers moved from anonymous namespaces in the test files into
sax_event_loggers.hpp, so clang now warns that their vtables are
emitted in every translation unit.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Silence MSVC 2015 C4100 in parser::syntax_error for static SAX::parse_error

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Inline invalid_byte() into its call sites

The wrapper only fixed the message string of unexpected_byte(), which is
the same kind of per-argument helper that was declined for
throw_type_must_be(). Call unexpected_byte("invalid byte", ...) directly.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann authored and GitHub committed 2026-10-11 08:21:13 +02:00
1 parent f8b47ff6f6
commit 1649d9eda4
26 files changed
+1329 -1391

No files matched your search

+18 -12
View File
@@ -279,6 +279,12 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
}
}
/// @throw invalid_iterator.214 always
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
{
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
}
public:
/*!
@brief return a reference to the value pointed to by the iterator
@@ -303,7 +309,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
}
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
throw_cannot_get_value();
case value_t::string:
case value_t::boolean:
@@ -319,7 +325,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
throw_cannot_get_value();
}
}
}
@@ -361,7 +367,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
throw_cannot_get_value();
}
}
}
@@ -478,7 +484,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
}
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -527,7 +533,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object));
}
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -540,7 +546,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object));
case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator);
@@ -596,7 +602,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
case value_t::array:
{
@@ -675,7 +681,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object));
case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator;
@@ -704,13 +710,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type)
{
case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object));
case value_t::array:
return *std::next(m_it.array_iterator, n);
case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
throw_cannot_get_value();
case value_t::string:
case value_t::boolean:
@@ -726,7 +732,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object;
}
JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
throw_cannot_get_value();
}
}
}
@@ -744,7 +750,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_it.object_iterator->first;
}
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object));
}
/*!