Address the clang-tidy findings of json_document and json_view

- the input dispatch takes byte ranges by const reference and reads the
  size once (which also settles a finding of the static analyzer); input
  adapters are taken by value
- the classification of inputs keeps its nested conditional operators, a
  constant expression of C++11 (NOLINT)
- the test's C arrays, fixed seed, and escaped literals are marked, as in
  the other tests

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-29 00:36:57 +02:00
parent 839e08b70e
commit b1628423a6
4 changed files with 33 additions and 27 deletions

View File

@@ -48,6 +48,7 @@ struct classify_input
#else
static constexpr bool is_string_view = false;
#endif
// NOLINTBEGIN(readability-avoid-nested-conditional-operator): a constant expression of C++11
static constexpr input_kind value =
std::is_array<R>::value ? input_kind::char_array
: std::is_pointer<D>::value ? input_kind::c_string
@@ -55,6 +56,7 @@ struct classify_input
: (is_bytes && (!is_rvalue || is_string_view)) ? input_kind::borrow_range
: is_bytes ? input_kind::copy_range
: input_kind::adapter;
// NOLINTEND(readability-avoid-nested-conditional-operator)
};
/// std::basic_string guarantees a NUL at data()[size()] (the parser's sentinel)
@@ -66,7 +68,7 @@ struct is_std_string<std::basic_string<char, Traits, Alloc>> : std::true_type {}
/// drain a json input adapter (UTF-16/32 inputs arrive as UTF-8)
template<typename Adapter>
std::string collect_adapter(Adapter&& ia)
std::string collect_adapter(Adapter ia)
{
std::string buf;
for (;;)

View File

@@ -396,7 +396,7 @@ class basic_json_document
{
return;
}
using node = detail::view::node;
using detail::view::node;
document_data& d = *m_data;
// allocate everything first, so that an exception leaves the document
@@ -511,15 +511,16 @@ class basic_json_document
}
template<typename T>
void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
{
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
build(s.size() == 0 ? "" : reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size()), ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
detail::view::is_std_string<typename std::decay<T>::type>::value || s.size() == 0);
const auto size = static_cast<std::size_t>(s.size());
build(size == 0 ? "" : reinterpret_cast<const char*>(s.data()), size, ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
detail::view::is_std_string<T>::value || size == 0);
}
template<typename T>
void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
{
build_owned(std::string(reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size())), ae, c, tc); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
}
@@ -561,9 +562,9 @@ class basic_json_document
}
template<typename T>
static std::string collect_impl(T&& s, std::true_type /*contiguous*/)
static std::string collect_impl(const T& s, std::true_type /*contiguous*/)
{
return std::string(reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size())); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
return {reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size())}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
}
template<typename T>
@@ -572,7 +573,7 @@ class basic_json_document
return detail::view::collect_adapter(detail::input_adapter(std::forward<T>(s)));
}
std::unique_ptr<document_data, document_data::deleter> m_data{};
std::unique_ptr<document_data, document_data::deleter> m_data{}; // NOLINT(readability-redundant-member-init)
};
/// a parsed JSON text for json

View File

@@ -1712,6 +1712,7 @@ struct classify_input
#else
static constexpr bool is_string_view = false;
#endif
// NOLINTBEGIN(readability-avoid-nested-conditional-operator): a constant expression of C++11
static constexpr input_kind value =
std::is_array<R>::value ? input_kind::char_array
: std::is_pointer<D>::value ? input_kind::c_string
@@ -1719,6 +1720,7 @@ struct classify_input
: (is_bytes && (!is_rvalue || is_string_view)) ? input_kind::borrow_range
: is_bytes ? input_kind::copy_range
: input_kind::adapter;
// NOLINTEND(readability-avoid-nested-conditional-operator)
};
/// std::basic_string guarantees a NUL at data()[size()] (the parser's sentinel)
@@ -1730,7 +1732,7 @@ struct is_std_string<std::basic_string<char, Traits, Alloc>> : std::true_type {}
/// drain a json input adapter (UTF-16/32 inputs arrive as UTF-8)
template<typename Adapter>
std::string collect_adapter(Adapter&& ia)
std::string collect_adapter(Adapter ia)
{
std::string buf;
for (;;)
@@ -2421,7 +2423,7 @@ class basic_json_document
{
return;
}
using node = detail::view::node;
using detail::view::node;
document_data& d = *m_data;
// allocate everything first, so that an exception leaves the document
@@ -2536,15 +2538,16 @@ class basic_json_document
}
template<typename T>
void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::borrow_range> /*unused*/)
{
// std::basic_string guarantees data()[size()] == 0: use it as sentinel
build(s.size() == 0 ? "" : reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size()), ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
detail::view::is_std_string<typename std::decay<T>::type>::value || s.size() == 0);
const auto size = static_cast<std::size_t>(s.size());
build(size == 0 ? "" : reinterpret_cast<const char*>(s.data()), size, ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
detail::view::is_std_string<T>::value || size == 0);
}
template<typename T>
void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant<input_kind, input_kind::copy_range> /*unused*/)
{
build_owned(std::string(reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size())), ae, c, tc); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
}
@@ -2586,9 +2589,9 @@ class basic_json_document
}
template<typename T>
static std::string collect_impl(T&& s, std::true_type /*contiguous*/)
static std::string collect_impl(const T& s, std::true_type /*contiguous*/)
{
return std::string(reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size())); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
return {reinterpret_cast<const char*>(s.data()), static_cast<std::size_t>(s.size())}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
}
template<typename T>
@@ -2597,7 +2600,7 @@ class basic_json_document
return detail::view::collect_adapter(detail::input_adapter(std::forward<T>(s)));
}
std::unique_ptr<document_data, document_data::deleter> m_data{};
std::unique_ptr<document_data, document_data::deleter> m_data{}; // NOLINT(readability-redundant-member-init)
};
/// a parsed JSON text for json

View File

@@ -62,7 +62,7 @@ std::string view_exception(const std::string& text, bool comments = false, bool
// a small deterministic generator of documents
struct generator
{
std::mt19937 rng{5295};
std::mt19937 rng{5295}; // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
int r(int n)
{
@@ -71,7 +71,7 @@ struct generator
void str(std::string& o)
{
static const char* const pieces[] = {"a", "Z", " ", "\\n", "\\\"", "\\u00e9", "\\ud83d\\ude00", "\xc3\xa9", "\xe3\x81\x82", "long text beyond the first sixteen bytes"};
static const char* const pieces[] = {"a", "Z", " ", "\\n", "\\\"", "\\u00e9", "\\ud83d\\ude00", "\xc3\xa9", "\xe3\x81\x82", "long text beyond the first sixteen bytes"}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
o += '"';
for (int n = r(5); n > 0; --n)
{
@@ -82,7 +82,7 @@ struct generator
void value(std::string& o, int depth)
{
static const char* const scalars[] = {"0", "-1", "123456789012", "18446744073709551615", "18446744073709551616", "-9223372036854775809",
static const char* const scalars[] = {"0", "-1", "123456789012", "18446744073709551615", "18446744073709551616", "-9223372036854775809", // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
"1.5", "-2.25e-3", "1E2", "0.1", "true", "false", "null"
};
const int k = depth > 5 ? 2 + r(4) : r(6);
@@ -120,7 +120,7 @@ TEST_CASE("json_view")
{
for (const char* text :
{"null", "true", "false", "0", "-1", "18446744073709551615", "-9223372036854775808", "18446744073709551616", "1.5",
"\"\"", "\"text\"", "[]", "[1,2,3]", "{}", "{\"a\":1,\"b\":2}"
"\"\"", "\"text\"", "[]", "[1,2,3]", "{}", "{\"a\":1,\"b\":2}" // NOLINT(modernize-raw-string-literal)
})
{
CAPTURE(text);
@@ -187,7 +187,7 @@ TEST_CASE("json_view")
for (const char* text :
{
"", " ", "[", "]", "{", "[1,]", "{\"a\":1,}", "[1 2]", "{\"a\" 1}", "{1:2}", "tru", "nul", "fals", "truex", "-", "01", "1.", ".5", "1e",
"\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\ud800\"", "\"\\udc00\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"", "[1]x", "/", "/*", "[\n 1,\n x\n]",
"\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\ud800\"", "\"\\udc00\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"", "[1]x", "/", "/*", "[\n 1,\n x\n]", // NOLINT(modernize-raw-string-literal)
"1e400", "-1e400", "[1.7976931348623159e308]", "{\"a\":\n{\"b\": [1, 2,\n 3 x]}}"
})
{
@@ -355,7 +355,7 @@ TEST_CASE("json_view")
{
big += (i != 0 ? ",\"" : "\"") + std::to_string(i) + "\"";
}
big += "]";
big += ']';
json_document d = json_document::parse(big);
CHECK(d.node_count() == 10001);
const std::size_t before = d.memory_usage();
@@ -373,9 +373,9 @@ TEST_CASE("json_view")
{
escaped += (i != 0 ? ",\"a\\n" : "\"a\\n") + std::to_string(i) + "\"";
}
escaped += "]";
escaped += ']';
json_document reused = json_document::parse(escaped);
const std::string smaller = "[\"x\\ty\", [true, \"\\u00e4\"]]";
const std::string smaller = "[\"x\\ty\", [true, \"\\u00e4\"]]"; // NOLINT(modernize-raw-string-literal)
reused.read(smaller);
const std::size_t grown = reused.memory_usage();
reused.shrink_to_fit();
@@ -383,7 +383,7 @@ TEST_CASE("json_view")
CHECK(reused.root().materialize() == json::parse(smaller));
// a small document stays in the storage block of the header
json_document small = json_document::parse("[1,[2,3],{\"a\":\"b\\n\"}]");
json_document small = json_document::parse("[1,[2,3],{\"a\":\"b\\n\"}]"); // NOLINT(modernize-raw-string-literal)
small.shrink_to_fit();
CHECK(small.root().materialize() == json::parse("[1,[2,3],{\"a\":\"b\\n\"}]"));
}