From 5185181552f5bc250b66ed4ae77c79c9f6653d7c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 24 Sep 2026 21:59:00 +0200 Subject: [PATCH] Test that the destructor uses the provided allocator Adds a regression test for #4842: destroying a nested array or object must allocate its temporary stack through the basic_json allocator, not std::allocator. Signed-off-by: Niels Lohmann --- tests/src/unit-allocator.cpp | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/src/unit-allocator.cpp b/tests/src/unit-allocator.cpp index bc283ab28..9a5bf9db1 100644 --- a/tests/src/unit-allocator.cpp +++ b/tests/src/unit-allocator.cpp @@ -602,3 +602,56 @@ TEST_CASE("bad my_allocator::construct") j["test"].push_back("should not leak"); } } + +namespace +{ +std::size_t counting_allocator_allocations = 0; + +template +struct counting_allocator : std::allocator +{ + using std::allocator::allocator; + + T* allocate(std::size_t n) + { + ++counting_allocator_allocations; + return std::allocator::allocate(n); + } + + template + struct rebind + { + using other = counting_allocator; + }; +}; +} // namespace + +TEST_CASE("destructor uses the provided allocator") +{ + // see https://github.com/nlohmann/json/issues/4842 + using counting_json = nlohmann::basic_json; + + SECTION("array") + { + auto* j = new counting_json({1, {2, {3, 4}}, 5}); // NOLINT(cppcoreguidelines-owning-memory) + const auto before = counting_allocator_allocations; + delete j; // NOLINT(cppcoreguidelines-owning-memory) + // the stack used to destroy the children is allocated with the provided allocator + CHECK(counting_allocator_allocations > before); + } + + SECTION("object") + { + auto* j = new counting_json({{"a", {{"b", {1, 2}}}}, {"c", 3}}); // NOLINT(cppcoreguidelines-owning-memory) + const auto before = counting_allocator_allocations; + delete j; // NOLINT(cppcoreguidelines-owning-memory) + CHECK(counting_allocator_allocations > before); + } +}