From 3bb551f46f95e5214231611d97e8856d394b6f75 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 5 Sep 2026 16:34:21 +0200 Subject: [PATCH] Fix swap(array_t&)/swap(object_t&) to update parent pointers under JSON_DIAGNOSTICS Both overloads swapped the underlying container storage but never called set_parents(), leaving elements moved into *this with stale m_parent pointers (typically nullptr from the free-standing array_t/object_t). This produced wrong JSON Pointer paths in diagnostic messages and could trip assert_invariant() on subsequent copies. Mirrors the fix already applied in swap(reference other). Signed-off-by: Niels Lohmann --- include/nlohmann/json.hpp | 2 ++ single_include/nlohmann/json.hpp | 2 ++ tests/src/unit-diagnostics.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index be4ccb90f..f90999c22 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3573,6 +3573,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { using std::swap; swap(*(m_data.m_value.array), other); + set_parents(); } else { @@ -3589,6 +3590,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { using std::swap; swap(*(m_data.m_value.object), other); + set_parents(); } else { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 31bfb869d..6e38fbfa0 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -25001,6 +25001,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { using std::swap; swap(*(m_data.m_value.array), other); + set_parents(); } else { @@ -25017,6 +25018,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { using std::swap; swap(*(m_data.m_value.object), other); + set_parents(); } else { diff --git a/tests/src/unit-diagnostics.cpp b/tests/src/unit-diagnostics.cpp index 1e8ed6aa3..135ecf9d6 100644 --- a/tests/src/unit-diagnostics.cpp +++ b/tests/src/unit-diagnostics.cpp @@ -273,5 +273,36 @@ TEST_CASE("Regression tests for extended diagnostics") CHECK(j1["numbers"]["two"] == 2); CHECK(j1["string"] == "t"); } + + SECTION("Regression test - swap(array_t&)/swap(object_t&) must update JSON_DIAGNOSTICS parent pointers") + { + // swap(array_t&) + { + json j = json::array(); + json::array_t arr = {json::array({1})}; + j.swap(arr); + + // parent pointers of the moved-in elements must point into j, not + // into the now-defunct free-standing array_t + CHECK_THROWS_WITH_AS(j[0][0].get(), "[json.exception.type_error.302] (/0/0) type must be string, but is number", json::type_error); + + // must not trigger assert_invariant() in a debug/assert-enabled build + json const k = j; + CHECK(k == j); + } + + // swap(object_t&) + { + json o = json::object(); + json::object_t obj = {{"a", json::array({1})}}; + o.swap(obj); + + CHECK_THROWS_WITH_AS(o["a"][0].get(), "[json.exception.type_error.302] (/a/0) type must be string, but is number", json::type_error); + + // must not trigger assert_invariant() in a debug/assert-enabled build + json const p = o; + CHECK(p == o); + } + } }