From d9c0fda82913ca39a0e803b04eb6603b312b7093 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 24 Oct 2026 14:39:04 +0200 Subject: [PATCH] State the frame stack invariants of json_sax_dom_parser as assertions clang-tidy's static analyzer analyzes the forwarding SAX handlers of the error recovery tests (RecoveringDomParser, BasicRecoveringParser) as entry points, with the state of their json_sax_dom_parser member unknown. It then assumes a frame stack of capacity 0 that grow() cannot enlarge (clang-analyzer-security.ArrayBound) and a collected container without an enclosing frame (clang-analyzer-core.NonNullParamChecker). Neither can happen: the stack starts with local_capacity frames, and only a container inside another one is collected. Assert both, which also lets the analyzer discard these paths. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/input/json_sax.hpp | 4 ++++ single_include/nlohmann/json.hpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 7c7021c63..23df9925c 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -749,6 +749,8 @@ class json_sax_dom_parser JSON_HEDLEY_NEVER_INLINE void grow() { + // the stack starts with local_capacity frames and only grows + JSON_ASSERT(capacity != 0); const std::size_t new_capacity = capacity * 2; std::unique_ptr new_heap(new frame[new_capacity]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) std::copy(data, data + count, new_heap.get()); @@ -914,6 +916,8 @@ class json_sax_dom_parser /// directly if the value was the one too many. void place_container(const frame& f, frame* enclosing, BasicJsonType&& value, const bool enclosing_is_innermost) { + // a container without a slot of its own is collected for an enclosing one + JSON_ASSERT(f.slot != nullptr || enclosing != nullptr); if (f.slot != nullptr) { *f.slot = std::move(value); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 9a63b2360..1bfd56c43 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -16529,6 +16529,8 @@ class json_sax_dom_parser JSON_HEDLEY_NEVER_INLINE void grow() { + // the stack starts with local_capacity frames and only grows + JSON_ASSERT(capacity != 0); const std::size_t new_capacity = capacity * 2; std::unique_ptr new_heap(new frame[new_capacity]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) std::copy(data, data + count, new_heap.get()); @@ -16694,6 +16696,8 @@ class json_sax_dom_parser /// directly if the value was the one too many. void place_container(const frame& f, frame* enclosing, BasicJsonType&& value, const bool enclosing_is_innermost) { + // a container without a slot of its own is collected for an enclosing one + JSON_ASSERT(f.slot != nullptr || enclosing != nullptr); if (f.slot != nullptr) { *f.slot = std::move(value);