Merge branch 'develop' into techdebt/5725-lint-cleanup

Conflicts:
- include/nlohmann/json.hpp: took develop's json_literals.hpp include (#5610) without the GCC C++20 -Wignored-attributes pragma pop this branch removes
- single_include/nlohmann/json.hpp: regenerated with make amalgamate

Also dropped the redundant semicolon from six CAPTURE() call sites that
develop added in unit-allocator, unit-class_lexer, and unit-comparison,
as this branch removes -Wno-extra-semi-stmt.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-30 20:41:18 +02:00
80 changed files with 4872 additions and 641 deletions
+46
View File
@@ -17,6 +17,7 @@
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <map>
#include <string>
@@ -825,6 +826,24 @@ Json nest(Json j, const std::size_t depth)
}
return j;
}
// orders keys case-insensitively, so "key" and "KEY" compare equivalent
// (neither less than the other) although they are not equal
struct case_insensitive_less
{
bool operator()(const std::string& a, const std::string& b) const
{
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
[](unsigned char x, unsigned char y)
{
return std::tolower(x) < std::tolower(y);
});
}
};
template<class Key, class Value, class /*Compare*/, class Allocator>
using case_insensitive_map = std::map<Key, Value, case_insensitive_less, Allocator>;
using ci_json = nlohmann::basic_json<case_insensitive_map>;
} // namespace
TEST_CASE("equality of objects whose entries have no fixed order")
@@ -872,6 +891,33 @@ TEST_CASE("equality of objects whose entries have no fixed order")
}
}
TEST_CASE("equality of an object whose comparator treats different keys as equivalent")
{
// https://github.com/nlohmann/json/issues/5655: past the nesting bound,
// the entries are compared without the call stack, and a key that finds
// no counterpart at the same position is looked up with find(), which
// uses the object's own comparator. A case-insensitive comparator then
// finds "KEY" for "key" and must not accept that pair as a match - the
// object type's own operator==, like std::map's, compares keys with ==.
ci_json a = ci_json::object();
a["key"] = 1;
ci_json b = ci_json::object();
b["KEY"] = 1;
// sanity check: the object type's own comparison already disagrees
CHECK_FALSE(a.get_ref<const ci_json::object_t&>() == b.get_ref<const ci_json::object_t&>());
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
{
CAPTURE(depth)
const ci_json x = nest(a, depth);
const ci_json y = nest(b, depth);
CHECK_FALSE(x == y);
CHECK(x != y);
}
}
TEST_CASE("containers are compared element by element")
{
// Containers nested deeper than a bound are compared without the call