Compare commits

..
Author SHA1 Message Date
Niels Lohmann d7a7534872 Tests: skip the parse() allocation sweep with MSVC iterator debugging
With _ITERATOR_DEBUG_LEVEL > 0 (MSVC STL debug builds, clang-cl), containers
allocate inside noexcept functions, so a failing allocation terminates the
program: "test case CRASHED: Terminate handler called" in the sax_parse
subcase of #5985. The sweep test of #6000 has the same guard.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 19:12:26 +01:00
Niels Lohmann eaee875178 json_view: no constant operand in the number dispatch (MSVC C4127)
The `!negative ||` that #6006 added to the digit test of the number scan has
a constant left operand in the template (negative is a template argument), so
MSVC reports C4127 under /WX in builder.hpp(1177). Restore the plain test; the
branch behaves the same.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 19:12:26 +01:00
Niels Lohmann 3724295333 json_view: a value that is not found has no bytes in its exception context
Keeping the byte range of the root value (#5990) dropped the found flag of
value_path(), but a root has no tokens and is found, while a view of a value
that was removed from the document is not found. Without the flag, the
removed value got the bytes of its stale node, and
"json_view exceptions name the path and the bytes of the value" failed on
the macOS jobs with JSON_DIAGNOSTICS and JSON_DIAGNOSTIC_POSITIONS.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 19:10:16 +01:00
7 changed files with 26 additions and 36 deletions

No files matched your search

+5 -6
View File
@@ -1168,20 +1168,19 @@ indent_done:
if (negative)
{
++p;
if (NLOHMANN_VIEW_UNLIKELY(cur() < '0' || cur() > '9'))
{
return fail(error_code::number_after_minus);
}
}
const unsigned char* const int_start = p;
if (cur() == '0')
{
++p;
}
else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
{
p = skip_digits(p + 1, e);
}
else
{
// (a digit: the dispatch or the check after the sign saw it)
p = skip_digits(p + 1, e);
return fail(error_code::number_after_minus);
}
const auto int_digits = static_cast<std::size_t>(p - int_start);
std::size_t frac_digits = 0;
+5 -6
View File
@@ -2473,20 +2473,19 @@ indent_done:
if (negative)
{
++p;
if (NLOHMANN_VIEW_UNLIKELY(cur() < '0' || cur() > '9'))
{
return fail(error_code::number_after_minus);
}
}
const unsigned char* const int_start = p;
if (cur() == '0')
{
++p;
}
else if (NLOHMANN_VIEW_LIKELY(cur() >= '1' && cur() <= '9'))
{
p = skip_digits(p + 1, e);
}
else
{
// (a digit: the dispatch or the check after the sign saw it)
p = skip_digits(p + 1, e);
return fail(error_code::number_after_minus);
}
const auto int_digits = static_cast<std::size_t>(p - int_start);
std::size_t frac_digits = 0;
+7 -13
View File
@@ -1026,7 +1026,10 @@ TEST_CASE("insert() provides its documented exception guarantees (#5986)")
}
#endif
#if !defined(JSON_NOEXCEPTION)
// the no-exceptions CI job has no std::bad_alloc to catch; with iterator
// debugging (MSVC STL debug builds) containers allocate inside noexcept
// functions, so failing an allocation there terminates the program
#if !defined(JSON_NOEXCEPTION) && !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
namespace
{
// the allocation numbered sweep_fail_at (counted from 1) throws; 0 never fails
@@ -1038,14 +1041,6 @@ template<class T>
struct sweep_allocator
{
using value_type = T;
// GCC 4.8's std::map takes these from the allocator instead of from
// std::allocator_traits
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
sweep_allocator() = default;
template<class U> sweep_allocator(const sweep_allocator<U>& /*unused*/) noexcept {}
@@ -1163,9 +1158,8 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
// With iterator debugging (MSVC STL debug builds, also used by clang-cl),
// containers allocate a debug proxy through the allocator inside their
// noexcept default and move constructors, so failing that allocation
// terminates the program instead of throwing std::bad_alloc. Nothing to
// check there.
// noexcept move constructors, so failing that allocation terminates the
// program instead of throwing std::bad_alloc. Nothing to check there.
#if !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
SECTION("parser callback")
{
@@ -1187,6 +1181,7 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
return sweep_json::parse(text, drop_d);
});
}
#endif
SECTION("sax_parse into an existing value")
{
@@ -1200,6 +1195,5 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
return value;
});
}
#endif
}
#endif
+3 -4
View File
@@ -2222,10 +2222,9 @@ TEST_CASE("serialization of short strings")
{
CAPTURE(s)
const json j = s;
const std::string quoted = "\"" + s + "\""; // NOLINT(performance-inefficient-string-concatenation)
CHECK(j.dump() == quoted);
CHECK(j.dump(-1, ' ', true) == quoted);
CHECK(json({{s, s}}).dump() == "{" + quoted + ":" + quoted + "}"); // NOLINT(performance-inefficient-string-concatenation)
CHECK(j.dump() == "\"" + s + "\"");
CHECK(j.dump(-1, ' ', true) == "\"" + s + "\"");
CHECK(json({{s, s}}).dump() == "{\"" + s + "\":\"" + s + "\"}");
}
// a character that needs an escape
+1 -1
View File
@@ -111,7 +111,7 @@ struct failing_allocator
template<typename U>
explicit failing_allocator(const failing_allocator<U>& /*other*/) noexcept {}
[[noreturn]] T* allocate(std::size_t /*n*/) // NOLINT(readability-convert-member-functions-to-static)
T* allocate(std::size_t /*n*/) // NOLINT(readability-convert-member-functions-to-static)
{
throw std::bad_alloc();
}
+4 -4
View File
@@ -505,8 +505,8 @@ struct locale_switching_token
{
using value_type = char;
locale_switching_token(std::string text_, std::string switch_to_)
: text(std::move(text_)), switch_to(std::move(switch_to_))
locale_switching_token(std::string text_, const char* switch_to_)
: text(std::move(text_)), switch_to(switch_to_)
{}
std::size_t size() const
@@ -523,13 +523,13 @@ struct locale_switching_token
{
if (!switched)
{
switched = std::setlocale(LC_NUMERIC, switch_to.c_str()) != nullptr;
switched = std::setlocale(LC_NUMERIC, switch_to) != nullptr;
}
return &text[0];
}
std::string text;
std::string switch_to;
const char* switch_to;
bool switched = false;
};
+1 -2
View File
@@ -122,8 +122,7 @@ TEST_CASE("JSON text with std::byte")
SECTION("invalid text")
{
CHECK_FALSE(json::accept(to_bytes("[1,\n2,")));
json _;
CHECK_THROWS_WITH_AS(_ = json::parse(to_bytes("[1,\n2,")),
CHECK_THROWS_WITH_AS(json::parse(to_bytes("[1,\n2,")),
"[json.exception.parse_error.101] parse error at line 2, column 3: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&);
}
}