Fix shadowed locals and guard the exception-dependent tests

Two problems in the tests added for the bulk scanners, both found by CI:

- the inner `const json j` in the counted-iterator diagnostics shadowed the
  one declared at test-case scope, which -Wshadow rejects on GCC and clang
  and C4456 rejects on MSVC under /WX; rename them
- the new parity checks parse deliberately invalid input, which calls
  std::abort() rather than throwing when JSON_NOEXCEPTION is defined, so
  they would have crashed the no-exception build; guard them the way the
  other tests do

json::accept() does not abort, so the UTF-8 range assertions stay compiled
without exceptions and keep covering validate_one_utf8() there.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-05 10:24:30 +02:00
parent 4f3b18eb4d
commit d058932b03
2 changed files with 28 additions and 10 deletions
+12 -1
View File
@@ -300,6 +300,8 @@ TEST_CASE("lexer number fast path")
} }
} }
#if !defined(JSON_NOEXCEPTION)
// these sections parse invalid input, which aborts when exceptions are off
SECTION("exhaustive grammar parity with the streaming path") SECTION("exhaustive grammar parity with the streaming path")
{ {
// The JSON number grammar is encoded twice: once as the scan_number() // The JSON number grammar is encoded twice: once as the scan_number()
@@ -415,6 +417,7 @@ TEST_CASE("lexer number fast path")
"[json.exception.parse_error.101] parse error at line 1, column 3: " "[json.exception.parse_error.101] parse error at line 1, column 3: "
"syntax error while parsing array - unexpected number literal; expected ']'"); "syntax error while parsing array - unexpected number literal; expected ']'");
} }
#endif
} }
TEST_CASE("lexer string fast path") TEST_CASE("lexer string fast path")
@@ -432,8 +435,10 @@ TEST_CASE("lexer string fast path")
return result; return result;
}; };
#if !defined(JSON_NOEXCEPTION)
// the full outcome of parsing @a doc: the parsed value, or the exact error // the full outcome of parsing @a doc: the parsed value, or the exact error
// message, so a mismatch in either is caught // message, so a mismatch in either is caught. Only usable with exceptions
// on: parsing invalid input aborts when they are off.
const auto outcome = [](const std::string & doc, bool streaming) const auto outcome = [](const std::string & doc, bool streaming)
{ {
try try
@@ -455,11 +460,13 @@ TEST_CASE("lexer string fast path")
return std::string(e.what()); return std::string(e.what());
} }
}; };
#endif
// once at the start of the string, once past the first 8-byte SWAR word, so // once at the start of the string, once past the first 8-byte SWAR word, so
// the bulk scanner sees each case with and without a run behind it // the bulk scanner sees each case with and without a run behind it
const std::vector<std::size_t> offsets{0, 9}; const std::vector<std::size_t> offsets{0, 9};
#if !defined(JSON_NOEXCEPTION)
SECTION("exhaustive contiguous vs streaming parity") SECTION("exhaustive contiguous vs streaming parity")
{ {
// ordinary ASCII, both specials, a control byte, characters that make // ordinary ASCII, both specials, a control byte, characters that make
@@ -533,7 +540,9 @@ TEST_CASE("lexer string fast path")
CAPTURE(mismatches); CAPTURE(mismatches);
CHECK(mismatches.empty()); CHECK(mismatches.empty());
} }
#endif
// json::accept() never throws, so the ranges stay covered without exceptions
SECTION("UTF-8 ranges are accepted and rejected as documented") SECTION("UTF-8 ranges are accepted and rejected as documented")
{ {
// The bulk validator must accept exactly what the byte-at-a-time // The bulk validator must accept exactly what the byte-at-a-time
@@ -577,7 +586,9 @@ TEST_CASE("lexer string fast path")
CAPTURE(offset); CAPTURE(offset);
const std::string doc = "[\"" + std::string(offset, 'a') + test_case.sequence + "\"]"; const std::string doc = "[\"" + std::string(offset, 'a') + test_case.sequence + "\"]";
CHECK(json::accept(doc) == test_case.valid); CHECK(json::accept(doc) == test_case.valid);
#if !defined(JSON_NOEXCEPTION)
CHECK(outcome(doc, false) == outcome(doc, true)); CHECK(outcome(doc, false) == outcome(doc, true));
#endif
} }
} }
} }
+16 -9
View File
@@ -254,9 +254,11 @@ TEST_CASE("std::counted_iterator reaches the contiguous fast paths")
// parsing through the pointer adapter must give exactly the same result // parsing through the pointer adapter must give exactly the same result
CHECK(j == json::parse(json_str)); CHECK(j == json::parse(json_str));
#if !defined(JSON_NOEXCEPTION)
// Diagnostics that quote the offending token are reconstructed from the // Diagnostics that quote the offending token are reconstructed from the
// already-consumed input (supports_seek), a path a sized sentinel only // already-consumed input (supports_seek), a path a sized sentinel only
// reaches now; check a few that include the "last read" text. // reaches now; check a few that include the "last read" text. Parsing
// invalid input aborts when exceptions are off, hence the guard.
for (const char* doc : for (const char* doc :
{"1\nx", "truX", "[tru]", "\"abc", "[\"\\ud834\"]", "[\"a\x01""b\"]", {"1\nx", "truX", "[tru]", "\"abc", "[\"\\ud834\"]", "[\"a\x01""b\"]",
"[\"\xc3\x28\"]", "[1e]", "[\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX" "[\"\xc3\x28\"]", "[1e]", "[\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX"
@@ -269,8 +271,8 @@ TEST_CASE("std::counted_iterator reaches the contiguous fast paths")
std::string string_message; std::string string_message;
try try
{ {
const json j = json::parse(it, std::default_sentinel); const json counted_result = json::parse(it, std::default_sentinel);
static_cast<void>(j); static_cast<void>(counted_result);
} }
catch (const json::parse_error& e) catch (const json::parse_error& e)
{ {
@@ -278,8 +280,8 @@ TEST_CASE("std::counted_iterator reaches the contiguous fast paths")
} }
try try
{ {
const json j = json::parse(text); const json string_result = json::parse(text);
static_cast<void>(j); static_cast<void>(string_result);
} }
catch (const json::parse_error& e) catch (const json::parse_error& e)
{ {
@@ -296,8 +298,8 @@ TEST_CASE("std::counted_iterator reaches the contiguous fast paths")
std::string string_what; std::string string_what;
try try
{ {
const json j = json::parse(bad_first, std::default_sentinel); const json counted_result = json::parse(bad_first, std::default_sentinel);
static_cast<void>(j); static_cast<void>(counted_result);
} }
catch (const json::parse_error& e) catch (const json::parse_error& e)
{ {
@@ -305,8 +307,8 @@ TEST_CASE("std::counted_iterator reaches the contiguous fast paths")
} }
try try
{ {
const json j = json::parse(bad); const json string_result = json::parse(bad);
static_cast<void>(j); static_cast<void>(string_result);
} }
catch (const json::parse_error& e) catch (const json::parse_error& e)
{ {
@@ -314,8 +316,12 @@ TEST_CASE("std::counted_iterator reaches the contiguous fast paths")
} }
CHECK_FALSE(counted_what.empty()); CHECK_FALSE(counted_what.empty());
CHECK(counted_what == string_what); CHECK(counted_what == string_what);
#endif
} }
#if !defined(JSON_NOEXCEPTION)
// several cases below are truncated on purpose, and parsing invalid input
// aborts when exceptions are off
TEST_CASE("std::counted_iterator bulk scanning stops at the counted end") TEST_CASE("std::counted_iterator bulk scanning stops at the counted end")
{ {
// The count, not the size of the underlying buffer, is the end of the // The count, not the size of the underlying buffer, is the end of the
@@ -376,5 +382,6 @@ TEST_CASE("std::counted_iterator bulk scanning stops at the counted end")
} }
} }
#endif #endif
#endif
} // namespace } // namespace