From 0c4462676dfe0787ed6788079821323a40416234 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 4 Oct 2026 12:23:53 +0200 Subject: [PATCH 1/2] Document options to reduce compile times (#5611) * Document options to reduce compile times Add an integration page that collects the ways to reduce compile times with measurements: json_fwd.hpp in headers, JSON_NO_AUTOMATIC_UDLS, explicit instantiation with extern template, modules, and precompiled headers, and notes that JSON_NO_IO and JSON_USE_GLOBAL_UDLS have no measurable effect. Signed-off-by: Niels Lohmann * Include only json_literals.hpp in the JSON_NO_AUTOMATIC_UDLS examples json_literals.hpp includes json.hpp itself, so including both is redundant. Signed-off-by: Niels Lohmann --------- Signed-off-by: Niels Lohmann --- .../docs/api/macros/json_no_automatic_udls.md | 3 +- docs/mkdocs/docs/integration/compile_times.md | 149 ++++++++++++++++++ docs/mkdocs/docs/integration/index.md | 2 +- docs/mkdocs/mkdocs.yml | 1 + 4 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 docs/mkdocs/docs/integration/compile_times.md diff --git a/docs/mkdocs/docs/api/macros/json_no_automatic_udls.md b/docs/mkdocs/docs/api/macros/json_no_automatic_udls.md index acb6a356e..1235d634d 100644 --- a/docs/mkdocs/docs/api/macros/json_no_automatic_udls.md +++ b/docs/mkdocs/docs/api/macros/json_no_automatic_udls.md @@ -43,9 +43,9 @@ By default, `#!cpp JSON_NO_AUTOMATIC_UDLS` is not defined, and ` // this file uses the literals, so it includes them explicitly + // (the header includes itself) #include int main() @@ -62,6 +62,7 @@ By default, `#!cpp JSON_NO_AUTOMATIC_UDLS` is not defined, and `` pays for parsing the header and instantiating what it uses. This page lists the options to reduce +that cost, ordered by how much they typically save. + +!!! info "Measurements" + + The numbers below are medians of nine runs compiling a single translation unit with `-std=c++17 -c` against the + single-header version, with Apple clang and GCC 16 on macOS (Apple silicon). They show the order of magnitude to + expect; measure your own code before and after a change. + +## Include `json_fwd.hpp` in headers + +Header files that only need to *name* the `json` type — for function declarations, members held by pointer or +reference, or friend declarations — can include `` instead of ``. It only +forward-declares `basic_json`, `json`, `ordered_json`, `json_pointer`, and `adl_serializer`. The translation units that +actually use the values then include ``. + +```cpp title="person.hpp" +#pragma once +#include + +struct person; +void to_json(nlohmann::json& j, const person& p); +void from_json(const nlohmann::json& j, person& p); +``` + +```cpp title="person.cpp" +#include "person.hpp" +#include + +void to_json(nlohmann::json& j, const person& p) { /* ... */ } +void from_json(const nlohmann::json& j, person& p) { /* ... */ } +``` + +| Compiler | `json.hpp` (`-O0`) | `json_fwd.hpp` (`-O0`) | Change | +|-------------|-------------------:|-----------------------:|-------:| +| Apple clang | 704 ms | 329 ms | −53% | +| GCC 16 | 779 ms | 242 ms | −69% | + +This is the most effective option, because it avoids the full header in every translation unit that includes +*your* headers. + +## Opt out of the automatic user-defined string literals + +The user-defined string literals [`operator""_json`](../api/operator_literal_json.md) and +[`operator""_json_pointer`](../api/operator_literal_json_pointer.md) are ordinary inline functions whose bodies call the +parser. As `` includes them by default, every translation unit instantiates the parser, even if it +never parses anything itself. + +Define [`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md) for the whole project and include +`` instead of `` in the files that use the literals (it includes +`` itself): + +```cmake +target_compile_definitions(my_target PRIVATE JSON_NO_AUTOMATIC_UDLS) +``` + +```cpp +#include // only where "..."_json is used; includes +``` + +The saving applies to translation units that do not parse JSON, for example ones that define types and their +conversions or only pass `json` values around: + +| Compiler | Translation unit | Default (`-O0` / `-O2`) | `JSON_NO_AUTOMATIC_UDLS` (`-O0` / `-O2`) | Change | +|-------------|------------------|------------------------:|-----------------------------------------:|------------:| +| Apple clang | model | 776 ms / 846 ms | 629 ms / 692 ms | −19% / −18% | +| GCC 16 | model | 1022 ms / 1120 ms | 882 ms / 965 ms | −14% / −14% | +| Apple clang | parsing | 992 ms / 1815 ms | 1006 ms / 1823 ms | +1% / 0% | +| GCC 16 | parsing | 2018 ms / 3420 ms | 1990 ms / 3454 ms | −1% / +1% | + +Translation units that include only the header save up to a third. Translation units that parse anyway instantiate +the parser regardless and see no difference. + +## Instantiate `basic_json` once + +Each translation unit instantiates the member functions of `nlohmann::json` it uses. An explicit instantiation +declaration tells the compiler that the non-template members are instantiated elsewhere, so it can skip them: + +```cpp title="json_instance.hpp" +#pragma once +#include + +extern template class nlohmann::basic_json<>; +``` + +```cpp title="json_instance.cpp" +#include "json_instance.hpp" + +template class nlohmann::basic_json<>; +``` + +Include `json_instance.hpp` instead of `` and compile and link `json_instance.cpp` once. + +| Compiler | Translation unit | Default (`-O0` / `-O2`) | `extern template` (`-O0` / `-O2`) | Change | +|-------------|---------------------|------------------------:|----------------------------------:|------------:| +| Apple clang | parsing | 992 ms / 1815 ms | 953 ms / 1625 ms | −4% / −10% | +| GCC 16 | parsing | 2018 ms / 3420 ms | 1522 ms / 2728 ms | −25% / −20% | +| Apple clang | `json_instance.cpp` | — | 2166 ms / 4660 ms | — | +| GCC 16 | `json_instance.cpp` | — | 5085 ms / 10616 ms | — | + +Notes: + +- The saving grows with the number of translation units that use `json`, while the instantiation translation unit is + compiled only once (and is rarely recompiled, as it does not depend on your code). +- Member function templates (such as `get()`, `parse(InputType&&)`, or `value(key, default)`) are not covered by + the explicit instantiation and are still instantiated where they are used. +- The declaration covers exactly `nlohmann::json`. Add the same lines for `nlohmann::ordered_json` + (`nlohmann::basic_json`) or your own `basic_json` specializations if you use them. + +## Use C++20 modules + +With a toolchain that supports named modules, `import nlohmann.json;` compiles the library once into a module and +avoids parsing the header in every translation unit. See [Modules](../features/modules.md) for requirements and known +issues. Module support is experimental and currently depends heavily on the compiler version. + +## Use precompiled headers + +Build systems can precompile `` together with other stable headers, for example with CMake's +[`target_precompile_headers`](https://cmake.org/cmake/help/latest/command/target_precompile_headers.html): + +```cmake +target_precompile_headers(my_target PRIVATE ) +``` + +This removes the cost of parsing the header, but not of instantiating templates in each translation unit, so it +combines well with the options above. + +## Options without effect on compile times + +Some configuration macros change what the library declares, but do not measurably change compile times: + +| Macro | Apple clang, model (`-O0` / `-O2`) | GCC 16, model (`-O0` / `-O2`) | +|------------------------------------------------------------------------|-----------------------------------:|------------------------------:| +| default | 776 ms / 846 ms | 1022 ms / 1120 ms | +| [`JSON_NO_IO`](../api/macros/json_no_io.md) | 764 ms / 836 ms | 1022 ms / 1117 ms | +| [`JSON_USE_GLOBAL_UDLS`](../api/macros/json_use_global_udls.md)`=0` | 763 ms / 852 ms | 1019 ms / 1106 ms | + +`JSON_USE_GLOBAL_UDLS` only controls *where* the literals are declared; to avoid their cost, use +`JSON_NO_AUTOMATIC_UDLS` instead. + +## See also + +- [`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md) - do not include the user-defined string + literals automatically +- [Modules](../features/modules.md) - C++20 module support +- [Header only](index.md) - including the library diff --git a/docs/mkdocs/docs/integration/index.md b/docs/mkdocs/docs/integration/index.md index 5c3995a95..846c15fdc 100644 --- a/docs/mkdocs/docs/integration/index.md +++ b/docs/mkdocs/docs/integration/index.md @@ -45,7 +45,7 @@ Clang). You can further use file [`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp) -for forward declarations, and file +for forward declarations (see [Compile times](compile_times.md)), and file [`single_include/nlohmann/json_literals.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_literals.hpp) for the user-defined string literals if you define [`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md). diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 3cb25ba9b..dcb94864d 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -108,6 +108,7 @@ nav: - integration/cmake.md - integration/package_managers.md - integration/pkg-config.md + - integration/compile_times.md - API Documentation: - basic_json: - 'Overview': api/basic_json/index.md From d8d47be4a5a46da22b3d8deee8e4e0b114aaf0e7 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 4 Oct 2026 17:18:19 +0200 Subject: [PATCH 2/2] Fix CI on develop after merging the ready-to-merge PRs (#5754) * Keep the serializer conversion for objects whose keys cannot be converted #5591 added a test converting nlohmann::json into a basic_json whose string type cannot be constructed from std::string. That instantiates convert_iteratively(), whose members.emplace_back(next.key(), ...) needs exactly that key conversion, and broke the build of unit-alt-string. Dispatch on the key's constructibility and leave such conversions to the serializers, as the levels above the nesting bound already do (#3425). Signed-off-by: Niels Lohmann * Fix the remaining CI failures on develop - unit-wstring: with a 16-bit wchar_t (Windows), a lone surrogate is reported as the ill-formed byte 0xFF since #5704; the std::wstring expectations still had the previous . - ci_single_binaries: json_literals.hpp (#5610) and json.hpp include each other on purpose, and IWYU, not following the cycle, asks to replace json.hpp with json_fwd.hpp. Report its findings without failing the build, as already done for json.hpp. Signed-off-by: Niels Lohmann * Fix the library warnings and noexcept specifications from the merged PRs - binary_reader: rename the error_handler constructor parameter, which shadowed the member (-Wshadow, -Wshadow-field-in-constructor; #5746) - basic_json(copy_construct_tag, ...): declare it noexcept when copying the base class is (GCC 16 -Wnoexcept; #5690) - the scalar-on-left legacy comparison operators: noexcept only when converting the scalar is, like their member counterparts (#5682, #5751) - compare_leaves: use std::is_eq/is_lt/is_gt instead of comparing a std::partial_ordering with 0 (-Wzero-as-null-pointer-constant; #5686) - serializer: silence MSVC C4127 for the EnsureAscii template parameter (#5741, #5746) - clang-tidy: return the sanitized reference in binary_writer, take the key of ordered_map::find_impl by const reference (#5727), and mark the switches over parse_array_index (#5728) - ordered_map: keep for std::allocator (IWYU) Signed-off-by: Niels Lohmann * Split unit-conversions.cpp so MinGW can link it clang 18 with the MinGW linker failed to link test-conversions_cpp17 ("relocation truncated to fit: IMAGE_REL_AMD64_REL32"). As windows.yml recommends, keep the objects small by splitting the test file. Signed-off-by: Niels Lohmann * Fix the tests added by the merged PRs for all CI configurations - discard the results of dump() and from_*() in CHECK_THROWS with utils::ignore_return_value (GCC -Werror=unused-result) - give unit-bson's huge_string_t a default constructor (MSVC C2512, GCC 5, clang 3.5) - unit-disabled_exceptions: use the literals namespace when the global UDLs are off (ci_test_noglobaludls; #5700) - unit-binary_utf8_strict: expect the JSON pointer prefix with JSON_DIAGNOSTICS (#5741) - skip the tests that rely on exceptions under JSON_NOEXCEPTION (#5678, #5732) - clang-tidy and clang -Werror: static test data, CAPTURE(...);, const-correctness, use-after-move alias, unused conversion operator, a missing include Signed-off-by: Niels Lohmann * Title the macro examples and add JSON_STRICT_BINARY_UTF8 to the docset The documentation style check requires "Example: ..." titles on pages with several examples (#5741, #5591) and a docset entry for every macro page. Signed-off-by: Niels Lohmann * Regenerate BUILD.bazel and nlohmann_json.natvis Signed-off-by: Niels Lohmann #5746 added detail/output/error_handler.hpp and #5741 the json_abi_sbu8 ABI tag. * Install libidn11 for the CMake 3.5.0 binary in ci_cmake_flags Signed-off-by: Niels Lohmann #5733 moved ci_cmake_options from ubuntu:focal to ubuntu:24.04, which no longer ships libidn.so.11; the CMake 3.5.0 release binary links against it, so every ci_cmake_flags run has failed since. Install focal's libidn11 package for that matrix entry only. * Suppress Infer's false STACK_VARIABLE_ADDRESS_ESCAPE in get_impl get_impl() returns its local by value. A test added by the merged PRs instantiates it with a type Infer misreads, so ci_infer reported the 2021 code for the first time. Signed-off-by: Niels Lohmann --------- Signed-off-by: Niels Lohmann --- .github/workflows/ubuntu.yml | 7 + BUILD.bazel | 1 + cmake/ci.cmake | 10 +- docs/docset/docSet.sql | 1 + .../api/macros/json_strict_binary_utf8.md | 4 +- .../macros/json_use_implicit_conversions.md | 4 +- .../nlohmann/detail/input/binary_reader.hpp | 6 +- include/nlohmann/detail/json_pointer.hpp | 4 + .../nlohmann/detail/output/binary_writer.hpp | 6 +- include/nlohmann/detail/output/serializer.hpp | 8 + include/nlohmann/json.hpp | 56 +- include/nlohmann/ordered_map.hpp | 4 +- nlohmann_json.natvis | 3840 +++++++++++++++++ single_include/nlohmann/json.hpp | 84 +- tests/src/test_utils.hpp | 3 +- tests/src/unit-allocator.cpp | 2 +- tests/src/unit-binary_utf8_error_handler.cpp | 120 +- tests/src/unit-binary_utf8_strict.cpp | 36 +- tests/src/unit-bjdata.cpp | 2 +- tests/src/unit-bon8.cpp | 2 + tests/src/unit-bson.cpp | 4 +- tests/src/unit-cbor.cpp | 8 +- tests/src/unit-class_parser.cpp | 6 +- tests/src/unit-comparison.cpp | 4 +- ...-conversions.cpp => unit-conversions1.cpp} | 805 ---- tests/src/unit-conversions2.cpp | 868 ++++ tests/src/unit-custom-base-class.cpp | 2 +- tests/src/unit-disabled_exceptions.cpp | 3 + tests/src/unit-element_access2.cpp | 9 +- tests/src/unit-modifiers.cpp | 2 + tests/src/unit-msgpack.cpp | 2 +- tests/src/unit-ordered_map.cpp | 6 +- tests/src/unit-ubjson.cpp | 2 +- tests/src/unit-wstring.cpp | 4 +- 34 files changed, 4978 insertions(+), 947 deletions(-) rename tests/src/{unit-conversions.cpp => unit-conversions1.cpp} (52%) create mode 100644 tests/src/unit-conversions2.cpp diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index f5addb92e..1e4a72870 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -111,6 +111,13 @@ jobs: steps: - name: Install build-essential run: apt-get update ; apt-get install -y build-essential unzip wget git + # the CMake 3.5.0 release binary that ci_cmake_flags downloads links + # against libidn.so.11, which Ubuntu 24.04 no longer ships + - name: Install libidn11 for CMake 3.5.0 + if: matrix.target == 'ci_cmake_flags' + run: | + wget -q http://archive.ubuntu.com/ubuntu/pool/main/libi/libidn/libidn11_1.33-2.2ubuntu2_amd64.deb + dpkg -i libidn11_1.33-2.2ubuntu2_amd64.deb - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false diff --git a/BUILD.bazel b/BUILD.bazel index a9b09fd79..3dfffad1e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -57,6 +57,7 @@ cc_library( "include/nlohmann/detail/meta/type_traits.hpp", "include/nlohmann/detail/meta/void_t.hpp", "include/nlohmann/detail/output/binary_writer.hpp", + "include/nlohmann/detail/output/error_handler.hpp", "include/nlohmann/detail/output/output_adapters.hpp", "include/nlohmann/detail/output/serializer.hpp", "include/nlohmann/detail/recursion_depth_limit.hpp", diff --git a/cmake/ci.cmake b/cmake/ci.cmake index 2aceb5afd..d0af68786 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -596,8 +596,9 @@ foreach(SRC_FILE ${SRC_FILES}) add_executable(single_${RELATIVE_SRC_FILE} EXCLUDE_FROM_ALL ${PROJECT_BINARY_DIR}/src_single/${RELATIVE_SRC_FILE}.cpp) target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include) target_compile_features(single_${RELATIVE_SRC_FILE} PRIVATE cxx_std_11) - if(RELATIVE_SRC_FILE STREQUAL "json") - # see below: report json.hpp's diagnostics without --error, so they do not fail the build + if(RELATIVE_SRC_FILE STREQUAL "json" OR RELATIVE_SRC_FILE STREQUAL "json_literals") + # see below: report the diagnostics of json.hpp and json_literals.hpp without --error, so they + # do not fail the build set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_TOOL} -Xiwyu --max_line_length=300) else() set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path_and_options}") @@ -611,7 +612,10 @@ foreach(SRC_FILE ${SRC_FILES}) # reporting its diagnostics (informational, via CXX_INCLUDE_WHAT_YOU_USE above) but exclude it # from the hard gate below so a fresh IWYU/compiler combination does not fail this target on a # nondeterministic suggestion for a header that already re-exports everything on purpose. - if(NOT RELATIVE_SRC_FILE STREQUAL "json") + # json_literals.hpp and json.hpp include each other on purpose (json.hpp includes it at its end + # unless JSON_NO_AUTOMATIC_UDLS is defined), and IWYU, not following the cycle, suggests replacing + # json.hpp with json_fwd.hpp although the literals need the complete basic_json; exclude it, too. + if(NOT RELATIVE_SRC_FILE STREQUAL "json" AND NOT RELATIVE_SRC_FILE STREQUAL "json_literals") list(APPEND single_binaries_tus src_single/${RELATIVE_SRC_FILE}.cpp) endif() endforeach() diff --git a/docs/docset/docSet.sql b/docs/docset/docSet.sql index 09802e43e..801ade93a 100644 --- a/docs/docset/docSet.sql +++ b/docs/docset/docSet.sql @@ -242,6 +242,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_THREAD_LOCAL', 'Macro INSERT INTO searchIndex(name, type, path) VALUES ('JSON_PRECISE_STREAM_POSITION', 'Macro', 'api/macros/json_precise_stream_position/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_LIBRARY_VERSION_CHECK', 'Macro', 'api/macros/json_skip_library_version_check/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_UNSUPPORTED_COMPILER_CHECK', 'Macro', 'api/macros/json_skip_unsupported_compiler_check/index.html'); +INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_BINARY_UTF8', 'Macro', 'api/macros/json_strict_binary_utf8/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_NUL_HANDLING', 'Macro', 'api/macros/json_strict_nul_handling/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_THROW_USER', 'Macro', 'api/macros/json_throw_user/index.html'); INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api/macros/json_throw_user/index.html'); diff --git a/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md b/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md index ed7bbe6b3..4c573d56e 100644 --- a/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md +++ b/docs/mkdocs/docs/api/macros/json_strict_binary_utf8.md @@ -54,7 +54,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i ## Examples -??? example "Default behavior (macro not defined)" +??? example "Example: default behavior (macro not defined)" Without the macro, the bytes are written unchanged: @@ -70,7 +70,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i } ``` -??? example "Opt-in check (macro defined to 1)" +??? example "Example: opt-in check (macro defined to 1)" With the macro, ill-formed UTF-8 is rejected: diff --git a/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md b/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md index 11bf74b22..113344412 100644 --- a/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md +++ b/docs/mkdocs/docs/api/macros/json_use_implicit_conversions.md @@ -44,7 +44,7 @@ By default, implicit conversions are enabled. ## Examples -??? example +??? example "Example: implicit conversion" This is an example for an implicit conversion: @@ -61,7 +61,7 @@ By default, implicit conversions are enabled. auto s = j.get(); ``` -??? example "Conversion between `basic_json` specializations" +??? example "Example: conversion between `basic_json` specializations" A `basic_json` specialization with a different string type is also no longer converted implicitly when `JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`: diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 1a78a6502..d6b45909a 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -110,15 +110,15 @@ class binary_reader @param[in] adapter input adapter to read from @param[in] format the binary format to parse - @param[in] error_handler how to treat text strings and object keys that + @param[in] error_handler_ how to treat text strings and object keys that are not well-formed UTF-8; none of the supported formats requires a decoder to reject those, so the default is to @ref error_handler_t::keep them unchanged, as every binary reader did before this parameter existed */ explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json, - const error_handler_t error_handler = error_handler_t::keep) noexcept - : ia(std::move(adapter)), input_format(format), error_handler(error_handler) + const error_handler_t error_handler_ = error_handler_t::keep) noexcept + : ia(std::move(adapter)), input_format(format), error_handler(error_handler_) { (void)detail::is_sax_static_asserts {}; } diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 788af1047..30edb4a3b 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -322,6 +322,8 @@ class json_pointer typename BasicJsonType::size_type idx{}; switch (parse_array_index(s, idx)) { + // the branches differ in their messages, not after JSON_THROW's expansion + // NOLINTNEXTLINE(bugprone-branch-clone) case array_index_status::leading_zero: JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); case array_index_status::not_a_number: @@ -685,6 +687,8 @@ class json_pointer typename BasicJsonType::size_type idx{}; switch (parse_array_index(reference_token, idx)) { + // the branches differ in their messages, not after JSON_THROW's expansion + // NOLINTNEXTLINE(bugprone-branch-clone) case array_index_status::leading_zero: JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr)); case array_index_status::not_a_number: diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index e74671db2..a4921f217 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -2260,18 +2260,18 @@ class binary_writer switch (error_handler) { case error_handler_t::keep: - return s; + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call case error_handler_t::strict: check_utf8(s, context); - return s; + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call case error_handler_t::replace: case error_handler_t::ignore: default: if (is_valid_utf8(s)) { - return s; + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call } storage = sanitize_utf8(s, error_handler); return storage; diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index dcaae8055..8306440ef 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -706,6 +706,11 @@ class serializer @a ensure_ascii is a template parameter here so that the branch on it is resolved once, outside the loop; see @ref dump_escaped. */ +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push) + // EnsureAscii is a template parameter; C++11 has no if constexpr +#pragma warning(disable : 4127) // conditional expression is constant +#endif template void dump_escaped_impl(const string_t& s) { @@ -1055,6 +1060,9 @@ class serializer } } } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(pop) +#endif private: /*! diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 421feda50..3f3645524 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -1048,6 +1048,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec never name it to call this constructor itself. */ basic_json(copy_construct_tag /*unused*/, const basic_json& src) + noexcept(std::is_nothrow_copy_constructible::value) : json_base_class_t(src) #if JSON_DIAGNOSTIC_POSITIONS , start_position(src.start_position) @@ -1442,7 +1443,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec be destroyed. */ template - void convert_iteratively(const BasicJsonType& val) + void convert_iteratively(const BasicJsonType& val, std::true_type /*unused*/) { using other_const_iterator = typename BasicJsonType::const_iterator; @@ -1536,21 +1537,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_LIKELY(guard.okay())) { - // every element comes back to the converting constructor - if (val.is_object()) - { - using other_object_t = typename BasicJsonType::object_t; - JSONSerializer::to_json(*this, val.template get_ref()); - } - else - { - using other_array_t = typename BasicJsonType::array_t; - JSONSerializer::to_json(*this, val.template get_ref()); - } + convert_by_serializers(val); return; } - convert_iteratively(val); + // the iterative conversion needs to construct this object type's keys + // from those of @a val; if it cannot, neither can the range constructor, + // and the serializers convert @a val some other way (see #3425) + convert_iteratively(val, std::is_constructible {}); + } + + /// @brief convert the object or array @a val with the serializers; every + /// element comes back to the converting constructor + template + void convert_by_serializers(const BasicJsonType& val) + { + if (val.is_object()) + { + using other_object_t = typename BasicJsonType::object_t; + JSONSerializer::to_json(*this, val.template get_ref()); + } + else + { + using other_array_t = typename BasicJsonType::array_t; + JSONSerializer::to_json(*this, val.template get_ref()); + } + } + + /// @brief convert @a val whose keys cannot be converted, see @ref convert_structured + template + void convert_iteratively(const BasicJsonType& val, std::false_type /*unused*/) + { + convert_by_serializers(val); } @@ -1624,15 +1642,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept { const std::partial_ordering order = lhs <=> rhs; // *NOPAD* - if (order == 0) + if (std::is_eq(order)) { return compare_result::equal; } - if (order < 0) + if (std::is_lt(order)) { return compare_result::less; } - if (order > 0) + if (std::is_gt(order)) { return compare_result::greater; } @@ -2704,6 +2722,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto ret = ValueType(); JSONSerializer::from_json(*this, ret); + // false positive: ret is returned by value, not its address + // @infer-ignore STACK_VARIABLE_ADDRESS_ESCAPE return ret; } @@ -5058,7 +5078,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template requires std::is_scalar_v - friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) <= rhs; } @@ -5067,7 +5087,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template requires std::is_scalar_v - friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) >= rhs; } diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index eb521114e..2e0970726 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -12,7 +12,7 @@ #include // equal_to, less #include // initializer_list #include // input_iterator_tag, iterator_traits -#include // allocator +#include // allocator // IWYU pragma: keep #include // for operator new (placement new) #include // for out_of_range #include // forward_as_tuple @@ -78,7 +78,7 @@ private: /// @brief find the entry for @a key, for either constness of @a self /// @note the single place that performs the linear key search template - static auto find_impl(Self& self, KeyType&& key) -> decltype(self.begin()) + static auto find_impl(Self& self, const KeyType& key) -> decltype(self.begin()) { for (auto it = self.begin(); it != self.end(); ++it) { diff --git a/nlohmann_json.natvis b/nlohmann_json.natvis index ed443145e..17029f1dd 100644 --- a/nlohmann_json.natvis +++ b/nlohmann_json.natvis @@ -455,6 +455,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -755,6 +815,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -995,6 +1115,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -1175,6 +1355,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -1295,6 +1535,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -1355,6 +1655,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -1595,6 +2015,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -1775,6 +2255,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -1895,6 +2435,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -1955,6 +2555,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2135,6 +2855,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2255,6 +3035,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2315,6 +3155,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2435,6 +3395,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2495,6 +3515,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2555,6 +3695,186 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2735,6 +4055,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2855,6 +4235,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -2915,6 +4355,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3035,6 +4595,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3095,6 +4715,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3155,6 +4895,186 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3275,6 +5195,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3335,6 +5315,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3395,6 +5495,186 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3455,6 +5735,246 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3575,6 +6095,66 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3635,6 +6215,126 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3695,6 +6395,186 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3755,6 +6635,246 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3815,6 +6935,306 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + null @@ -3875,4 +7295,424 @@ + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + + + + null + {*(m_data.m_value.object)} + {*(m_data.m_value.array)} + {*(m_data.m_value.string)} + {m_data.m_value.boolean} + {m_data.m_value.number_integer} + {m_data.m_value.number_unsigned} + {m_data.m_value.number_float} + discarded + + + *(m_data.m_value.object),view(simple) + + + *(m_data.m_value.array),view(simple) + + + + + + + {second} + + second + + + diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 511401699..75684866e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -13696,15 +13696,15 @@ class binary_reader @param[in] adapter input adapter to read from @param[in] format the binary format to parse - @param[in] error_handler how to treat text strings and object keys that + @param[in] error_handler_ how to treat text strings and object keys that are not well-formed UTF-8; none of the supported formats requires a decoder to reject those, so the default is to @ref error_handler_t::keep them unchanged, as every binary reader did before this parameter existed */ explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json, - const error_handler_t error_handler = error_handler_t::keep) noexcept - : ia(std::move(adapter)), input_format(format), error_handler(error_handler) + const error_handler_t error_handler_ = error_handler_t::keep) noexcept + : ia(std::move(adapter)), input_format(format), error_handler(error_handler_) { (void)detail::is_sax_static_asserts {}; } @@ -19998,6 +19998,8 @@ class json_pointer typename BasicJsonType::size_type idx{}; switch (parse_array_index(s, idx)) { + // the branches differ in their messages, not after JSON_THROW's expansion + // NOLINTNEXTLINE(bugprone-branch-clone) case array_index_status::leading_zero: JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); case array_index_status::not_a_number: @@ -20361,6 +20363,8 @@ class json_pointer typename BasicJsonType::size_type idx{}; switch (parse_array_index(reference_token, idx)) { + // the branches differ in their messages, not after JSON_THROW's expansion + // NOLINTNEXTLINE(bugprone-branch-clone) case array_index_status::leading_zero: JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr)); case array_index_status::not_a_number: @@ -23485,18 +23489,18 @@ class binary_writer switch (error_handler) { case error_handler_t::keep: - return s; + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call case error_handler_t::strict: check_utf8(s, context); - return s; + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call case error_handler_t::replace: case error_handler_t::ignore: default: if (is_valid_utf8(s)) { - return s; + return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call } storage = sanitize_utf8(s, error_handler); return storage; @@ -25681,6 +25685,11 @@ class serializer @a ensure_ascii is a template parameter here so that the branch on it is resolved once, outside the loop; see @ref dump_escaped. */ +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push) + // EnsureAscii is a template parameter; C++11 has no if constexpr +#pragma warning(disable : 4127) // conditional expression is constant +#endif template void dump_escaped_impl(const string_t& s) { @@ -26030,6 +26039,9 @@ class serializer } } } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(pop) +#endif private: /*! @@ -26612,7 +26624,7 @@ NLOHMANN_JSON_NAMESPACE_END #include // equal_to, less #include // initializer_list #include // input_iterator_tag, iterator_traits -#include // allocator +#include // allocator // IWYU pragma: keep #include // for operator new (placement new) #include // for out_of_range #include // forward_as_tuple @@ -26681,7 +26693,7 @@ private: /// @brief find the entry for @a key, for either constness of @a self /// @note the single place that performs the linear key search template - static auto find_impl(Self& self, KeyType&& key) -> decltype(self.begin()) + static auto find_impl(Self& self, const KeyType& key) -> decltype(self.begin()) { for (auto it = self.begin(); it != self.end(); ++it) { @@ -27985,6 +27997,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec never name it to call this constructor itself. */ basic_json(copy_construct_tag /*unused*/, const basic_json& src) + noexcept(std::is_nothrow_copy_constructible::value) : json_base_class_t(src) #if JSON_DIAGNOSTIC_POSITIONS , start_position(src.start_position) @@ -28379,7 +28392,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec be destroyed. */ template - void convert_iteratively(const BasicJsonType& val) + void convert_iteratively(const BasicJsonType& val, std::true_type /*unused*/) { using other_const_iterator = typename BasicJsonType::const_iterator; @@ -28473,21 +28486,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec if (JSON_HEDLEY_LIKELY(guard.okay())) { - // every element comes back to the converting constructor - if (val.is_object()) - { - using other_object_t = typename BasicJsonType::object_t; - JSONSerializer::to_json(*this, val.template get_ref()); - } - else - { - using other_array_t = typename BasicJsonType::array_t; - JSONSerializer::to_json(*this, val.template get_ref()); - } + convert_by_serializers(val); return; } - convert_iteratively(val); + // the iterative conversion needs to construct this object type's keys + // from those of @a val; if it cannot, neither can the range constructor, + // and the serializers convert @a val some other way (see #3425) + convert_iteratively(val, std::is_constructible {}); + } + + /// @brief convert the object or array @a val with the serializers; every + /// element comes back to the converting constructor + template + void convert_by_serializers(const BasicJsonType& val) + { + if (val.is_object()) + { + using other_object_t = typename BasicJsonType::object_t; + JSONSerializer::to_json(*this, val.template get_ref()); + } + else + { + using other_array_t = typename BasicJsonType::array_t; + JSONSerializer::to_json(*this, val.template get_ref()); + } + } + + /// @brief convert @a val whose keys cannot be converted, see @ref convert_structured + template + void convert_iteratively(const BasicJsonType& val, std::false_type /*unused*/) + { + convert_by_serializers(val); } @@ -28561,15 +28591,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept { const std::partial_ordering order = lhs <=> rhs; // *NOPAD* - if (order == 0) + if (std::is_eq(order)) { return compare_result::equal; } - if (order < 0) + if (std::is_lt(order)) { return compare_result::less; } - if (order > 0) + if (std::is_gt(order)) { return compare_result::greater; } @@ -29641,6 +29671,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec { auto ret = ValueType(); JSONSerializer::from_json(*this, ret); + // false positive: ret is returned by value, not its address + // @infer-ignore STACK_VARIABLE_ADDRESS_ESCAPE return ret; } @@ -31995,7 +32027,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ template requires std::is_scalar_v - friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) <= rhs; } @@ -32004,7 +32036,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ template requires std::is_scalar_v - friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible::value) { return basic_json(lhs) >= rhs; } diff --git a/tests/src/test_utils.hpp b/tests/src/test_utils.hpp index b2a381fb0..8f7bd06a0 100644 --- a/tests/src/test_utils.hpp +++ b/tests/src/test_utils.hpp @@ -10,7 +10,8 @@ #include // uint8_t #include // size_t -#include // ifstream, istreambuf_iterator, ios +#include // ifstream, ios +#include // istream_iterator #include // vector namespace utils diff --git a/tests/src/unit-allocator.cpp b/tests/src/unit-allocator.cpp index fbdfaa351..bc283ab28 100644 --- a/tests/src/unit-allocator.cpp +++ b/tests/src/unit-allocator.cpp @@ -567,7 +567,7 @@ struct allocator_no_forward : std::allocator { allocator_no_forward() = default; template - allocator_no_forward(allocator_no_forward /*unused*/) {} + allocator_no_forward(const allocator_no_forward& /*unused*/) {} template struct rebind diff --git a/tests/src/unit-binary_utf8_error_handler.cpp b/tests/src/unit-binary_utf8_error_handler.cpp index 85505bffb..c3f2435fd 100644 --- a/tests/src/unit-binary_utf8_error_handler.cpp +++ b/tests/src/unit-binary_utf8_error_handler.cpp @@ -7,6 +7,7 @@ // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" +#include "test_utils.hpp" #include using nlohmann::json; @@ -25,18 +26,27 @@ struct ill_formed_case // RFC 3629 ill-formed sequences used throughout this file, plus one // well-formed sequence for contrast -const std::vector ill_formed_cases = +std::vector ill_formed_cases() { - {"overlong", "\xC0\xAE"}, - {"lone_0xFF", "\xFF"}, - {"truncated", "\xE2\x82"}, - {"surrogate", "\xED\xA0\x80"}, -}; + return + { + {"overlong", "\xC0\xAE"}, + {"lone_0xFF", "\xFF"}, + {"truncated", "\xE2\x82"}, + {"surrogate", "\xED\xA0\x80"}, + }; +} -const std::string valid_sequence = "\xC3\xA9"; // U+00E9, "é" +std::string valid_sequence() +{ + return "\xC3\xA9"; // U+00E9, "é" +} using eh = json::error_handler_t; -const std::vector all_handlers = {eh::strict, eh::replace, eh::ignore, eh::keep}; +std::vector all_handlers() +{ + return {eh::strict, eh::replace, eh::ignore, eh::keep}; +} // what dump()+parse() produces for a sanitizing error_handler; this is the // ground truth every binary writer/reader is checked against @@ -51,9 +61,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") { SECTION("writers: string value") { - for (const auto& c : ill_formed_cases) + for (const auto& c : ill_formed_cases()) { - CAPTURE(c.name); + CAPTURE(c.name) const json jval = c.bytes; CHECK_THROWS_AS(json::to_cbor(jval, eh::strict), json::type_error&); @@ -71,7 +81,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") eh::replace, eh::ignore }) { - CAPTURE(static_cast(h)); + CAPTURE(static_cast(h)) const std::string expected = dump_and_parse(c.bytes, h); CHECK(json::from_cbor(json::to_cbor(jval, h)).get() == expected); @@ -103,9 +113,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") SECTION("writers: object key") { - for (const auto& c : ill_formed_cases) + for (const auto& c : ill_formed_cases()) { - CAPTURE(c.name); + CAPTURE(c.name) json jobj; jobj[c.bytes] = 1; @@ -120,7 +130,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") eh::replace, eh::ignore }) { - CAPTURE(static_cast(h)); + CAPTURE(static_cast(h)) const std::string expected = dump_and_parse(c.bytes, h); CHECK(json::from_cbor(json::to_cbor(jobj, h)).begin().key() == expected); @@ -141,9 +151,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") SECTION("readers: string value") { - for (const auto& c : ill_formed_cases) + for (const auto& c : ill_formed_cases()) { - CAPTURE(c.name); + CAPTURE(c.name) // bytes produced the lenient (keep) way, as any binary reader // accepted them before this parameter existed @@ -166,15 +176,15 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") CHECK(json::from_bson(bson_bytes)["k"].get() == c.bytes); // strict: parse_error.113, discarded (not thrown) when allow_exceptions is false - CHECK_THROWS_AS(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict)), json::parse_error&); CHECK(json::from_cbor(cbor_bytes, true, false, json::cbor_tag_handler_t::error, eh::strict).is_discarded()); - CHECK_THROWS_AS(json::from_msgpack(msgpack_bytes, true, true, eh::strict), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_msgpack(msgpack_bytes, true, true, eh::strict)), json::parse_error&); CHECK(json::from_msgpack(msgpack_bytes, true, false, eh::strict).is_discarded()); - CHECK_THROWS_AS(json::from_ubjson(ubjson_bytes, true, true, eh::strict), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_ubjson(ubjson_bytes, true, true, eh::strict)), json::parse_error&); CHECK(json::from_ubjson(ubjson_bytes, true, false, eh::strict).is_discarded()); - CHECK_THROWS_AS(json::from_bjdata(bjdata_bytes, true, true, eh::strict), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_bjdata(bjdata_bytes, true, true, eh::strict)), json::parse_error&); CHECK(json::from_bjdata(bjdata_bytes, true, false, eh::strict).is_discarded()); - CHECK_THROWS_AS(json::from_bson(bson_bytes, true, true, eh::strict), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_bson(bson_bytes, true, true, eh::strict)), json::parse_error&); CHECK(json::from_bson(bson_bytes, true, false, eh::strict).is_discarded()); // replace / ignore: match what dump() would have sanitized the same bytes to @@ -183,7 +193,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") eh::replace, eh::ignore }) { - CAPTURE(static_cast(h)); + CAPTURE(static_cast(h)) const std::string expected = dump_and_parse(c.bytes, h); CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).get() == expected); @@ -197,9 +207,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") SECTION("readers: object key") { - for (const auto& c : ill_formed_cases) + for (const auto& c : ill_formed_cases()) { - CAPTURE(c.name); + CAPTURE(c.name) json jobj; jobj[c.bytes] = 1; @@ -215,18 +225,18 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") CHECK(json::from_bjdata(bjdata_bytes).begin().key() == c.bytes); CHECK(json::from_bson(bson_bytes).begin().key() == c.bytes); - CHECK_THROWS_AS(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict), json::parse_error&); - CHECK_THROWS_AS(json::from_msgpack(msgpack_bytes, true, true, eh::strict), json::parse_error&); - CHECK_THROWS_AS(json::from_ubjson(ubjson_bytes, true, true, eh::strict), json::parse_error&); - CHECK_THROWS_AS(json::from_bjdata(bjdata_bytes, true, true, eh::strict), json::parse_error&); - CHECK_THROWS_AS(json::from_bson(bson_bytes, true, true, eh::strict), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict)), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_msgpack(msgpack_bytes, true, true, eh::strict)), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_ubjson(ubjson_bytes, true, true, eh::strict)), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_bjdata(bjdata_bytes, true, true, eh::strict)), json::parse_error&); + CHECK_THROWS_AS(utils::ignore_return_value(json::from_bson(bson_bytes, true, true, eh::strict)), json::parse_error&); for (const auto h : { eh::replace, eh::ignore }) { - CAPTURE(static_cast(h)); + CAPTURE(static_cast(h)) const std::string expected = dump_and_parse(c.bytes, h); CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).begin().key() == expected); @@ -240,30 +250,30 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") SECTION("well-formed UTF-8 is unaffected by error_handler") { - const json jval = valid_sequence; + const json jval = valid_sequence(); json jobj; - jobj[valid_sequence] = valid_sequence; + jobj[valid_sequence()] = valid_sequence(); - for (const auto h : all_handlers) + for (const auto h : all_handlers()) { - CAPTURE(static_cast(h)); + CAPTURE(static_cast(h)) - CHECK(json::from_cbor(json::to_cbor(jval, h)).get() == valid_sequence); - CHECK(json::from_msgpack(json::to_msgpack(jval, h)).get() == valid_sequence); - CHECK(json::from_ubjson(json::to_ubjson(jval, false, false, h)).get() == valid_sequence); - CHECK(json::from_bjdata(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, h)).get() == valid_sequence); - CHECK(json::from_bson(json::to_bson(jobj, h)).begin().key() == valid_sequence); + CHECK(json::from_cbor(json::to_cbor(jval, h)).get() == valid_sequence()); + CHECK(json::from_msgpack(json::to_msgpack(jval, h)).get() == valid_sequence()); + CHECK(json::from_ubjson(json::to_ubjson(jval, false, false, h)).get() == valid_sequence()); + CHECK(json::from_bjdata(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, h)).get() == valid_sequence()); + CHECK(json::from_bson(json::to_bson(jobj, h)).begin().key() == valid_sequence()); - CHECK(json::from_cbor(json::to_cbor(jval, eh::keep), true, true, json::cbor_tag_handler_t::error, h).get() == valid_sequence); - CHECK(json::from_msgpack(json::to_msgpack(jval), true, true, h).get() == valid_sequence); + CHECK(json::from_cbor(json::to_cbor(jval, eh::keep), true, true, json::cbor_tag_handler_t::error, h).get() == valid_sequence()); + CHECK(json::from_msgpack(json::to_msgpack(jval), true, true, h).get() == valid_sequence()); } } SECTION("dump() with error_handler_t::keep writes raw bytes as is") { - for (const auto& c : ill_formed_cases) + for (const auto& c : ill_formed_cases()) { - CAPTURE(c.name); + CAPTURE(c.name) const json jval = c.bytes; const std::string dumped = jval.dump(-1, ' ', false, eh::keep); @@ -276,10 +286,10 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") // well-formed characters around an ill-formed sequence are still // escaped as usual under ensure_ascii - const json mixed = valid_sequence + ill_formed_cases[1].bytes; // "é" + lone 0xFF + const json mixed = valid_sequence() + ill_formed_cases()[1].bytes; // "é" + lone 0xFF const std::string dumped_mixed = mixed.dump(-1, ' ', true, eh::keep); CHECK(dumped_mixed.find("\\u00e9") != std::string::npos); - CHECK(dumped_mixed.find(ill_formed_cases[1].bytes) != std::string::npos); + CHECK(dumped_mixed.find(ill_formed_cases()[1].bytes) != std::string::npos); // the byte that ends an ill-formed sequence is read again, so a quote, // a backslash, or a control character after it is still escaped, and @@ -289,7 +299,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") false, true }) { - CAPTURE(ensure_ascii); + CAPTURE(ensure_ascii) CHECK(json("\xC3\"").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\"\""); CHECK(json("\xC3\\").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\\\""); CHECK(json("\xC3\n").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\n\""); @@ -303,12 +313,12 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") SECTION("to_msgpack defaults to keep; to_bon8 is not affected by error_handler") { - const json jval = ill_formed_cases[1].bytes; // lone 0xFF + const json jval = ill_formed_cases()[1].bytes; // lone 0xFF // to_msgpack's error_handler defaults to keep, as MessagePack's spec // allows any bytes in a str, so the bytes are passed through CHECK(json::to_msgpack(jval) == json::to_msgpack(jval, eh::keep)); - CHECK(json::from_msgpack(json::to_msgpack(jval)).get() == ill_formed_cases[1].bytes); + CHECK(json::from_msgpack(json::to_msgpack(jval)).get() == ill_formed_cases()[1].bytes); // the diagnostics context of an ill-formed key is the object json jobj; @@ -322,14 +332,14 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") SECTION("allow_exceptions=false with error_handler_t::strict discards the value") { - const auto bytes = json::to_cbor(json(ill_formed_cases[0].bytes), eh::keep); + const auto bytes = json::to_cbor(json(ill_formed_cases()[0].bytes), eh::keep); const json result = json::from_cbor(bytes, true, false, json::cbor_tag_handler_t::error, eh::strict); CHECK(result.is_discarded()); } SECTION("default parameters are unchanged") { - const json jval = ill_formed_cases[0].bytes; + const json jval = ill_formed_cases()[0].bytes; // to_*: the default error_handler is keep, so ill-formed bytes are // written unchanged, exactly as in release 3.12.0 (it is strict only @@ -347,16 +357,16 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers") // from_*: the default error_handler is keep, so ill-formed bytes are // still accepted unchanged, exactly as in release 3.12.0 const auto cbor_bytes = json::to_cbor(jval, eh::keep); - CHECK(json::from_cbor(cbor_bytes).get() == ill_formed_cases[0].bytes); + CHECK(json::from_cbor(cbor_bytes).get() == ill_formed_cases()[0].bytes); const auto ubjson_bytes = json::to_ubjson(jval, false, false, eh::keep); - CHECK(json::from_ubjson(ubjson_bytes).get() == ill_formed_cases[0].bytes); + CHECK(json::from_ubjson(ubjson_bytes).get() == ill_formed_cases()[0].bytes); const auto bjdata_bytes = json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, eh::keep); - CHECK(json::from_bjdata(bjdata_bytes).get() == ill_formed_cases[0].bytes); + CHECK(json::from_bjdata(bjdata_bytes).get() == ill_formed_cases()[0].bytes); const auto msgpack_bytes = json::to_msgpack(jval); - CHECK(json::from_msgpack(msgpack_bytes).get() == ill_formed_cases[0].bytes); + CHECK(json::from_msgpack(msgpack_bytes).get() == ill_formed_cases()[0].bytes); json bson_obj; bson_obj["k"] = jval; const auto bson_bytes = json::to_bson(bson_obj, eh::keep); - CHECK(json::from_bson(bson_bytes)["k"].get() == ill_formed_cases[0].bytes); + CHECK(json::from_bson(bson_bytes)["k"].get() == ill_formed_cases()[0].bytes); } } diff --git a/tests/src/unit-binary_utf8_strict.cpp b/tests/src/unit-binary_utf8_strict.cpp index 2ac8aabf4..96d96e61c 100644 --- a/tests/src/unit-binary_utf8_strict.cpp +++ b/tests/src/unit-binary_utf8_strict.cpp @@ -83,21 +83,45 @@ TEST_CASE("JSON_STRICT_BINARY_UTF8 (see #5529, #5651)") // any bytes reach the output adapter (the BSON document length // prefix must be known up front, so nothing is written incrementally) std::vector out{0x42}; // a sentinel byte the writer must not touch - CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}, nlohmann::detail::output_adapter(out)), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}, nlohmann::detail::output_adapter(out)), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#else + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}, nlohmann::detail::output_adapter(out)), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#endif CHECK(out == std::vector {0x42}); - CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#else + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#endif // a truncated multi-byte sequence - CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC3"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC3"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xC3", json::type_error&); +#else + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC3"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&); +#endif // an encoded surrogate half (U+D800) - CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xED", json::type_error&); +#else + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&); +#endif // an overlong encoding of '.' - CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xC0", json::type_error&); +#else + CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&); +#endif // an object key with ill-formed UTF-8 is rejected as well; unlike // the reader (which never validates element names), the writer // checks both string values and object keys - CHECK_THROWS_WITH_AS(json::to_bson(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS(json::to_bson(json {{"\xFF", 1}}), "[json.exception.type_error.316] (/\xFF) invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#else + CHECK_THROWS_WITH_AS(json::to_bson(json {{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&); +#endif } SECTION("an explicit error_handler overrides the default") diff --git a/tests/src/unit-bjdata.cpp b/tests/src/unit-bjdata.cpp index f34df1717..0098541cf 100644 --- a/tests/src/unit-bjdata.cpp +++ b/tests/src/unit-bjdata.cpp @@ -3921,7 +3921,7 @@ TEST_CASE("Universal Binary JSON Specification Examples 1") CHECK_NOTHROW(j = json::from_bjdata(v)); REQUIRE(j.is_string()); CHECK(j.get_ref() == std::string("\xc0\xae")); - CHECK_THROWS_AS(j.dump(), json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); CHECK(json::from_bjdata(json::to_bjdata(j)) == j); // the same bytes as an object key round-trip as well diff --git a/tests/src/unit-bon8.cpp b/tests/src/unit-bon8.cpp index a751d7dbe..491189867 100644 --- a/tests/src/unit-bon8.cpp +++ b/tests/src/unit-bon8.cpp @@ -786,6 +786,7 @@ TEST_CASE("Parse BON8 directly from a file using iterator and sentinel") CHECK((parsed.is_object() || parsed.is_array())); } +#if !defined(JSON_NOEXCEPTION) // corpus values that do not survive the round trip are skipped by catching the exception TEST_CASE("BON8 round-trip invariants") { // This checks what the parse_bon8_fuzzer driver checks (see @@ -818,6 +819,7 @@ TEST_CASE("BON8 round-trip invariants") CHECK(json::to_bon8(j2) == vec); } } +#endif TEST_CASE("BON8 roundtrips" * doctest::skip()) { diff --git a/tests/src/unit-bson.cpp b/tests/src/unit-bson.cpp index 92a14e6fe..17819dc65 100644 --- a/tests/src/unit-bson.cpp +++ b/tests/src/unit-bson.cpp @@ -62,6 +62,8 @@ class huge_string_t : public std::string { public: using std::string::string; + // inheriting std::string's constructors does not inherit its default constructor + huge_string_t() = default; huge_string_t(const std::string& s) : std::string(s) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) // returns a copy of @a s whose size() pretends to be huge @@ -174,7 +176,7 @@ TEST_CASE("BSON") REQUIRE(j.contains("s")); CHECK(j["s"].get_ref() == std::string("\xc0\xae")); // dump() still requires valid UTF-8 and throws for such a value - CHECK_THROWS_AS(j.dump(), json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); // to_bson() writes the bytes back unchanged, as before 3.13.0, // unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp) CHECK(json::from_bson(json::to_bson(j)) == j); diff --git a/tests/src/unit-cbor.cpp b/tests/src/unit-cbor.cpp index 633f50fea..f3f4301e5 100644 --- a/tests/src/unit-cbor.cpp +++ b/tests/src/unit-cbor.cpp @@ -1820,7 +1820,7 @@ TEST_CASE("CBOR") // dump() still requires valid UTF-8 and throws for such a value, // unless an error handler that replaces or ignores the bytes is // passed - CHECK_THROWS_AS(j_value.dump(), json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j_value.dump()), json::type_error&); // to_cbor() writes the bytes back unchanged, as before 3.13.0, // unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp) CHECK(json::from_cbor(json::to_cbor(j_value)) == j_value); @@ -1878,13 +1878,13 @@ TEST_CASE("CBOR") // a truncated code point is kept as is CHECK_NOTHROW(_ = json::from_cbor(std::vector({0x7f, 0x61, 0xc3, 0xff}))); CHECK(_ == "\xc3"); - CHECK_THROWS_AS(_.dump(), json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(_.dump()), json::type_error&); CHECK(json::from_cbor(json::to_cbor(_)) == _); // an ill-formed later chunk is kept after valid ones CHECK_NOTHROW(_ = json::from_cbor(std::vector({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc0, 0xae, 0xff}))); CHECK(_ == "\xc3\xa9\xc0\xae"); - CHECK_THROWS_AS(_.dump(), json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(_.dump()), json::type_error&); // valid multi-byte chunks are accepted CHECK(json::from_cbor(std::vector({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6"); @@ -2390,6 +2390,7 @@ TEST_CASE("issue #5405 - array reserve for definite-length CBOR arrays") } } +#if !defined(JSON_NOEXCEPTION) // corpus values that do not survive the round trip are skipped by catching the exception TEST_CASE("CBOR round-trip invariants") { // This checks what the parse_cbor_fuzzer driver checks (see @@ -2422,6 +2423,7 @@ TEST_CASE("CBOR round-trip invariants") CHECK(json::to_cbor(j2) == vec); } } +#endif TEST_CASE("CBOR roundtrips" * doctest::skip()) { diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp index 0ae5e382b..f2de97ea3 100644 --- a/tests/src/unit-class_parser.cpp +++ b/tests/src/unit-class_parser.cpp @@ -2704,9 +2704,9 @@ TEST_CASE("diagnostic positions: value lifetime, input adapters, and SAX") CHECK(b["b"].end_pos() == nested_end); // the moved-from value is reset to a null and reports npos - CHECK(a.is_null()); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move) - CHECK(a.start_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move) - CHECK(a.end_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move) + CHECK(a.is_null()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move) + CHECK(a.start_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move) + CHECK(a.end_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move) } SECTION("swap() exchanges positions along with values") diff --git a/tests/src/unit-comparison.cpp b/tests/src/unit-comparison.cpp index 15715fec2..b9257cb17 100644 --- a/tests/src/unit-comparison.cpp +++ b/tests/src/unit-comparison.cpp @@ -969,7 +969,7 @@ TEST_CASE("copying an object preserves its comparator's state") for (const std::size_t depth : std::vector {0, 127, 128, 200}) { - CAPTURE(depth); + CAPTURE(depth) key_case_json original = object; for (std::size_t i = 0; i < depth; ++i) @@ -1138,7 +1138,7 @@ TEST_CASE("operator<=> of binary values with a different subtype does not depend // and must still agree with the levels that do for (const std::size_t depth : std::vector {0, 127, 128, 200}) { - CAPTURE(depth); + CAPTURE(depth) const json x = deep(a, depth); const json y = deep(b, depth); CHECK((x <=> y) == std::partial_ordering::less); // *NOPAD* diff --git a/tests/src/unit-conversions.cpp b/tests/src/unit-conversions1.cpp similarity index 52% rename from tests/src/unit-conversions.cpp rename to tests/src/unit-conversions1.cpp index 5f83a9253..3d39e10c9 100644 --- a/tests/src/unit-conversions.cpp +++ b/tests/src/unit-conversions1.cpp @@ -15,11 +15,6 @@ #include "doctest_compatibility.h" -// skip tests if JSON_DisableEnumSerialization=ON (#4384) -#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) - #define SKIP_TESTS_FOR_ENUM_SERIALIZATION -#endif - #define JSON_TESTS_PRIVATE #include using nlohmann::json; @@ -1259,808 +1254,8 @@ TEST_CASE("value conversion") } } #endif - - SECTION("get a binary value (explicit)") - { - json::binary_t const n_reference{{1, 2, 3}}; - json j(n_reference); - - SECTION("binary_t") - { - json::binary_t const b = j.get(); - CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); - } - - SECTION("get_binary()") - { - SECTION("non-const") - { - auto& b = j.get_binary(); - CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); - } - - SECTION("non-const") - { - const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization) - const auto& b = j_const.get_binary(); - CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); - } - } - - SECTION("exception in case of a non-string type") - { - json j_null(json::value_t::null); - json j_object(json::value_t::object); - json j_array(json::value_t::array); - json j_string(json::value_t::string); - json j_boolean(json::value_t::boolean); - const json j_null_const(json::value_t::null); - const json j_object_const(json::value_t::object); - const json j_array_const(json::value_t::array); - const json j_string_const(json::value_t::string); - const json j_boolean_const(json::value_t::boolean); - - CHECK_THROWS_WITH_AS(j_null.get(), - "[json.exception.type_error.302] type must be binary, but is null", - json::type_error&); - CHECK_THROWS_WITH_AS(j_object.get(), - "[json.exception.type_error.302] type must be binary, but is object", - json::type_error&); - CHECK_THROWS_WITH_AS(j_array.get(), - "[json.exception.type_error.302] type must be binary, but is array", - json::type_error&); - CHECK_THROWS_WITH_AS(j_string.get(), - "[json.exception.type_error.302] type must be binary, but is string", - json::type_error&); - CHECK_THROWS_WITH_AS(j_boolean.get(), - "[json.exception.type_error.302] type must be binary, but is boolean", - json::type_error&); - - CHECK_THROWS_WITH_AS(j_null_const.get(), - "[json.exception.type_error.302] type must be binary, but is null", - json::type_error&); - CHECK_THROWS_WITH_AS(j_object_const.get(), - "[json.exception.type_error.302] type must be binary, but is object", - json::type_error&); - CHECK_THROWS_WITH_AS(j_array_const.get(), - "[json.exception.type_error.302] type must be binary, but is array", - json::type_error&); - CHECK_THROWS_WITH_AS(j_string_const.get(), - "[json.exception.type_error.302] type must be binary, but is string", - json::type_error&); - CHECK_THROWS_WITH_AS(j_boolean_const.get(), - "[json.exception.type_error.302] type must be binary, but is boolean", - json::type_error&); - - CHECK_THROWS_WITH_AS(j_null.get_binary(), - "[json.exception.type_error.302] type must be binary, but is null", - json::type_error&); - CHECK_THROWS_WITH_AS(j_object.get_binary(), - "[json.exception.type_error.302] type must be binary, but is object", - json::type_error&); - CHECK_THROWS_WITH_AS(j_array.get_binary(), - "[json.exception.type_error.302] type must be binary, but is array", - json::type_error&); - CHECK_THROWS_WITH_AS(j_string.get_binary(), - "[json.exception.type_error.302] type must be binary, but is string", - json::type_error&); - CHECK_THROWS_WITH_AS(j_boolean.get_binary(), - "[json.exception.type_error.302] type must be binary, but is boolean", - json::type_error&); - - CHECK_THROWS_WITH_AS(j_null_const.get_binary(), - "[json.exception.type_error.302] type must be binary, but is null", - json::type_error&); - CHECK_THROWS_WITH_AS(j_object_const.get_binary(), - "[json.exception.type_error.302] type must be binary, but is object", - json::type_error&); - CHECK_THROWS_WITH_AS(j_array_const.get_binary(), - "[json.exception.type_error.302] type must be binary, but is array", - json::type_error&); - CHECK_THROWS_WITH_AS(j_string_const.get_binary(), - "[json.exception.type_error.302] type must be binary, but is string", - json::type_error&); - CHECK_THROWS_WITH_AS(j_boolean_const.get_binary(), - "[json.exception.type_error.302] type must be binary, but is boolean", - json::type_error&); - } - } - -#if JSON_USE_IMPLICIT_CONVERSIONS - SECTION("get a binary value (implicit)") - { - json::binary_t const n_reference{{1, 2, 3}}; - json const j(n_reference); - - SECTION("binary_t") - { - json::binary_t const b = j; - CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); - } - } -#endif - -#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION - SECTION("get an enum") - { - enum c_enum { value_1, value_2 }; // NOLINT(cppcoreguidelines-use-enum-class) - enum class cpp_enum { value_1, value_2 }; - - CHECK(json(value_1).get() == value_1); - CHECK(json(cpp_enum::value_1).get() == cpp_enum::value_1); - } - - SECTION("get an enum with underlying type bool (#5671)") - { - enum class bool_enum : bool { off, on }; - - CHECK(json(bool_enum::off).get() == bool_enum::off); - CHECK(json(bool_enum::on).get() == bool_enum::on); - } -#endif - - SECTION("more involved conversions") - { - SECTION("object-like STL containers") - { - json const j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; - json const j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; - json const j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; - json const j4 = {{"one", true}, {"two", false}, {"three", true}}; - json const j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; - - SECTION("std::map") - { - CHECK(j1.get>() == (std::map {{"one", 1}, {"two", 2}, {"three", 3}})); - CHECK(j2.get>() == (std::map {{"one", 1u}, {"two", 2u}, {"three", 3u}})); - CHECK(j3.get>() == (std::map {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); - CHECK(j4.get>() == (std::map {{"one", true}, {"two", false}, {"three", true}})); - CHECK(j5.get>() == (std::map {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); - } - - SECTION("std::unordered_map") - { - CHECK(j1.get>() == (std::unordered_map {{"one", 1}, {"two", 2}, {"three", 3}})); - CHECK(j2.get>() == (std::unordered_map {{"one", 1u}, {"two", 2u}, {"three", 3u}})); - CHECK(j3.get>() == (std::unordered_map {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); - CHECK(j4.get>() == (std::unordered_map {{"one", true}, {"two", false}, {"three", true}})); - const auto m5 = j5.get>(); - CHECK(m5 == (std::unordered_map {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); - CHECK(m5.at("one") == "eins"); - } - - SECTION("reserve is called on containers that support it (#5406)") - { - // build a larger object so that a missing/incorrect reserve() - // call would be more likely to corrupt or drop elements - json j_large; - for (int i = 0; i < 100; ++i) - { - j_large[std::to_string(i)] = i; - } - - SECTION("std::unordered_map (supports reserve)") - { - const auto m = j_large.get>(); - CHECK(m.size() == 100); - for (int i = 0; i < 100; ++i) - { - CHECK(m.at(std::to_string(i)) == i); - } - } - - SECTION("std::map (no reserve, fallback path)") - { - const auto m = j_large.get>(); - CHECK(m.size() == 100); - for (int i = 0; i < 100; ++i) - { - CHECK(m.at(std::to_string(i)) == i); - } - } - } - - SECTION("std::multimap") - { - CHECK(j1.get>() == (std::multimap {{"one", 1}, {"two", 2}, {"three", 3}})); - CHECK(j2.get>() == (std::multimap {{"one", 1u}, {"two", 2u}, {"three", 3u}})); - CHECK(j3.get>() == (std::multimap {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); - CHECK(j4.get>() == (std::multimap {{"one", true}, {"two", false}, {"three", true}})); - const auto m5 = j5.get>(); - CHECK(m5 == (std::multimap {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); - CHECK(m5.find("one")->second == "eins"); - } - - SECTION("std::unordered_multimap") - { - CHECK(j1.get>() == (std::unordered_multimap {{"one", 1}, {"two", 2}, {"three", 3}})); - CHECK(j2.get>() == (std::unordered_multimap {{"one", 1u}, {"two", 2u}, {"three", 3u}})); - CHECK(j3.get>() == (std::unordered_multimap {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); - CHECK(j4.get>() == (std::unordered_multimap {{"one", true}, {"two", false}, {"three", true}})); - const auto m5 = j5.get>(); - CHECK(m5 == (std::unordered_multimap {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); - CHECK(m5.find("one")->second == "eins"); - } - - SECTION("exception in case of a non-object type") - { - CHECK_THROWS_WITH_AS( - (json().get>()), - "[json.exception.type_error.302] type must be object, but is null", json::type_error&); - } - } - - SECTION("array-like STL containers") - { - json const j1 = {1, 2, 3, 4}; - json const j2 = {1u, 2u, 3u, 4u}; - json const j3 = {1.2, 2.3, 3.4, 4.5}; - json const j4 = {true, false, true}; - json const j5 = {"one", "two", "three"}; - - SECTION("std::list") - { - CHECK(j1.get>() == (std::list {1, 2, 3, 4})); - CHECK(j2.get>() == (std::list {1u, 2u, 3u, 4u})); - CHECK(j3.get>() == (std::list {1.2, 2.3, 3.4, 4.5})); - CHECK(j4.get>() == (std::list {true, false, true})); - CHECK(j5.get>() == (std::list {"one", "two", "three"})); - } - - SECTION("std::forward_list") - { - CHECK(j1.get>() == (std::forward_list {1, 2, 3, 4})); - CHECK(j2.get>() == (std::forward_list {1u, 2u, 3u, 4u})); - CHECK(j3.get>() == (std::forward_list {1.2, 2.3, 3.4, 4.5})); - CHECK(j4.get>() == (std::forward_list {true, false, true})); - CHECK(j5.get>() == (std::forward_list {"one", "two", "three"})); - } - - SECTION("std::array") - { - CHECK(j1.get>() == (std::array {{1, 2, 3, 4}})); - // only the first 3 elements of j2 are converted, since the target array is smaller - CHECK(j2.get>() == (std::array {{1u, 2u, 3u}})); - CHECK(j3.get>() == (std::array {{1.2, 2.3, 3.4, 4.5}})); - CHECK(j4.get>() == (std::array {{true, false, true}})); - CHECK(j5.get>() == (std::array {{"one", "two", "three"}})); - - SECTION("std::array is larger than JSON") - { - std::array arr6 = {{1, 2, 3, 4, 5, 6}}; - CHECK_THROWS_WITH_AS(j1.get_to(arr6), "[json.exception.out_of_range.401] " - "array index 4 is out of range", json::out_of_range&); - } - - SECTION("std::array is smaller than JSON") - { - std::array arr2 = {{8, 9}}; - j1.get_to(arr2); - CHECK(arr2[0] == 1); - CHECK(arr2[1] == 2); - } - } - - SECTION("std::valarray") - { - // valarray has no operator== that returns bool, so compare via a vector copy - const auto v1 = j1.get>(); - CHECK((std::vector(std::begin(v1), std::end(v1)) == std::vector {1, 2, 3, 4})); - const auto v2 = j2.get>(); - CHECK((std::vector(std::begin(v2), std::end(v2)) == std::vector {1u, 2u, 3u, 4u})); - const auto v3 = j3.get>(); - CHECK((std::vector(std::begin(v3), std::end(v3)) == std::vector {1.2, 2.3, 3.4, 4.5})); - const auto v4 = j4.get>(); - CHECK((std::vector(std::begin(v4), std::end(v4)) == std::vector {true, false, true})); - const auto v5 = j5.get>(); - CHECK((std::vector(std::begin(v5), std::end(v5)) == std::vector {"one", "two", "three"})); - } - - SECTION("std::vector") - { - CHECK(j1.get>() == (std::vector {1, 2, 3, 4})); - CHECK(j2.get>() == (std::vector {1u, 2u, 3u, 4u})); - CHECK(j3.get>() == (std::vector {1.2, 2.3, 3.4, 4.5})); - CHECK(j4.get>() == (std::vector {true, false, true})); - CHECK(j5.get>() == (std::vector {"one", "two", "three"})); - } - - SECTION("std::deque") - { - CHECK(j1.get>() == (std::deque {1, 2, 3, 4})); - CHECK(j2.get>() == (std::deque {1u, 2u, 3u, 4u})); - CHECK(j3.get>() == (std::deque {1.2, 2.3, 3.4, 4.5})); - CHECK(j4.get>() == (std::deque {true, false, true})); - CHECK(j5.get>() == (std::deque {"one", "two", "three"})); - } - - SECTION("std::set") - { - CHECK(j1.get>() == (std::set {1, 2, 3, 4})); - CHECK(j2.get>() == (std::set {1u, 2u, 3u, 4u})); - CHECK(j3.get>() == (std::set {1.2, 2.3, 3.4, 4.5})); - CHECK(j4.get>() == (std::set {true, false, true})); - CHECK(j5.get>() == (std::set {"one", "two", "three"})); - } - - SECTION("std::unordered_set") - { - CHECK(j1.get>() == (std::unordered_set {1, 2, 3, 4})); - CHECK(j2.get>() == (std::unordered_set {1u, 2u, 3u, 4u})); - CHECK(j3.get>() == (std::unordered_set {1.2, 2.3, 3.4, 4.5})); - CHECK(j4.get>() == (std::unordered_set {true, false, true})); - CHECK(j5.get>() == (std::unordered_set {"one", "two", "three"})); - } - - SECTION("std::map (array of pairs)") - { - const std::map m{{0, 1}, {1, 2}, {2, 3}}; - json const j6 = m; - - auto m2 = j6.get>(); - CHECK(m == m2); - - json const j7 = {0, 1, 2, 3}; - json const j8 = 2; -#if JSON_DIAGNOSTICS - CHECK_THROWS_WITH_AS((j7.get>()), - "[json.exception.type_error.302] (/0) type must be array, " - "but is number", json::type_error&); -#else - CHECK_THROWS_WITH_AS((j7.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number", json::type_error&); -#endif - CHECK_THROWS_WITH_AS((j8.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number", json::type_error&); - - SECTION("superfluous entries") - { - json const j9 = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}; - m2 = j9.get>(); - CHECK(m == m2); - } - } - - SECTION("std::unordered_map (array of pairs)") - { - const std::unordered_map m{{0, 1}, {1, 2}, {2, 3}}; - json const j6 = m; - - auto m2 = j6.get>(); - CHECK(m == m2); - - json const j7 = {0, 1, 2, 3}; - json const j8 = 2; -#if JSON_DIAGNOSTICS - CHECK_THROWS_WITH_AS((j7.get>()), - "[json.exception.type_error.302] (/0) type must be array, " - "but is number", json::type_error&); -#else - CHECK_THROWS_WITH_AS((j7.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number", json::type_error&); -#endif - CHECK_THROWS_WITH_AS((j8.get>()), - "[json.exception.type_error.302] type must be array, " - "but is number", json::type_error&); - - SECTION("superfluous entries") - { - json const j9{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}; - m2 = j9.get>(); - CHECK(m == m2); - } - } - - SECTION("exception in case of a non-object type") - { - // does type really must be an array? or it rather must not be null? - // that's what I thought when other test like this one broke - CHECK_THROWS_WITH_AS( - (json().get>()), - "[json.exception.type_error.302] type must be array, but is null", json::type_error&); - CHECK_THROWS_WITH_AS( - (json().get>()), - "[json.exception.type_error.302] type must be array, but is null", json::type_error&); - CHECK_THROWS_WITH_AS( - (json().get>()), - "[json.exception.type_error.302] type must be array, but is null", json::type_error&); - CHECK_THROWS_WITH_AS( - (json().get>()), - "[json.exception.type_error.302] type must be array, but is null", json::type_error&); - CHECK_THROWS_WITH_AS( - (json().get>()), - "[json.exception.type_error.302] type must be array, but is null", json::type_error&); - CHECK_THROWS_WITH_AS( - (json().get>()), - "[json.exception.type_error.302] type must be array, but is null", json::type_error&); - } - } - } } -enum class cards {kreuz, pik, herz, karo}; - -// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive -NLOHMANN_JSON_SERIALIZE_ENUM(cards, -{ - {cards::kreuz, "kreuz"}, - {cards::pik, "pik"}, - {cards::pik, "puk"}, // second entry for cards::puk; will not be used - {cards::herz, "herz"}, - {cards::karo, "karo"} -}) - -enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) -{ - TS_STOPPED, - TS_RUNNING, - TS_COMPLETED, - TS_INVALID = -1, -}; - -// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive -NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, -{ - {TS_INVALID, nullptr}, - {TS_STOPPED, "stopped"}, - {TS_RUNNING, "running"}, - {TS_COMPLETED, "completed"}, -}) - -TEST_CASE("JSON to enum mapping") -{ - SECTION("enum class") - { - // enum -> json - CHECK(json(cards::kreuz) == "kreuz"); - CHECK(json(cards::pik) == "pik"); - CHECK(json(cards::herz) == "herz"); - CHECK(json(cards::karo) == "karo"); - - // json -> enum - CHECK(cards::kreuz == json("kreuz")); - CHECK(cards::pik == json("pik")); - CHECK(cards::herz == json("herz")); - CHECK(cards::karo == json("karo")); - - // invalid json -> first enum - CHECK(cards::kreuz == json("what?").get()); - } - - SECTION("traditional enum") - { - // enum -> json - CHECK(json(TS_STOPPED) == "stopped"); - CHECK(json(TS_RUNNING) == "running"); - CHECK(json(TS_COMPLETED) == "completed"); - CHECK(json(TS_INVALID) == json()); - - // json -> enum - CHECK(TS_STOPPED == json("stopped")); - CHECK(TS_RUNNING == json("running")); - CHECK(TS_COMPLETED == json("completed")); - CHECK(TS_INVALID == json()); - - // invalid json -> first enum - CHECK(TS_INVALID == json("what?").get()); - } -} - -enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping - -// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive -NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards, -{ - {strict_cards::kreuz, "kreuz"}, - {strict_cards::pik, "pik"}, - {strict_cards::pik, "puk"}, // second entry for cards::pik; will not be used - {strict_cards::herz, "herz"}, - {strict_cards::karo, "karo"} -}) - -enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) -{ - STRICT_TS_STOPPED, - STRICT_TS_RUNNING, - STRICT_TS_COMPLETED, - STRICT_TS_OTHER, // STRICT_TS_OTHER not in mapping - STRICT_TS_INVALID = -1, -}; - -// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive -NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState, -{ - {STRICT_TS_INVALID, nullptr}, - {STRICT_TS_STOPPED, "stopped"}, - {STRICT_TS_RUNNING, "running"}, - {STRICT_TS_COMPLETED, "completed"}, -}) - -// regression test for #5708 item 2: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT must not rely on -// unqualified lookup of a helper name that a user's own namespace may also declare -namespace ns_with_colliding_name -{ -// NOLINTNEXTLINE(misc-use-internal-linkage) - used to shadow the library's internal helper name -inline void templated_json_throw(int /*unused*/) {} - -enum class colliding_enum { a, b }; - -// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive -NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(colliding_enum, -{ - {colliding_enum::a, "a"}, - {colliding_enum::b, "b"} -}) -} // namespace ns_with_colliding_name - -TEST_CASE("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT in a namespace with a colliding name") -{ - using ns_with_colliding_name::colliding_enum; - - CHECK(json(colliding_enum::a) == "a"); - CHECK(colliding_enum::b == json("b")); - - json _; - CHECK_THROWS_WITH_AS(_ = json("nope").get(), "[json.exception.out_of_range.410] enum value out of range for colliding_enum: \"nope\"", json::out_of_range&); -} - -TEST_CASE("Strict JSON to enum mapping") -{ - SECTION("enum class") - { - // enum -> json - CHECK(json(strict_cards::kreuz) == "kreuz"); - CHECK(json(strict_cards::pik) == "pik"); - CHECK(json(strict_cards::herz) == "herz"); - CHECK(json(strict_cards::karo) == "karo"); - - // json -> enum - CHECK(json("kreuz").get() == strict_cards::kreuz); - CHECK(json("pik").get() == strict_cards::pik); - CHECK(json("herz").get() == strict_cards::herz); - CHECK(json("karo").get() == strict_cards::karo); - - // comparison of enum and json - CHECK(strict_cards::kreuz == json("kreuz")); - CHECK(strict_cards::pik == json("pik")); - CHECK(strict_cards::herz == json("herz")); - CHECK(strict_cards::karo == json("karo")); - - // invalid json -> exception thrown - json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&); - - // conversion of unmapped enum -> exception thrown - CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); - - // comparing an unmapped enum with json throws the same exception - // (the scalar comparison operators used to be noexcept, so this - // called std::terminate) - CHECK_THROWS_WITH_AS(static_cast(strict_cards::andere == json("andere")), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); - CHECK_THROWS_WITH_AS(static_cast(json("andere") != strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); - - // invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the - // message (regression test for #5667); such strings can reach get() unvalidated, - // e.g. from from_cbor()/from_msgpack() (#5529) - const json j_invalid_utf8 = "\xFF"; - CHECK_THROWS_WITH_AS(_ = j_invalid_utf8.get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"\xEF\xBF\xBD\"", json::out_of_range&); - } - - SECTION("traditional enum") - { - // enum -> json - CHECK(json(STRICT_TS_STOPPED) == "stopped"); - CHECK(json(STRICT_TS_RUNNING) == "running"); - CHECK(json(STRICT_TS_COMPLETED) == "completed"); - CHECK(json(STRICT_TS_INVALID) == json()); - - // json -> enum - CHECK(json("stopped").get() == STRICT_TS_STOPPED); - CHECK(json("running").get() == STRICT_TS_RUNNING); - CHECK(json("completed").get() == STRICT_TS_COMPLETED); - CHECK(json().get() == STRICT_TS_INVALID); - - // comparison of enum and json - CHECK(STRICT_TS_STOPPED == json("stopped")); - CHECK(STRICT_TS_RUNNING == json("running")); - CHECK(STRICT_TS_COMPLETED == json("completed")); - CHECK(STRICT_TS_INVALID == json()); - - // invalid json -> exception thrown - json _; - CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&); - - // conversion of unmapped enum -> exception thrown - CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); - - // comparing an unmapped enum with json throws the same exception - CHECK_THROWS_WITH_AS(static_cast(STRICT_TS_OTHER < json("x")), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); - } -} - - -#ifdef JSON_HAS_CPP_17 -#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM -TEST_CASE("std::filesystem::path") -{ - SECTION("ascii") - { - json const j_string = "Path"; - auto p = j_string.template get(); - json const j_path = p; - - CHECK(j_path.template get() == - j_string.template get()); - } - - SECTION("utf-8") - { - json const j_string = "P\xc4\x9b\xc5\xa1ina"; - auto p = j_string.template get(); - json const j_path = p; - - CHECK(j_path.template get() == - j_string.template get()); - } -} -#endif - -// the ADL to_json overload for std::u8string only exists under the same guard -// as std::filesystem::path support (it is otherwise only reached indirectly, -// via std::filesystem::path::u8string()) -- mirror both #if conditions from -// include/nlohmann/detail/conversions/to_json.hpp exactly -#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM -#if defined(__cpp_lib_char8_t) -TEST_CASE("std::u8string") -{ - SECTION("ascii") - { - const std::u8string s = u8"Path"; - json const j = s; - - CHECK(j.template get() == "Path"); - } - - SECTION("utf-8") - { - // use \u universal-character-names (rather than raw \x byte escapes - // or literal non-ASCII source bytes) to compose the multi-byte UTF-8 - // encoding -- MSVC treats \x escapes used that way inside a u8 - // literal as a nonstandard extension (warning C5321), which some of - // our CI configs promote to an error; \u is portable and produces - // the exact same encoded bytes without depending on the source - // file's encoding - const std::u8string s = u8"P\u011B\u0161ina"; - json const j = s; - - CHECK(j.template get() == "P\xc4\x9b\xc5\xa1ina"); - } -} -#endif -#endif - -#if !defined(JSON_NOEXCEPTION) -namespace -{ -// a type whose to_json reports an error by throwing, used below to check that -// converting a std::optional to JSON propagates an exception thrown while -// converting its contained value instead of calling std::terminate (#5642) -struct throwing_to_json_type {}; - -[[noreturn]] void to_json(json& /*unused*/, const throwing_to_json_type& /*unused*/) -{ - throw std::runtime_error("cannot serialize throwing_to_json_type"); -} -} // namespace -#endif - -TEST_CASE("std::optional") -{ - SECTION("null") - { - const json j_null; - const std::optional opt_null; - - CHECK(json(opt_null) == j_null); - CHECK(j_null.get>() == std::nullopt); - - // Constructing std::optional directly from JSON null throws because - // std::optional's own converting constructor is chosen over basic_json's - // operator T(). This is a language-level limitation (std::optional is - // constructible from T, and T is constructible from basic_json via the - // operator); there is no SFINAE path that distinguishes "call from inside - // std::optional's constructor" from "direct call". Use get>() - // or get_to() instead for correct null handling. See #4864 and #5246. - CHECK_THROWS_WITH_AS(std::optional(j_null), - "[json.exception.type_error.302] type must be string, but is null", json::type_error&); - CHECK_THROWS_WITH_AS(std::optional(j_null), - "[json.exception.type_error.302] type must be number, but is null", json::type_error&); - - // Assignment goes through the same overload resolution as direct - // construction, so it throws for the same reason. This relies on - // basic_json's implicit conversion operator, so it only applies - // when JSON_USE_IMPLICIT_CONVERSIONS is enabled (the default). -#if JSON_USE_IMPLICIT_CONVERSIONS - std::optional opt_assign; - CHECK_THROWS_WITH_AS(opt_assign = j_null, - "[json.exception.type_error.302] type must be string, but is null", json::type_error&); -#endif - - // get_to() is the correct way to obtain std::nullopt from a JSON null. - std::optional opt_get_to = "placeholder"; - j_null.get_to(opt_get_to); - CHECK(opt_get_to == std::nullopt); - } - - SECTION("string") - { - json j_string = "string"; - std::optional opt_string = "string"; - - CHECK(json(opt_string) == j_string); - CHECK(std::optional(j_string) == opt_string); - // false positive: Infer attributes the destruction of the temporaries above to opt_string - // @infer-ignore USE_AFTER_DELETE - } - - SECTION("bool") - { - json j_bool = true; - std::optional opt_bool = true; - - CHECK(json(opt_bool) == j_bool); - CHECK(std::optional(j_bool) == opt_bool); - } - - SECTION("number") - { - json j_number = 1; - std::optional opt_int = 1; - - CHECK(json(opt_int) == j_number); - CHECK(j_number.get>() == opt_int); - } - - SECTION("array") - { - json j_array = {1, 2, nullptr}; - std::vector> opt_array = {{1, 2, std::nullopt}}; - - CHECK(json(opt_array) == j_array); - CHECK(j_array.get>>() == opt_array); - } - - SECTION("object") - { - json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}}; - std::map> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}}; - - CHECK(json(opt_object) == j_object); - CHECK(std::map>(j_object) == opt_object); - } - -#if !defined(JSON_NOEXCEPTION) - SECTION("exception from contained value's to_json propagates (#5642)") - { - // to_json(BasicJsonType&, const std::optional&) must not be - // noexcept: it calls T's to_json, which may throw (a user-defined - // to_json that reports an error, or std::bad_alloc for T = - // std::string/vector/json). Before the fix, this called - // std::terminate() instead of letting the exception propagate. - const std::optional opt = throwing_to_json_type{}; - CHECK_THROWS_WITH_AS(json(opt), "cannot serialize throwing_to_json_type", std::runtime_error&); - - // the conversion is noexcept exactly when converting the contained value is - static_assert(!std::is_nothrow_constructible&>::value); - static_assert(std::is_nothrow_constructible&>::value); - } -#endif -} -#endif - #ifdef JSON_HAS_CPP_17 #undef JSON_HAS_CPP_17 #endif diff --git a/tests/src/unit-conversions2.cpp b/tests/src/unit-conversions2.cpp new file mode 100644 index 000000000..eca482b83 --- /dev/null +++ b/tests/src/unit-conversions2.cpp @@ -0,0 +1,868 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +// cmake/test.cmake selects the C++ standard versions with which to build a +// unit test based on the presence of JSON_HAS_CPP_ macros. +// When using macros that are only defined for particular versions of the standard +// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding +// version macro in a comment close by, like this: +// JSON_HAS_CPP_ (do not remove; see note at top of file) + +#include "doctest_compatibility.h" + +// skip tests if JSON_DisableEnumSerialization=ON (#4384) +#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) + #define SKIP_TESTS_FOR_ENUM_SERIALIZATION +#endif + +#define JSON_TESTS_PRIVATE +#include +using nlohmann::json; + +#include +#include +#include +#include +#include +#include +#include + +// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair +DOCTEST_CLANG_SUPPRESS_WARNING_PUSH +DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") + +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +#ifdef JSON_HAS_CPP_17 + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + +#if defined(JSON_HAS_CPP_17) + #include +#endif + +TEST_CASE("value conversion") +{ + SECTION("get a binary value (explicit)") + { + json::binary_t const n_reference{{1, 2, 3}}; + json j(n_reference); + + SECTION("binary_t") + { + json::binary_t const b = j.get(); + CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); + } + + SECTION("get_binary()") + { + SECTION("non-const") + { + auto& b = j.get_binary(); + CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); + } + + SECTION("non-const") + { + const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization) + const auto& b = j_const.get_binary(); + CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); + } + } + + SECTION("exception in case of a non-string type") + { + json j_null(json::value_t::null); + json j_object(json::value_t::object); + json j_array(json::value_t::array); + json j_string(json::value_t::string); + json j_boolean(json::value_t::boolean); + const json j_null_const(json::value_t::null); + const json j_object_const(json::value_t::object); + const json j_array_const(json::value_t::array); + const json j_string_const(json::value_t::string); + const json j_boolean_const(json::value_t::boolean); + + CHECK_THROWS_WITH_AS(j_null.get(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object.get(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array.get(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string.get(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean.get(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null_const.get(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object_const.get(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array_const.get(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string_const.get(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean_const.get(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null.get_binary(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object.get_binary(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array.get_binary(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string.get_binary(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean.get_binary(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + } + } + +#if JSON_USE_IMPLICIT_CONVERSIONS + SECTION("get a binary value (implicit)") + { + json::binary_t const n_reference{{1, 2, 3}}; + json const j(n_reference); + + SECTION("binary_t") + { + json::binary_t const b = j; + CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); + } + } +#endif + +#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION + SECTION("get an enum") + { + enum c_enum { value_1, value_2 }; // NOLINT(cppcoreguidelines-use-enum-class) + enum class cpp_enum { value_1, value_2 }; + + CHECK(json(value_1).get() == value_1); + CHECK(json(cpp_enum::value_1).get() == cpp_enum::value_1); + } + + SECTION("get an enum with underlying type bool (#5671)") + { + enum class bool_enum : bool { off, on }; + + CHECK(json(bool_enum::off).get() == bool_enum::off); + CHECK(json(bool_enum::on).get() == bool_enum::on); + } +#endif + + SECTION("more involved conversions") + { + SECTION("object-like STL containers") + { + json const j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; + json const j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; + json const j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; + json const j4 = {{"one", true}, {"two", false}, {"three", true}}; + json const j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; + + SECTION("std::map") + { + CHECK(j1.get>() == (std::map {{"one", 1}, {"two", 2}, {"three", 3}})); + CHECK(j2.get>() == (std::map {{"one", 1u}, {"two", 2u}, {"three", 3u}})); + CHECK(j3.get>() == (std::map {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); + CHECK(j4.get>() == (std::map {{"one", true}, {"two", false}, {"three", true}})); + CHECK(j5.get>() == (std::map {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); + } + + SECTION("std::unordered_map") + { + CHECK(j1.get>() == (std::unordered_map {{"one", 1}, {"two", 2}, {"three", 3}})); + CHECK(j2.get>() == (std::unordered_map {{"one", 1u}, {"two", 2u}, {"three", 3u}})); + CHECK(j3.get>() == (std::unordered_map {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); + CHECK(j4.get>() == (std::unordered_map {{"one", true}, {"two", false}, {"three", true}})); + const auto m5 = j5.get>(); + CHECK(m5 == (std::unordered_map {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); + CHECK(m5.at("one") == "eins"); + } + + SECTION("reserve is called on containers that support it (#5406)") + { + // build a larger object so that a missing/incorrect reserve() + // call would be more likely to corrupt or drop elements + json j_large; + for (int i = 0; i < 100; ++i) + { + j_large[std::to_string(i)] = i; + } + + SECTION("std::unordered_map (supports reserve)") + { + const auto m = j_large.get>(); + CHECK(m.size() == 100); + for (int i = 0; i < 100; ++i) + { + CHECK(m.at(std::to_string(i)) == i); + } + } + + SECTION("std::map (no reserve, fallback path)") + { + const auto m = j_large.get>(); + CHECK(m.size() == 100); + for (int i = 0; i < 100; ++i) + { + CHECK(m.at(std::to_string(i)) == i); + } + } + } + + SECTION("std::multimap") + { + CHECK(j1.get>() == (std::multimap {{"one", 1}, {"two", 2}, {"three", 3}})); + CHECK(j2.get>() == (std::multimap {{"one", 1u}, {"two", 2u}, {"three", 3u}})); + CHECK(j3.get>() == (std::multimap {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); + CHECK(j4.get>() == (std::multimap {{"one", true}, {"two", false}, {"three", true}})); + const auto m5 = j5.get>(); + CHECK(m5 == (std::multimap {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); + CHECK(m5.find("one")->second == "eins"); + } + + SECTION("std::unordered_multimap") + { + CHECK(j1.get>() == (std::unordered_multimap {{"one", 1}, {"two", 2}, {"three", 3}})); + CHECK(j2.get>() == (std::unordered_multimap {{"one", 1u}, {"two", 2u}, {"three", 3u}})); + CHECK(j3.get>() == (std::unordered_multimap {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); + CHECK(j4.get>() == (std::unordered_multimap {{"one", true}, {"two", false}, {"three", true}})); + const auto m5 = j5.get>(); + CHECK(m5 == (std::unordered_multimap {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); + CHECK(m5.find("one")->second == "eins"); + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_WITH_AS( + (json().get>()), + "[json.exception.type_error.302] type must be object, but is null", json::type_error&); + } + } + + SECTION("array-like STL containers") + { + json const j1 = {1, 2, 3, 4}; + json const j2 = {1u, 2u, 3u, 4u}; + json const j3 = {1.2, 2.3, 3.4, 4.5}; + json const j4 = {true, false, true}; + json const j5 = {"one", "two", "three"}; + + SECTION("std::list") + { + CHECK(j1.get>() == (std::list {1, 2, 3, 4})); + CHECK(j2.get>() == (std::list {1u, 2u, 3u, 4u})); + CHECK(j3.get>() == (std::list {1.2, 2.3, 3.4, 4.5})); + CHECK(j4.get>() == (std::list {true, false, true})); + CHECK(j5.get>() == (std::list {"one", "two", "three"})); + } + + SECTION("std::forward_list") + { + CHECK(j1.get>() == (std::forward_list {1, 2, 3, 4})); + CHECK(j2.get>() == (std::forward_list {1u, 2u, 3u, 4u})); + CHECK(j3.get>() == (std::forward_list {1.2, 2.3, 3.4, 4.5})); + CHECK(j4.get>() == (std::forward_list {true, false, true})); + CHECK(j5.get>() == (std::forward_list {"one", "two", "three"})); + } + + SECTION("std::array") + { + CHECK(j1.get>() == (std::array {{1, 2, 3, 4}})); + // only the first 3 elements of j2 are converted, since the target array is smaller + CHECK(j2.get>() == (std::array {{1u, 2u, 3u}})); + CHECK(j3.get>() == (std::array {{1.2, 2.3, 3.4, 4.5}})); + CHECK(j4.get>() == (std::array {{true, false, true}})); + CHECK(j5.get>() == (std::array {{"one", "two", "three"}})); + + SECTION("std::array is larger than JSON") + { + std::array arr6 = {{1, 2, 3, 4, 5, 6}}; + CHECK_THROWS_WITH_AS(j1.get_to(arr6), "[json.exception.out_of_range.401] " + "array index 4 is out of range", json::out_of_range&); + } + + SECTION("std::array is smaller than JSON") + { + std::array arr2 = {{8, 9}}; + j1.get_to(arr2); + CHECK(arr2[0] == 1); + CHECK(arr2[1] == 2); + } + } + + SECTION("std::valarray") + { + // valarray has no operator== that returns bool, so compare via a vector copy + const auto v1 = j1.get>(); + CHECK((std::vector(std::begin(v1), std::end(v1)) == std::vector {1, 2, 3, 4})); + const auto v2 = j2.get>(); + CHECK((std::vector(std::begin(v2), std::end(v2)) == std::vector {1u, 2u, 3u, 4u})); + const auto v3 = j3.get>(); + CHECK((std::vector(std::begin(v3), std::end(v3)) == std::vector {1.2, 2.3, 3.4, 4.5})); + const auto v4 = j4.get>(); + CHECK((std::vector(std::begin(v4), std::end(v4)) == std::vector {true, false, true})); + const auto v5 = j5.get>(); + CHECK((std::vector(std::begin(v5), std::end(v5)) == std::vector {"one", "two", "three"})); + } + + SECTION("std::vector") + { + CHECK(j1.get>() == (std::vector {1, 2, 3, 4})); + CHECK(j2.get>() == (std::vector {1u, 2u, 3u, 4u})); + CHECK(j3.get>() == (std::vector {1.2, 2.3, 3.4, 4.5})); + CHECK(j4.get>() == (std::vector {true, false, true})); + CHECK(j5.get>() == (std::vector {"one", "two", "three"})); + } + + SECTION("std::deque") + { + CHECK(j1.get>() == (std::deque {1, 2, 3, 4})); + CHECK(j2.get>() == (std::deque {1u, 2u, 3u, 4u})); + CHECK(j3.get>() == (std::deque {1.2, 2.3, 3.4, 4.5})); + CHECK(j4.get>() == (std::deque {true, false, true})); + CHECK(j5.get>() == (std::deque {"one", "two", "three"})); + } + + SECTION("std::set") + { + CHECK(j1.get>() == (std::set {1, 2, 3, 4})); + CHECK(j2.get>() == (std::set {1u, 2u, 3u, 4u})); + CHECK(j3.get>() == (std::set {1.2, 2.3, 3.4, 4.5})); + CHECK(j4.get>() == (std::set {true, false, true})); + CHECK(j5.get>() == (std::set {"one", "two", "three"})); + } + + SECTION("std::unordered_set") + { + CHECK(j1.get>() == (std::unordered_set {1, 2, 3, 4})); + CHECK(j2.get>() == (std::unordered_set {1u, 2u, 3u, 4u})); + CHECK(j3.get>() == (std::unordered_set {1.2, 2.3, 3.4, 4.5})); + CHECK(j4.get>() == (std::unordered_set {true, false, true})); + CHECK(j5.get>() == (std::unordered_set {"one", "two", "three"})); + } + + SECTION("std::map (array of pairs)") + { + const std::map m{{0, 1}, {1, 2}, {2, 3}}; + json const j6 = m; + + auto m2 = j6.get>(); + CHECK(m == m2); + + json const j7 = {0, 1, 2, 3}; + json const j8 = 2; +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS((j7.get>()), + "[json.exception.type_error.302] (/0) type must be array, " + "but is number", json::type_error&); +#else + CHECK_THROWS_WITH_AS((j7.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); +#endif + CHECK_THROWS_WITH_AS((j8.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); + + SECTION("superfluous entries") + { + json const j9 = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}; + m2 = j9.get>(); + CHECK(m == m2); + } + } + + SECTION("std::unordered_map (array of pairs)") + { + const std::unordered_map m{{0, 1}, {1, 2}, {2, 3}}; + json const j6 = m; + + auto m2 = j6.get>(); + CHECK(m == m2); + + json const j7 = {0, 1, 2, 3}; + json const j8 = 2; +#if JSON_DIAGNOSTICS + CHECK_THROWS_WITH_AS((j7.get>()), + "[json.exception.type_error.302] (/0) type must be array, " + "but is number", json::type_error&); +#else + CHECK_THROWS_WITH_AS((j7.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); +#endif + CHECK_THROWS_WITH_AS((j8.get>()), + "[json.exception.type_error.302] type must be array, " + "but is number", json::type_error&); + + SECTION("superfluous entries") + { + json const j9{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}; + m2 = j9.get>(); + CHECK(m == m2); + } + } + + SECTION("exception in case of a non-object type") + { + // does type really must be an array? or it rather must not be null? + // that's what I thought when other test like this one broke + CHECK_THROWS_WITH_AS( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + CHECK_THROWS_WITH_AS( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null", json::type_error&); + } + } + } +} + +enum class cards {kreuz, pik, herz, karo}; + +// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM(cards, +{ + {cards::kreuz, "kreuz"}, + {cards::pik, "pik"}, + {cards::pik, "puk"}, // second entry for cards::puk; will not be used + {cards::herz, "herz"}, + {cards::karo, "karo"} +}) + +enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) +{ + TS_STOPPED, + TS_RUNNING, + TS_COMPLETED, + TS_INVALID = -1, +}; + +// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, +{ + {TS_INVALID, nullptr}, + {TS_STOPPED, "stopped"}, + {TS_RUNNING, "running"}, + {TS_COMPLETED, "completed"}, +}) + +TEST_CASE("JSON to enum mapping") +{ + SECTION("enum class") + { + // enum -> json + CHECK(json(cards::kreuz) == "kreuz"); + CHECK(json(cards::pik) == "pik"); + CHECK(json(cards::herz) == "herz"); + CHECK(json(cards::karo) == "karo"); + + // json -> enum + CHECK(cards::kreuz == json("kreuz")); + CHECK(cards::pik == json("pik")); + CHECK(cards::herz == json("herz")); + CHECK(cards::karo == json("karo")); + + // invalid json -> first enum + CHECK(cards::kreuz == json("what?").get()); + } + + SECTION("traditional enum") + { + // enum -> json + CHECK(json(TS_STOPPED) == "stopped"); + CHECK(json(TS_RUNNING) == "running"); + CHECK(json(TS_COMPLETED) == "completed"); + CHECK(json(TS_INVALID) == json()); + + // json -> enum + CHECK(TS_STOPPED == json("stopped")); + CHECK(TS_RUNNING == json("running")); + CHECK(TS_COMPLETED == json("completed")); + CHECK(TS_INVALID == json()); + + // invalid json -> first enum + CHECK(TS_INVALID == json("what?").get()); + } +} + +enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping + +// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards, +{ + {strict_cards::kreuz, "kreuz"}, + {strict_cards::pik, "pik"}, + {strict_cards::pik, "puk"}, // second entry for cards::pik; will not be used + {strict_cards::herz, "herz"}, + {strict_cards::karo, "karo"} +}) + +enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) +{ + STRICT_TS_STOPPED, + STRICT_TS_RUNNING, + STRICT_TS_COMPLETED, + STRICT_TS_OTHER, // STRICT_TS_OTHER not in mapping + STRICT_TS_INVALID = -1, +}; + +// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState, +{ + {STRICT_TS_INVALID, nullptr}, + {STRICT_TS_STOPPED, "stopped"}, + {STRICT_TS_RUNNING, "running"}, + {STRICT_TS_COMPLETED, "completed"}, +}) + +// regression test for #5708 item 2: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT must not rely on +// unqualified lookup of a helper name that a user's own namespace may also declare +namespace ns_with_colliding_name +{ +// NOLINTNEXTLINE(misc-use-internal-linkage) - used to shadow the library's internal helper name +inline void templated_json_throw(int /*unused*/) {} + +enum class colliding_enum { a, b }; + +// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive +NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(colliding_enum, +{ + {colliding_enum::a, "a"}, + {colliding_enum::b, "b"} +}) +} // namespace ns_with_colliding_name + +TEST_CASE("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT in a namespace with a colliding name") +{ + using ns_with_colliding_name::colliding_enum; + + CHECK(json(colliding_enum::a) == "a"); + CHECK(colliding_enum::b == json("b")); + + json _; + CHECK_THROWS_WITH_AS(_ = json("nope").get(), "[json.exception.out_of_range.410] enum value out of range for colliding_enum: \"nope\"", json::out_of_range&); +} + +TEST_CASE("Strict JSON to enum mapping") +{ + SECTION("enum class") + { + // enum -> json + CHECK(json(strict_cards::kreuz) == "kreuz"); + CHECK(json(strict_cards::pik) == "pik"); + CHECK(json(strict_cards::herz) == "herz"); + CHECK(json(strict_cards::karo) == "karo"); + + // json -> enum + CHECK(json("kreuz").get() == strict_cards::kreuz); + CHECK(json("pik").get() == strict_cards::pik); + CHECK(json("herz").get() == strict_cards::herz); + CHECK(json("karo").get() == strict_cards::karo); + + // comparison of enum and json + CHECK(strict_cards::kreuz == json("kreuz")); + CHECK(strict_cards::pik == json("pik")); + CHECK(strict_cards::herz == json("herz")); + CHECK(strict_cards::karo == json("karo")); + + // invalid json -> exception thrown + json _; + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&); + + // conversion of unmapped enum -> exception thrown + CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); + + // comparing an unmapped enum with json throws the same exception + // (the scalar comparison operators used to be noexcept, so this + // called std::terminate) + CHECK_THROWS_WITH_AS(static_cast(strict_cards::andere == json("andere")), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); + CHECK_THROWS_WITH_AS(static_cast(json("andere") != strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); + + // invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the + // message (regression test for #5667); such strings can reach get() unvalidated, + // e.g. from from_cbor()/from_msgpack() (#5529) + const json j_invalid_utf8 = "\xFF"; + CHECK_THROWS_WITH_AS(_ = j_invalid_utf8.get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"\xEF\xBF\xBD\"", json::out_of_range&); + } + + SECTION("traditional enum") + { + // enum -> json + CHECK(json(STRICT_TS_STOPPED) == "stopped"); + CHECK(json(STRICT_TS_RUNNING) == "running"); + CHECK(json(STRICT_TS_COMPLETED) == "completed"); + CHECK(json(STRICT_TS_INVALID) == json()); + + // json -> enum + CHECK(json("stopped").get() == STRICT_TS_STOPPED); + CHECK(json("running").get() == STRICT_TS_RUNNING); + CHECK(json("completed").get() == STRICT_TS_COMPLETED); + CHECK(json().get() == STRICT_TS_INVALID); + + // comparison of enum and json + CHECK(STRICT_TS_STOPPED == json("stopped")); + CHECK(STRICT_TS_RUNNING == json("running")); + CHECK(STRICT_TS_COMPLETED == json("completed")); + CHECK(STRICT_TS_INVALID == json()); + + // invalid json -> exception thrown + json _; + CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&); + + // conversion of unmapped enum -> exception thrown + CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); + + // comparing an unmapped enum with json throws the same exception + CHECK_THROWS_WITH_AS(static_cast(STRICT_TS_OTHER < json("x")), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); + } +} + + +#ifdef JSON_HAS_CPP_17 +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +TEST_CASE("std::filesystem::path") +{ + SECTION("ascii") + { + json const j_string = "Path"; + auto p = j_string.template get(); + json const j_path = p; + + CHECK(j_path.template get() == + j_string.template get()); + } + + SECTION("utf-8") + { + json const j_string = "P\xc4\x9b\xc5\xa1ina"; + auto p = j_string.template get(); + json const j_path = p; + + CHECK(j_path.template get() == + j_string.template get()); + } +} +#endif + +// the ADL to_json overload for std::u8string only exists under the same guard +// as std::filesystem::path support (it is otherwise only reached indirectly, +// via std::filesystem::path::u8string()) -- mirror both #if conditions from +// include/nlohmann/detail/conversions/to_json.hpp exactly +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +#if defined(__cpp_lib_char8_t) +TEST_CASE("std::u8string") +{ + SECTION("ascii") + { + const std::u8string s = u8"Path"; + json const j = s; + + CHECK(j.template get() == "Path"); + } + + SECTION("utf-8") + { + // use \u universal-character-names (rather than raw \x byte escapes + // or literal non-ASCII source bytes) to compose the multi-byte UTF-8 + // encoding -- MSVC treats \x escapes used that way inside a u8 + // literal as a nonstandard extension (warning C5321), which some of + // our CI configs promote to an error; \u is portable and produces + // the exact same encoded bytes without depending on the source + // file's encoding + const std::u8string s = u8"P\u011B\u0161ina"; + json const j = s; + + CHECK(j.template get() == "P\xc4\x9b\xc5\xa1ina"); + } +} +#endif +#endif + +#if !defined(JSON_NOEXCEPTION) +namespace +{ +// a type whose to_json reports an error by throwing, used below to check that +// converting a std::optional to JSON propagates an exception thrown while +// converting its contained value instead of calling std::terminate (#5642) +struct throwing_to_json_type {}; + +[[noreturn]] void to_json(json& /*unused*/, const throwing_to_json_type& /*unused*/) +{ + throw std::runtime_error("cannot serialize throwing_to_json_type"); +} +} // namespace +#endif + +TEST_CASE("std::optional") +{ + SECTION("null") + { + const json j_null; + const std::optional opt_null; + + CHECK(json(opt_null) == j_null); + CHECK(j_null.get>() == std::nullopt); + + // Constructing std::optional directly from JSON null throws because + // std::optional's own converting constructor is chosen over basic_json's + // operator T(). This is a language-level limitation (std::optional is + // constructible from T, and T is constructible from basic_json via the + // operator); there is no SFINAE path that distinguishes "call from inside + // std::optional's constructor" from "direct call". Use get>() + // or get_to() instead for correct null handling. See #4864 and #5246. + CHECK_THROWS_WITH_AS(std::optional(j_null), + "[json.exception.type_error.302] type must be string, but is null", json::type_error&); + CHECK_THROWS_WITH_AS(std::optional(j_null), + "[json.exception.type_error.302] type must be number, but is null", json::type_error&); + + // Assignment goes through the same overload resolution as direct + // construction, so it throws for the same reason. This relies on + // basic_json's implicit conversion operator, so it only applies + // when JSON_USE_IMPLICIT_CONVERSIONS is enabled (the default). +#if JSON_USE_IMPLICIT_CONVERSIONS + std::optional opt_assign; + CHECK_THROWS_WITH_AS(opt_assign = j_null, + "[json.exception.type_error.302] type must be string, but is null", json::type_error&); +#endif + + // get_to() is the correct way to obtain std::nullopt from a JSON null. + std::optional opt_get_to = "placeholder"; + j_null.get_to(opt_get_to); + CHECK(opt_get_to == std::nullopt); + } + + SECTION("string") + { + json j_string = "string"; + std::optional opt_string = "string"; + + CHECK(json(opt_string) == j_string); + CHECK(std::optional(j_string) == opt_string); + // false positive: Infer attributes the destruction of the temporaries above to opt_string + // @infer-ignore USE_AFTER_DELETE + } + + SECTION("bool") + { + json j_bool = true; + std::optional opt_bool = true; + + CHECK(json(opt_bool) == j_bool); + CHECK(std::optional(j_bool) == opt_bool); + } + + SECTION("number") + { + json j_number = 1; + std::optional opt_int = 1; + + CHECK(json(opt_int) == j_number); + CHECK(j_number.get>() == opt_int); + } + + SECTION("array") + { + json j_array = {1, 2, nullptr}; + std::vector> opt_array = {{1, 2, std::nullopt}}; + + CHECK(json(opt_array) == j_array); + CHECK(j_array.get>>() == opt_array); + } + + SECTION("object") + { + json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}}; + std::map> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}}; + + CHECK(json(opt_object) == j_object); + CHECK(std::map>(j_object) == opt_object); + } + +#if !defined(JSON_NOEXCEPTION) + SECTION("exception from contained value's to_json propagates (#5642)") + { + // to_json(BasicJsonType&, const std::optional&) must not be + // noexcept: it calls T's to_json, which may throw (a user-defined + // to_json that reports an error, or std::bad_alloc for T = + // std::string/vector/json). Before the fix, this called + // std::terminate() instead of letting the exception propagate. + const std::optional opt = throwing_to_json_type{}; + CHECK_THROWS_WITH_AS(json(opt), "cannot serialize throwing_to_json_type", std::runtime_error&); + + // the conversion is noexcept exactly when converting the contained value is + static_assert(!std::is_nothrow_constructible&>::value); + static_assert(std::is_nothrow_constructible&>::value); + } +#endif +} +#endif + +#ifdef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_17 +#endif + +#ifdef JSON_HAS_CPP_14 + #undef JSON_HAS_CPP_14 +#endif +DOCTEST_CLANG_SUPPRESS_WARNING_POP diff --git a/tests/src/unit-custom-base-class.cpp b/tests/src/unit-custom-base-class.cpp index 38b665793..b9e544705 100644 --- a/tests/src/unit-custom-base-class.cpp +++ b/tests/src/unit-custom-base-class.cpp @@ -505,7 +505,7 @@ static json_with_const_base make_nested_array(std::size_t depth) { if (depth == 0) { - return json_with_const_base(1); + return json_with_const_base(1); // NOLINT(modernize-return-braced-init-list): {1} would be an array } return json_with_const_base::array({make_nested_array(depth - 1)}); } diff --git a/tests/src/unit-disabled_exceptions.cpp b/tests/src/unit-disabled_exceptions.cpp index 8e3adf944..40a37c339 100644 --- a/tests/src/unit-disabled_exceptions.cpp +++ b/tests/src/unit-disabled_exceptions.cpp @@ -14,6 +14,9 @@ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") #include using json = nlohmann::json; +#ifdef JSON_TEST_NO_GLOBAL_UDLS + using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) +#endif ///////////////////////////////////////////////////////////////////// // for #2824 diff --git a/tests/src/unit-element_access2.cpp b/tests/src/unit-element_access2.cpp index efd7b15a0..9a7a58e6b 100644 --- a/tests/src/unit-element_access2.cpp +++ b/tests/src/unit-element_access2.cpp @@ -1963,8 +1963,8 @@ TEST_CASE("operator[] with user-defined std::string_view-convertible types") }; json j = {{"foo", "from_class"}, {"bar", "from_struct"}}; - TestClass foo_obj; - TestStruct bar_obj; + const TestClass foo_obj; + const TestStruct bar_obj; SECTION("read access") { @@ -2005,6 +2005,10 @@ TEST_CASE("keys convertible to std::string_view work with all lookup functions ( // 3.12.0, such a key worked with at, the const operator[], find, count and // contains via the conversion to std::string; #4958 made the KeyType&& // templates win overload resolution for it instead, and those then failed + // the lookups pick the conversion to std::string_view, which leaves the one + // to std::string unused; it has to exist to reproduce the ambiguity + DOCTEST_CLANG_SUPPRESS_WARNING_PUSH + DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function") struct DualKey { operator std::string() const @@ -2016,6 +2020,7 @@ TEST_CASE("keys convertible to std::string_view work with all lookup functions ( return "a"; } }; + DOCTEST_CLANG_SUPPRESS_WARNING_POP SECTION("nlohmann::json") { diff --git a/tests/src/unit-modifiers.cpp b/tests/src/unit-modifiers.cpp index 532283f95..3377eace0 100644 --- a/tests/src/unit-modifiers.cpp +++ b/tests/src/unit-modifiers.cpp @@ -1162,6 +1162,7 @@ TEST_CASE("update() on deeply nested values") TEST_CASE("update() with an argument that aliases *this (#5641)") { +#if !defined(JSON_NOEXCEPTION) // checks which exception is thrown, and that nothing changed SECTION("the target is checked before the argument, as before the copy") { json j = 1; @@ -1172,6 +1173,7 @@ TEST_CASE("update() with an argument that aliases *this (#5641)") CHECK_THROWS_WITH_AS(k.update(json::array()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); CHECK(k == json::object()); } +#endif SECTION("const reference") { diff --git a/tests/src/unit-msgpack.cpp b/tests/src/unit-msgpack.cpp index a92496144..c4b447d64 100644 --- a/tests/src/unit-msgpack.cpp +++ b/tests/src/unit-msgpack.cpp @@ -1560,7 +1560,7 @@ TEST_CASE("MessagePack") // dump() still requires valid UTF-8 and throws for such a value, // unless an error handler that replaces or ignores the bytes is // passed - CHECK_THROWS_AS(j_value.dump(), json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j_value.dump()), json::type_error&); // the same bytes as an object key round-trip as well const std::vector ill_formed_key = {0x81, 0xa2, 0xc0, 0xae, 0x01}; diff --git a/tests/src/unit-ordered_map.cpp b/tests/src/unit-ordered_map.cpp index dce3f61a5..0d0f7730c 100644 --- a/tests/src/unit-ordered_map.cpp +++ b/tests/src/unit-ordered_map.cpp @@ -430,8 +430,8 @@ TEST_CASE("ordered_map") SECTION("with T& (lvalue)") { - std::string one = "1"; - std::string four = "four"; + std::string one = "1"; // NOLINT(misc-const-correctness): emplace must accept a non-const lvalue + std::string four = "four"; // NOLINT(misc-const-correctness): see above auto res1 = om.emplace("eins", one); CHECK(res1.first == om.begin()); @@ -467,7 +467,7 @@ TEST_CASE("ordered_map") SECTION("with key of key_type (non-template overload)") { const std::string key_vier{"vier"}; - std::string four = "four"; + std::string four = "four"; // NOLINT(misc-const-correctness): emplace must accept a non-const lvalue auto res4 = om.emplace(key_vier, four); CHECK(res4.first == om.begin() + 3); diff --git a/tests/src/unit-ubjson.cpp b/tests/src/unit-ubjson.cpp index 450882a12..80ca214a4 100644 --- a/tests/src/unit-ubjson.cpp +++ b/tests/src/unit-ubjson.cpp @@ -2520,7 +2520,7 @@ TEST_CASE("Universal Binary JSON Specification Examples 1") CHECK_NOTHROW(j = json::from_ubjson(v)); REQUIRE(j.is_string()); CHECK(j.get_ref() == std::string("\xc0\xae")); - CHECK_THROWS_AS(j.dump(), json::type_error&); + CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&); CHECK(json::from_ubjson(json::to_ubjson(j)) == j); // the same bytes as an object key round-trip as well diff --git a/tests/src/unit-wstring.cpp b/tests/src/unit-wstring.cpp index c71684002..c555d54da 100644 --- a/tests/src/unit-wstring.cpp +++ b/tests/src/unit-wstring.cpp @@ -37,10 +37,10 @@ TEST_CASE("wide strings") // 32-bit wchar_t first encodes it as an ill-formed three-byte // sequence (rejected one byte later, at column 3) const char* const error_low_surrogate = sizeof(wchar_t) == 2 - ? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"'" + ? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xFF'" : "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xB0'"; const char* const error_high_surrogate = sizeof(wchar_t) == 2 - ? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"'" + ? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xFF'" : "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xA0'"; // a lone low surrogate cannot start a pair