Fix CI: do not hide base class methods in the #3989 test SAX parsers

clang-tidy's bugprone-derived-method-shadowing-base-method rejected the
test SAX parsers that derive from json_sax_dom_parser (or the test's
SaxEventLogger) and redefine their non-virtual event functions. The
recovering DOM parsers of unit-class_parser.cpp and unit-regression2.cpp
now hold a json_sax_dom_parser and forward to it, SaxEventLogger gets a
flag to recover from errors instead of a derived class, and the one
function unit-alt-string.cpp redefines is marked as intended.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-30 04:50:11 +02:00
parent 792853d725
commit c16dd7e4f5
3 changed files with 47 additions and 45 deletions
+3
View File
@@ -384,6 +384,9 @@ TEST_CASE("alternative string type")
: nlohmann::detail::json_sax_dom_parser<alt_json>(j, false) : nlohmann::detail::json_sax_dom_parser<alt_json>(j, false)
{} {}
// sax_parse() calls the SAX parser's own parse_error(), so hiding
// the one of the base class is what recovering takes
// NOLINTNEXTLINE(bugprone-derived-method-shadowing-base-method)
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const nlohmann::detail::exception& /*unused*/) bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const nlohmann::detail::exception& /*unused*/)
{ {
++errors; ++errors;
+28 -29
View File
@@ -143,11 +143,13 @@ class SaxEventLogger
{ {
errored = true; errored = true;
events.push_back("parse_error(" + std::to_string(position) + ")"); events.push_back("parse_error(" + std::to_string(position) + ")");
return false; return recover;
} }
std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init) std::vector<std::string> events {}; // NOLINT(readability-redundant-member-init)
bool errored = false; bool errored = false;
/// whether parse_error() asks the parser to recover from the error (see #3989)
bool recover = false;
}; };
class SaxCountdown : public nlohmann::json::json_sax_t class SaxCountdown : public nlohmann::json::json_sax_t
@@ -2822,57 +2824,61 @@ namespace
{ {
/// builds a value like json::parse(), but asks the parser to recover from /// builds a value like json::parse(), but asks the parser to recover from
/// errors (see #3989), and checks that the events it receives are balanced /// errors (see #3989), and checks that the events it receives are balanced
class RecoveringDomParser : public nlohmann::detail::json_sax_dom_parser<json> class RecoveringDomParser
{ {
using base = nlohmann::detail::json_sax_dom_parser<json>;
public: public:
explicit RecoveringDomParser(json& j, std::size_t max_errors_ = static_cast<std::size_t>(-1)) explicit RecoveringDomParser(json& j, std::size_t max_errors_ = static_cast<std::size_t>(-1))
: base(j, false) : dom(j, false)
, max_errors(max_errors_) , max_errors(max_errors_)
{} {}
bool null() bool null()
{ {
value(); value();
return base::null(); return dom.null();
} }
bool boolean(bool val) bool boolean(bool val)
{ {
value(); value();
return base::boolean(val); return dom.boolean(val);
} }
bool number_integer(json::number_integer_t val) bool number_integer(json::number_integer_t val)
{ {
value(); value();
return base::number_integer(val); return dom.number_integer(val);
} }
bool number_unsigned(json::number_unsigned_t val) bool number_unsigned(json::number_unsigned_t val)
{ {
value(); value();
return base::number_unsigned(val); return dom.number_unsigned(val);
} }
bool number_float(json::number_float_t val, const std::string& s) bool number_float(json::number_float_t val, const std::string& s)
{ {
value(); value();
return base::number_float(val, s); return dom.number_float(val, s);
} }
bool string(std::string& val) bool string(std::string& val)
{ {
value(); value();
return base::string(val); return dom.string(val);
}
bool binary(json::binary_t& val)
{
value();
return dom.binary(val);
} }
bool start_object(std::size_t elements) bool start_object(std::size_t elements)
{ {
value(); value();
stack.push_back('o'); stack.push_back('o');
return base::start_object(elements); return dom.start_object(elements);
} }
bool key(std::string& val) bool key(std::string& val)
@@ -2884,7 +2890,7 @@ class RecoveringDomParser : public nlohmann::detail::json_sax_dom_parser<json>
return false; return false;
} }
stack.back() = 'v'; stack.back() = 'v';
return base::key(val); return dom.key(val);
} }
bool end_object() bool end_object()
@@ -2896,14 +2902,14 @@ class RecoveringDomParser : public nlohmann::detail::json_sax_dom_parser<json>
return false; return false;
} }
stack.pop_back(); stack.pop_back();
return base::end_object(); return dom.end_object();
} }
bool start_array(std::size_t elements) bool start_array(std::size_t elements)
{ {
value(); value();
stack.push_back('a'); stack.push_back('a');
return base::start_array(elements); return dom.start_array(elements);
} }
bool end_array() bool end_array()
@@ -2915,7 +2921,7 @@ class RecoveringDomParser : public nlohmann::detail::json_sax_dom_parser<json>
return false; return false;
} }
stack.pop_back(); stack.pop_back();
return base::end_array(); return dom.end_array();
} }
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const json::exception& ex) bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const json::exception& ex)
@@ -2930,6 +2936,8 @@ class RecoveringDomParser : public nlohmann::detail::json_sax_dom_parser<json>
return well_formed && stack.empty(); return well_formed && stack.empty();
} }
/// builds the value
nlohmann::detail::json_sax_dom_parser<json> dom;
std::vector<std::string> errors {}; // NOLINT(readability-redundant-member-init) std::vector<std::string> errors {}; // NOLINT(readability-redundant-member-init)
std::size_t events = 0; std::size_t events = 0;
/// the open containers: 'a' for an array, 'o' for an object that expects /// the open containers: 'a' for an array, 'o' for an object that expects
@@ -2979,17 +2987,6 @@ RecoveryResult parse_recovering(InputType&& input, const bool strict = true,
return {j, sax.errors, sax.events, ok, sax.balanced()}; return {j, sax.errors, sax.events, ok, sax.balanced()};
} }
/// logs the events as strings and recovers from errors
class RecoveringEventLogger : public SaxEventLogger
{
public:
bool parse_error(std::size_t position, const std::string& /*unused*/, const json::exception& /*unused*/)
{
events.push_back("parse_error(" + std::to_string(position) + ")");
return true;
}
};
/// stops after a number of events, but recovers from errors /// stops after a number of events, but recovers from errors
class RecoveringCountdown : public SaxCountdown class RecoveringCountdown : public SaxCountdown
{ {
@@ -3139,7 +3136,8 @@ TEST_CASE("parser error recovery (#3989)")
CHECK(result.value[1].get<double>() == -std::numeric_limits<double>::infinity()); CHECK(result.value[1].get<double>() == -std::numeric_limits<double>::infinity());
// the SAX parser gets the number's text // the SAX parser gets the number's text
RecoveringEventLogger logger; SaxEventLogger logger;
logger.recover = true;
CHECK(!json::sax_parse("1e999", &logger)); CHECK(!json::sax_parse("1e999", &logger));
CHECK(logger.events == std::vector<std::string>({"parse_error(5)", "number_float(1e999)"})); CHECK(logger.events == std::vector<std::string>({"parse_error(5)", "number_float(1e999)"}));
} }
@@ -3191,7 +3189,8 @@ TEST_CASE("parser error recovery (#3989)")
SECTION("events") SECTION("events")
{ {
// see #4522 // see #4522
RecoveringEventLogger logger; SaxEventLogger logger;
logger.recover = true;
CHECK(!json::sax_parse(R"([{1}, "a"])", &logger)); CHECK(!json::sax_parse(R"([{1}, "a"])", &logger));
CHECK(logger.events == std::vector<std::string>( CHECK(logger.events == std::vector<std::string>(
{ {
+16 -16
View File
@@ -874,62 +874,60 @@ namespace
{ {
/// builds a value from SAX events, asks the parser to recover from its first /// builds a value from SAX events, asks the parser to recover from its first
/// 100 errors, and checks that the events are balanced (see #3989) /// 100 errors, and checks that the events are balanced (see #3989)
class RecoveringParser : public nlohmann::detail::json_sax_dom_parser<json> class RecoveringParser
{ {
using base = nlohmann::detail::json_sax_dom_parser<json>;
public: public:
explicit RecoveringParser(json& j) explicit RecoveringParser(json& j)
: base(j, false) : dom(j, false)
{} {}
bool null() bool null()
{ {
value(); value();
return base::null(); return dom.null();
} }
bool boolean(bool val) bool boolean(bool val)
{ {
value(); value();
return base::boolean(val); return dom.boolean(val);
} }
bool number_integer(json::number_integer_t val) bool number_integer(json::number_integer_t val)
{ {
value(); value();
return base::number_integer(val); return dom.number_integer(val);
} }
bool number_unsigned(json::number_unsigned_t val) bool number_unsigned(json::number_unsigned_t val)
{ {
value(); value();
return base::number_unsigned(val); return dom.number_unsigned(val);
} }
bool number_float(json::number_float_t val, const std::string& s) bool number_float(json::number_float_t val, const std::string& s)
{ {
value(); value();
return base::number_float(val, s); return dom.number_float(val, s);
} }
bool string(std::string& val) bool string(std::string& val)
{ {
value(); value();
return base::string(val); return dom.string(val);
} }
bool binary(json::binary_t& val) bool binary(json::binary_t& val)
{ {
value(); value();
return base::binary(val); return dom.binary(val);
} }
bool start_object(std::size_t elements) bool start_object(std::size_t elements)
{ {
value(); value();
stack.push_back('o'); stack.push_back('o');
return base::start_object(elements); return dom.start_object(elements);
} }
bool key(std::string& val) bool key(std::string& val)
@@ -940,7 +938,7 @@ class RecoveringParser : public nlohmann::detail::json_sax_dom_parser<json>
return false; return false;
} }
stack.back() = 'v'; stack.back() = 'v';
return base::key(val); return dom.key(val);
} }
bool end_object() bool end_object()
@@ -951,14 +949,14 @@ class RecoveringParser : public nlohmann::detail::json_sax_dom_parser<json>
return false; return false;
} }
stack.pop_back(); stack.pop_back();
return base::end_object(); return dom.end_object();
} }
bool start_array(std::size_t elements) bool start_array(std::size_t elements)
{ {
value(); value();
stack.push_back('a'); stack.push_back('a');
return base::start_array(elements); return dom.start_array(elements);
} }
bool end_array() bool end_array()
@@ -969,7 +967,7 @@ class RecoveringParser : public nlohmann::detail::json_sax_dom_parser<json>
return false; return false;
} }
stack.pop_back(); stack.pop_back();
return base::end_array(); return dom.end_array();
} }
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const json::exception& ex) bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const json::exception& ex)
@@ -986,6 +984,8 @@ class RecoveringParser : public nlohmann::detail::json_sax_dom_parser<json>
return well_formed && stack.empty(); return well_formed && stack.empty();
} }
/// builds the value
nlohmann::detail::json_sax_dom_parser<json> dom;
std::size_t errors = 0; std::size_t errors = 0;
std::vector<std::string> messages {}; // NOLINT(readability-redundant-member-init) std::vector<std::string> messages {}; // NOLINT(readability-redundant-member-init)
std::vector<char> stack {}; // NOLINT(readability-redundant-member-init) std::vector<char> stack {}; // NOLINT(readability-redundant-member-init)