From b1628423a67b1e2b7906a1a7dbd9a542e8b9a9d9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 29 Sep 2026 00:36:57 +0200 Subject: [PATCH] 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 --- include/nlohmann/detail/view/input.hpp | 4 +++- include/nlohmann/json_view.hpp | 17 +++++++++-------- single_include/nlohmann/json_view.hpp | 21 ++++++++++++--------- tests/src/unit-json_view.cpp | 18 +++++++++--------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/include/nlohmann/detail/view/input.hpp b/include/nlohmann/detail/view/input.hpp index c43979f1f..e8a5e3148 100644 --- a/include/nlohmann/detail/view/input.hpp +++ b/include/nlohmann/detail/view/input.hpp @@ -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::value ? input_kind::char_array : std::is_pointer::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::true_type {} /// drain a json input adapter (UTF-16/32 inputs arrive as UTF-8) template -std::string collect_adapter(Adapter&& ia) +std::string collect_adapter(Adapter ia) { std::string buf; for (;;) diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index 544e54d99..4993eeb18 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -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 - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { // std::basic_string guarantees data()[size()] == 0: use it as sentinel - build(s.size() == 0 ? "" : reinterpret_cast(s.data()), static_cast(s.size()), ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) - detail::view::is_std_string::type>::value || s.size() == 0); + const auto size = static_cast(s.size()); + build(size == 0 ? "" : reinterpret_cast(s.data()), size, ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + detail::view::is_std_string::value || size == 0); } template - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { build_owned(std::string(reinterpret_cast(s.data()), static_cast(s.size())), ae, c, tc); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } @@ -561,9 +562,9 @@ class basic_json_document } template - 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(s.data()), static_cast(s.size())); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + return {reinterpret_cast(s.data()), static_cast(s.size())}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } template @@ -572,7 +573,7 @@ class basic_json_document return detail::view::collect_adapter(detail::input_adapter(std::forward(s))); } - std::unique_ptr m_data{}; + std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) }; /// a parsed JSON text for json diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index 4c011d238..f24b5036d 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -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::value ? input_kind::char_array : std::is_pointer::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::true_type {} /// drain a json input adapter (UTF-16/32 inputs arrive as UTF-8) template -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 - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { // std::basic_string guarantees data()[size()] == 0: use it as sentinel - build(s.size() == 0 ? "" : reinterpret_cast(s.data()), static_cast(s.size()), ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) - detail::view::is_std_string::type>::value || s.size() == 0); + const auto size = static_cast(s.size()); + build(size == 0 ? "" : reinterpret_cast(s.data()), size, ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + detail::view::is_std_string::value || size == 0); } template - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { build_owned(std::string(reinterpret_cast(s.data()), static_cast(s.size())), ae, c, tc); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } @@ -2586,9 +2589,9 @@ class basic_json_document } template - 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(s.data()), static_cast(s.size())); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + return {reinterpret_cast(s.data()), static_cast(s.size())}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } template @@ -2597,7 +2600,7 @@ class basic_json_document return detail::view::collect_adapter(detail::input_adapter(std::forward(s))); } - std::unique_ptr m_data{}; + std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) }; /// a parsed JSON text for json diff --git a/tests/src/unit-json_view.cpp b/tests/src/unit-json_view.cpp index 0fa2fdde8..37b861e45 100644 --- a/tests/src/unit-json_view.cpp +++ b/tests/src/unit-json_view.cpp @@ -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\"}]")); }