From 379756e92ec315b3793154644dc3bfdce4ac4dbe Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 2 Nov 2026 19:08:09 +0100 Subject: [PATCH] json_view: no byte range in exceptions about a removed value #5990 dropped the found flag from diagnostics(), reasoning that a value value_path() does not find leaves no tokens, like the root. But the node of a value that is no longer part of the document still holds the bytes it had in the source, so with JSON_DIAGNOSTICS and JSON_DIAGNOSTIC_POSITIONS its exceptions gained "(bytes 1-4)" for text that is no longer there. The test #5997 added for exactly this case (unit-json_view_positions.cpp, "views of values that are no longer part of the document") has failed on develop since both were merged. Restore the flag: a value that is not found gets no context, the root keeps its byte range. Signed-off-by: Niels Lohmann --- include/nlohmann/detail/view/errors.hpp | 13 ++++++++++--- single_include/nlohmann/json_view.hpp | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/view/errors.hpp b/include/nlohmann/detail/view/errors.hpp index 3c97cef5c..100639478 100644 --- a/include/nlohmann/detail/view/errors.hpp +++ b/include/nlohmann/detail/view/errors.hpp @@ -230,7 +230,8 @@ void value_range(const document_data& d, const node* n, std::size_t& start, std: /// the context basic_json puts in front of the message of an exception about /// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then -/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root) +/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root); with +/// JSON_DIAGNOSTICS, nothing for a value that is no longer part of the document /// /// This and the throw helpers that take an error_site are templates, so that /// the path and range code is instantiated only where an exception can be @@ -251,8 +252,14 @@ std::string diagnostics(const error_site site) std::string context; #if JSON_DIAGNOSTICS std::vector tokens; - // a value that is not found leaves no tokens, like the root - static_cast(edited ? value_path(d, site.value(), tokens) : value_path(d, site.value(), tokens)); + // a value that is no longer part of the document has no path, and its + // node still holds the bytes it had in the source: no context at all + const bool found = edited ? value_path(d, site.value(), tokens) : value_path(d, site.value(), tokens); + if (!found) + { + // context is still empty; returning it keeps the copy elided (-Wnrvo) + return context; + } if (!tokens.empty()) { // the root value has no path, but still has a byte range diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index ea04acdbe..db26ed298 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -5059,7 +5059,8 @@ void value_range(const document_data& d, const node* n, std::size_t& start, std: /// the context basic_json puts in front of the message of an exception about /// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then -/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root) +/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root); with +/// JSON_DIAGNOSTICS, nothing for a value that is no longer part of the document /// /// This and the throw helpers that take an error_site are templates, so that /// the path and range code is instantiated only where an exception can be @@ -5080,8 +5081,14 @@ std::string diagnostics(const error_site site) std::string context; #if JSON_DIAGNOSTICS std::vector tokens; - // a value that is not found leaves no tokens, like the root - static_cast(edited ? value_path(d, site.value(), tokens) : value_path(d, site.value(), tokens)); + // a value that is no longer part of the document has no path, and its + // node still holds the bytes it had in the source: no context at all + const bool found = edited ? value_path(d, site.value(), tokens) : value_path(d, site.value(), tokens); + if (!found) + { + // context is still empty; returning it keeps the copy elided (-Wnrvo) + return context; + } if (!tokens.empty()) { // the root value has no path, but still has a byte range