diff --git a/include/nlohmann/detail/view/compare.hpp b/include/nlohmann/detail/view/compare.hpp index ab0e5bbef..1927e9de0 100644 --- a/include/nlohmann/detail/view/compare.hpp +++ b/include/nlohmann/detail/view/compare.hpp @@ -905,6 +905,8 @@ void resume_array(const lockstep_path& /*path*/, const A& /*a*/, const B& /*b*/, template bool floats_equal(const view_side& a, const view_side& b) { + // float_token_is_nan() reads at most the node length of the token, which is not null terminated + // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage) return (a.value_node()->extra == b.value_node()->extra && a.float_token() == b.float_token() && !(view_side::nav::editable() && float_token_is_nan(*a.value_node(), a.float_token().data()))) || std::equal_to()(a.floating(), b.floating()); } diff --git a/include/nlohmann/detail/view/document_data.hpp b/include/nlohmann/detail/view/document_data.hpp index c1e179e93..aea6c6972 100644 --- a/include/nlohmann/detail/view/document_data.hpp +++ b/include/nlohmann/detail/view/document_data.hpp @@ -158,7 +158,8 @@ struct document_data { const auto overlaps = [p, n](const char* b, std::size_t m) noexcept { - // (std::less: a total order also for pointers into different objects) + // (std::less: a total order also for pointers into different objects; std::less<> needs C++14) + // NOLINTNEXTLINE(modernize-use-transparent-functors) return n != 0 && m != 0 && std::less()(p, b + m) && std::less()(b, p + n); }; return overlaps(owned.data(), owned.capacity()) diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index af959c06b..233a55487 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -502,7 +502,8 @@ struct document_data { const auto overlaps = [p, n](const char* b, std::size_t m) noexcept { - // (std::less: a total order also for pointers into different objects) + // (std::less: a total order also for pointers into different objects; std::less<> needs C++14) + // NOLINTNEXTLINE(modernize-use-transparent-functors) return n != 0 && m != 0 && std::less()(p, b + m) && std::less()(b, p + n); }; return overlaps(owned.data(), owned.capacity()) @@ -4088,6 +4089,8 @@ void resume_array(const lockstep_path& /*path*/, const A& /*a*/, const B& /*b*/, template bool floats_equal(const view_side& a, const view_side& b) { + // float_token_is_nan() reads at most the node length of the token, which is not null terminated + // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage) return (a.value_node()->extra == b.value_node()->extra && a.float_token() == b.float_token() && !(view_side::nav::editable() && float_token_is_nan(*a.value_node(), a.float_token().data()))) || std::equal_to()(a.floating(), b.floating()); } diff --git a/tests/src/unit-json_patch.cpp b/tests/src/unit-json_patch.cpp index 6b38cbcd6..7ae82c1b7 100644 --- a/tests/src/unit-json_patch.cpp +++ b/tests/src/unit-json_patch.cpp @@ -2682,25 +2682,27 @@ TEST_CASE("JSON patch - consecutive operations with paths that share a prefix") const json doc = json::parse(R"({"a": [{"b": [1, 2, 3]}, {"b": [4, 5, 6]}], "c": {"d": {"e": 7}}})"); // an element inserted before the elements of an array moves them - CHECK(doc.patch(json::parse(R"([ + const json inserted = json::parse(R"([ {"op": "test", "path": "/a/0/b/2", "value": 3}, {"op": "add", "path": "/a/0", "value": {"b": [9]}}, {"op": "test", "path": "/a/1/b/2", "value": 3}, {"op": "test", "path": "/a/0/b/0", "value": 9}, {"op": "test", "path": "/a/2/b/0", "value": 4} - ])")) == json::parse(R"({"a": [{"b": [9]}, {"b": [1, 2, 3]}, {"b": [4, 5, 6]}], "c": {"d": {"e": 7}}})")); + ])"); + CHECK(doc.patch(inserted) == json::parse(R"({"a": [{"b": [9]}, {"b": [1, 2, 3]}, {"b": [4, 5, 6]}], "c": {"d": {"e": 7}}})")); // an element removed from the front - CHECK(doc.patch(json::parse(R"([ + const json removed_front = json::parse(R"([ {"op": "test", "path": "/a/1/b/2", "value": 6}, {"op": "remove", "path": "/a/0"}, {"op": "test", "path": "/a/0/b/2", "value": 6}, {"op": "replace", "path": "/a/0/b/2", "value": 60}, {"op": "test", "path": "/a/0/b/2", "value": 60} - ])")) == json::parse(R"({"a": [{"b": [4, 5, 60]}], "c": {"d": {"e": 7}}})")); + ])"); + CHECK(doc.patch(removed_front) == json::parse(R"({"a": [{"b": [4, 5, 60]}], "c": {"d": {"e": 7}}})")); // a value replaced above the path that was resolved before - CHECK(doc.patch(json::parse(R"([ + const json replaced_above = json::parse(R"([ {"op": "test", "path": "/c/d/e", "value": 7}, {"op": "replace", "path": "/c/d", "value": {"e": 8, "f": {"g": 9}}}, {"op": "test", "path": "/c/d/e", "value": 8}, @@ -2708,7 +2710,8 @@ TEST_CASE("JSON patch - consecutive operations with paths that share a prefix") {"op": "remove", "path": "/c/d/f"}, {"op": "add", "path": "/c/d/f", "value": [1]}, {"op": "test", "path": "/c/d/f/0", "value": 1} - ])")) == json::parse(R"({"a": [{"b": [1, 2, 3]}, {"b": [4, 5, 6]}], "c": {"d": {"e": 8, "f": [1]}}})")); + ])"); + CHECK(doc.patch(replaced_above) == json::parse(R"({"a": [{"b": [1, 2, 3]}, {"b": [4, 5, 6]}], "c": {"d": {"e": 8, "f": [1]}}})")); #if !defined(JSON_NOEXCEPTION) // a value removed above the path that was resolved before is gone diff --git a/tests/src/unit-json_view_builder.cpp b/tests/src/unit-json_view_builder.cpp index ea3c12de7..fed2b7a19 100644 --- a/tests/src/unit-json_view_builder.cpp +++ b/tests/src/unit-json_view_builder.cpp @@ -916,14 +916,15 @@ TEST_CASE("json_view document_data::holds() (the debug check of read())") SECTION("owned text") { - d->owned = "{\"a\":\"some text that does not fit a short string\"}"; + d->owned = R"({"a":"some text that does not fit a short string"})"; const char* const b = d->owned.data(); CHECK(d->holds(b, 1)); CHECK(d->holds(b + 5, 3)); CHECK(d->holds(b + d->owned.size() - 1, 1)); // also after clear(), which keeps the buffer that the next parse writes d->owned.clear(); - CHECK(d->holds(b, 4)); + // (b is only compared with the buffer, never read) + CHECK(d->holds(b, 4)); // NOLINT(clang-analyzer-cplusplus.InnerPointer) CHECK(!d->holds(other.data(), other.size())); CHECK(!d->holds(b, 0)); } @@ -946,7 +947,7 @@ TEST_CASE("json_view document_data::holds() (the debug check of read())") { d->edits.reset(new document_data::edit_state()); // NOLINT(cppcoreguidelines-owning-memory) CHECK(!d->holds(other.data(), other.size())); - d->edits->texts.emplace_back(new char[16]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + d->edits->texts.emplace_back(new char[16]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-owning-memory) d->edits->text_cap = 16; CHECK(d->holds(d->edits->texts.back().get() + 3, 3)); CHECK(!d->holds(other.data(), other.size())); diff --git a/tests/src/unit-recursion_depth_limit.cpp b/tests/src/unit-recursion_depth_limit.cpp index cf62d1218..2869a7241 100644 --- a/tests/src/unit-recursion_depth_limit.cpp +++ b/tests/src/unit-recursion_depth_limit.cpp @@ -494,7 +494,7 @@ TEST_CASE("walks over values nested around the recursion depth limit") for_each_value([](const std::string & kinds) { const json a = build(kinds, 1); - const std::hash hash; + const std::hash hash{}; CHECK(hash(a) == hash(json(a))); CHECK(hash(a) == hash(json::parse(expected_compact(kinds, "1")))); CHECK(hash(a) != hash(build(kinds, 2)));