Report one-character non-numeric array indices like longer ones (#5800)

* Report one-character non-numeric array indices like longer ones

A JSON pointer reference token that is not a number but has only one
character (e.g. "/a/x") was reported as out_of_range.404 ("unresolved
reference token"), because the "is not a number" check only ran for
tokens longer than one character; "/a/xy" got parse_error.109. Both now
throw parse_error.109. "-" and the empty token are still reported as
out_of_range.404. As a consequence, value(json_pointer, default) on an
array now throws for "/x" as it already did for "/xy".

Also document why ordered_map::erase's destroy/placement-new loop on
pair<const Key, T> is kept despite [basic.life]/8 before C++20.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Document the one-character array index change

Add 3.13.0 version-history entries to at, operator[], value, patch,
patch_inplace, and unflatten, and describe in exceptions.md which array
indices throw parse_error.109 and which out_of_range.404.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann authored and GitHub committed 2026-10-11 08:21:41 +02:00
1 parent 1649d9eda4
commit 2cca04ae6f
13 files changed
+95 -13

No files matched your search

+13 -3
View File
@@ -20283,7 +20283,7 @@ class json_pointer
{
ok, ///< @a s is a valid, representable array index
leading_zero, ///< @a s begins with '0' but has more than one character
not_a_number, ///< @a s does not begin with a digit
not_a_number, ///< @a s is neither empty nor "-" and does not begin with a digit
unresolved, ///< @a s could not be converted to an integer
exceeds_size_type ///< @a s converts to an integer that exceeds size_type
};
@@ -20310,8 +20310,10 @@ class json_pointer
return array_index_status::leading_zero;
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
// error condition (cf. RFC 6901, Sect. 4); this also covers single-
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty
// token are left to the conversion below and are reported as unresolved
if (JSON_HEDLEY_UNLIKELY(!s.empty() && s != "-" && !(s[0] >= '0' && s[0] <= '9')))
{
return array_index_status::not_a_number;
}
@@ -27883,6 +27885,14 @@ public:
// ^ ^
// first last
// Note on conformance: before C++20, [basic.life]/8 did not allow an
// object of a type with a const member (like value_type's const Key)
// to transparently replace the destroyed one, so strictly, accessing
// it through the vector's existing pointers would have required
// std::launder (which does not exist before C++17). C++20 dropped that
// condition (P1971R0, NB comment US 041). Compilers have always treated
// this pattern as intended, so it is kept deliberately.
// Since we cannot move const Keys, we re-construct them in place.
// We start at first and re-construct (viz. copy) the elements from
// the back of the vector. Example for the first iteration: