diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 1a47a885c..d6b654077 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -3,13 +3,61 @@ name: Ubuntu on: [push, pull_request] jobs: - build: + gcc_build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 + - name: install_gcc + run: | + sudo apt update + sudo apt install gcc-10 g++-10 + shell: bash - name: cmake run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On + env: + CC: gcc-10 + CXX: g++-10 + - name: build + run: cmake --build build --parallel 10 + - name: test + run: cd build ; ctest -j 10 --output-on-failure + + clang_build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: install_gcc + run: | + sudo apt update + sudo apt install clang-10 + shell: bash + - name: cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On + env: + CC: clang-10 + CXX: clang++-10 + - name: build + run: cmake --build build --parallel 10 + - name: test + run: cd build ; ctest -j 10 --output-on-failure + + clang_build_cxx20: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: install_gcc + run: | + sudo apt update + sudo apt install clang-10 + shell: bash + - name: cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On -DCMAKE_CXX_STANDARD=20 -DCMAKE_CXX_STANDARD_REQUIRED=ON + env: + CC: clang-10 + CXX: clang++-10 - name: build run: cmake --build build --parallel 10 - name: test diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5846cf750..1778c9418 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -15,20 +15,6 @@ jobs: - name: test run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure - clang9: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v1 - - name: install Clang - run: curl -fsSL -o LLVM9.exe https://releases.llvm.org/9.0.0/LLVM-9.0.0-win64.exe ; 7z x LLVM9.exe -y -o"C:/Program Files/LLVM" - - name: cmake - run: cmake -S . -B build -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug -DJSON_BuildTests=On - - name: build - run: cmake --build build --parallel 10 - - name: test - run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure - clang10: runs-on: windows-latest diff --git a/.travis.yml b/.travis.yml index bf749c1e3..f48ee1fd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -124,18 +124,6 @@ matrix: # OSX / Clang - - os: osx - osx_image: xcode9.3 - - - os: osx - osx_image: xcode9.4 - - - os: osx - osx_image: xcode10 - - - os: osx - osx_image: xcode10.1 - - os: osx osx_image: xcode10.2 @@ -222,7 +210,7 @@ matrix: compiler: gcc env: - COMPILER=g++-9 - - CXXFLAGS=-std=c++2a + - CXX_STANDARD=17 addons: apt: sources: ['ubuntu-toolchain-r-test'] @@ -306,11 +294,11 @@ matrix: compiler: clang env: - COMPILER=clang++-7 - - CXXFLAGS=-std=c++1z + - CXX_STANDARD=17 addons: apt: sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-7'] - packages: ['g++-6', 'clang-7', 'ninja-build'] + packages: ['g++-7', 'clang-7', 'ninja-build'] ################ # build script # @@ -333,6 +321,9 @@ script: # by default, use implicit conversions - if [[ "${IMPLICIT_CONVERSIONS}" == "" ]]; then export IMPLICIT_CONVERSIONS=ON; fi + # append CXX_STANDARD to CMAKE_OPTIONS if required + - CMAKE_OPTIONS+=${CXX_STANDARD:+ -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DCMAKE_CXX_STANDARD_REQUIRED=ON} + # compile and execute unit tests - mkdir -p build && cd build - cmake .. ${CMAKE_OPTIONS} -DJSON_MultipleHeaders=${MULTIPLE_HEADERS} -DJSON_ImplicitConversions=${IMPLICIT_CONVERSIONS} -DJSON_BuildTests=On -GNinja && cmake --build . --config Release diff --git a/CMakeLists.txt b/CMakeLists.txt index fa77a5aed..44ede3e79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,15 @@ cmake_minimum_required(VERSION 3.1) ## project(nlohmann_json VERSION 3.9.1 LANGUAGES CXX) +## +## MAIN_PROJECT CHECK +## determine if nlohmann_json is built as a subproject (using add_subdirectory) or if it is the main project +## +set(MAIN_PROJECT OFF) +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(MAIN_PROJECT ON) +endif() + ## ## INCLUDE ## @@ -21,8 +30,8 @@ if (POLICY CMP0077) cmake_policy(SET CMP0077 NEW) endif () -option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON) -option(JSON_Install "Install CMake targets during install step." ON) +option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${MAIN_PROJECT}) +option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT}) option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) option(JSON_ImplicitConversions "Enable implicit conversions." ON) @@ -101,9 +110,8 @@ CONFIGURE_FILE( ## TESTS ## create and configure the unit test target ## -include(CTest) #adds option BUILD_TESTING (default ON) - -if(BUILD_TESTING AND JSON_BuildTests) +if (JSON_BuildTests) + include(CTest) enable_testing() add_subdirectory(test) endif() diff --git a/Makefile b/Makefile index f506950f0..d3963f503 100644 --- a/Makefile +++ b/Makefile @@ -84,6 +84,7 @@ doctest: # -Wno-missing-prototypes: for NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE # -Wno-padded: padding is nothing to warn about # -Wno-range-loop-analysis: items tests "for(const auto i...)" +# -Wno-extra-semi-stmt: spurious warnings for semicolons after JSON_ASSERT() # -Wno-switch-enum -Wno-covered-switch-default: pedantic/contradicting warnings about switches # -Wno-weak-vtables: exception class is defined inline, but has virtual method pedantic_clang: @@ -100,6 +101,7 @@ pedantic_clang: -Wno-missing-prototypes \ -Wno-padded \ -Wno-range-loop-analysis \ + -Wno-extra-semi-stmt \ -Wno-switch-enum -Wno-covered-switch-default \ -Wno-weak-vtables" cmake -S . -B cmake-build-pedantic -GNinja -DCMAKE_BUILD_TYPE=Debug -DJSON_MultipleHeaders=ON -DJSON_BuildTests=On cmake --build cmake-build-pedantic @@ -608,7 +610,7 @@ ChangeLog.md: release: rm -fr release_files mkdir release_files - zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) meson.build + zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) meson.build LICENSE.MIT gpg --armor --detach-sig include.zip mv include.zip include.zip.asc release_files gpg --armor --detach-sig $(AMALGAMATED_FILE) diff --git a/README.md b/README.md index d7e6df388..30efb4ac0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/3lCHrFUZANONKv7a) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](https://nlohmann.github.io/json/doxygen/index.html) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnlohmann%2Fjson.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnlohmann%2Fjson?ref=badge_shield) [![GitHub Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) [![GitHub Downloads](https://img.shields.io/github/downloads/nlohmann/json/total)](https://github.com/nlohmann/json/releases) [![GitHub Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](https://github.com/nlohmann/json/issues) @@ -76,6 +75,7 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl - [Michael Hartmann](https://github.com/reFX-Mike) - [Stefan Hagen](https://github.com/sthagen) - [Steve Sperandeo](https://github.com/homer6) +- [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe) Thanks everyone! @@ -231,6 +231,15 @@ Please file issues [here](https://github.com/build2-packaging/nlohmann-json) if If you are using [`wsjcpp`](https://wsjcpp.org), you can use the command `wsjcpp install "https://github.com/nlohmann/json:develop"` to get the latest version. Note you can change the branch ":develop" to an existing tag or another branch. +If you are using [`CPM.cmake`](https://github.com/TheLartians/CPM.cmake), you can check this [`example`](https://github.com/TheLartians/CPM.cmake/tree/master/examples/json). After [adding CPM script](https://github.com/TheLartians/CPM.cmake#adding-cpm) to your project, implement the following snippet to your CMake: + +```cmake +CPMAddPackage( + NAME nlohmann_json + GITHUB_REPOSITORY nlohmann/json + VERSION 3.9.1) +``` + ### Pkg-config If you are using bare Makefiles, you can use `pkg-config` to generate the include flags that point to where the library is installed: @@ -247,7 +256,7 @@ json = dependency('nlohmann_json', required: true) ## Examples -Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5338e282d1d02bed389d852dd670d98d.html#a5338e282d1d02bed389d852dd670d98d)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)). +Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/api/basic_json/emplace/)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)). ### JSON as first-class data type @@ -316,7 +325,7 @@ json j2 = { }; ``` -Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to be explicit or express some edge cases, the functions [`json::array()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a9ad7ec0bc1082ed09d10900fbb20a21f.html#a9ad7ec0bc1082ed09d10900fbb20a21f) and [`json::object()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aaf509a7c029100d292187068f61c99b8.html#aaf509a7c029100d292187068f61c99b8) will help: +Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to be explicit or express some edge cases, the functions [`json::array()`](https://nlohmann.github.io/json/api/basic_json/array/) and [`json::object()`](https://nlohmann.github.io/json/api/basic_json/object/) will help: ```cpp // a way to express the empty array [] @@ -351,7 +360,7 @@ auto j2 = R"( Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object. -The above example can also be expressed explicitly using [`json::parse()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a265a473e939184aa42655c9ccdf34e58.html#a265a473e939184aa42655c9ccdf34e58): +The above example can also be expressed explicitly using [`json::parse()`](https://nlohmann.github.io/json/api/basic_json/parse/): ```cpp // parse explicitly @@ -394,9 +403,9 @@ std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aa6602bb24022183ab989439e19345d08.html#aa6602bb24022183ab989439e19345d08) returns the originally stored string value. +[`.dump()`](https://nlohmann.github.io/json/api/basic_json/dump/) returns the originally stored string value. -Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a50ec80b02d0f3f51130d4abb5d1cfdc5.html#a50ec80b02d0f3f51130d4abb5d1cfdc5) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. +Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. #### To/from streams (e.g. files, string streams) @@ -884,7 +893,7 @@ Some important things: * Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined). * Those methods **MUST** be available (e.g., proper headers must be included) everywhere you use these conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise. * When using `get()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.) -* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. +* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/api/basic_json/at/) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior. * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these. #### Simplify your life with macros @@ -1222,10 +1231,6 @@ The following compilers are currently used in continuous integration at [Travis] | Compiler | Operating System | CI Provider | |-----------------------------------------------------------------|--------------------|----------------| -| Apple Clang 9.1.0 (clang-902.0.39.1); Xcode 9.3 | macOS 10.13.3 | Travis | -| Apple Clang 9.1.0 (clang-902.0.39.2); Xcode 9.4.1 | macOS 10.13.6 | Travis | -| Apple Clang 10.0.0 (clang-1000.11.45.2); Xcode 10.0 | macOS 10.13.6 | Travis | -| Apple Clang 10.0.0 (clang-1000.11.45.5); Xcode 10.1 | macOS 10.13.6 | Travis | | Apple Clang 10.0.1 (clang-1001.0.46.4); Xcode 10.2.1 | macOS 10.14.4 | Travis | | Apple Clang 11.0.0 (clang-1100.0.33.12); Xcode 11.2.1 | macOS 10.14.6 | Travis | | Apple Clang 11.0.3 (clang-1103.0.32.59); Xcode 11.4.1 | macOS 10.15.4 | GitHub Actions | @@ -1574,7 +1579,7 @@ The library supports **Unicode input** as follows: - [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library. - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors. - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs. -- When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a50ec80b02d0f3f51130d4abb5d1cfdc5.html#a50ec80b02d0f3f51130d4abb5d1cfdc5) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. +- When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/api/basic_json/dump/) may throw an exception unless `json::error_handler_t::replace` or `json::error_handler_t::ignore` are used as error handlers. ### Comments in JSON @@ -1608,7 +1613,7 @@ Here is a related issue [#1924](https://github.com/nlohmann/json/issues/1924). ### Further notes -- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a233b02b0839ef798942dd46157cc0fe6.html#a233b02b0839ef798942dd46157cc0fe6) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a73ae333487310e3302135189ce8ff5d8.html#a73ae333487310e3302135189ce8ff5d8). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. +- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/api/basic_json/operator%5B%5D/) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/api/basic_json/at/). Furthermore, you can define `JSON_ASSERT(x)` to replace calls to `assert(x)`. - As the exact type of a number is not defined in the [JSON specification](https://tools.ietf.org/html/rfc8259.html), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions. - The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag. - **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by `abort()` calls. You can further control this behavior by defining `JSON_THROW_USER` (overriding `throw`), `JSON_TRY_USER` (overriding `try`), and `JSON_CATCH_USER` (overriding `catch`). Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or aborting), as continuing after it may yield undefined behavior. diff --git a/doc/mkdocs/docs/home/license.md b/doc/mkdocs/docs/home/license.md index f7d0aa82e..4cd6ca2cc 100644 --- a/doc/mkdocs/docs/home/license.md +++ b/doc/mkdocs/docs/home/license.md @@ -1,10 +1,10 @@ # License - + -The class is licensed under the [MIT License](http://opensource.org/licenses/MIT): +The class is licensed under the [MIT License](https://opensource.org/licenses/MIT): -Copyright © 2013-2020 [Niels Lohmann](http://nlohmann.me) +Copyright © 2013-2020 [Niels Lohmann](https://nlohmann.me) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -14,8 +14,8 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I * * * -The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) +The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) -The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](http://florian.loitsch.com/) +The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/) -The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](http://creativecommons.org/publicdomain/zero/1.0/). +The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/doc/mkdocs/mkdocs.yml b/doc/mkdocs/mkdocs.yml index 6feee1248..e1552d39b 100644 --- a/doc/mkdocs/mkdocs.yml +++ b/doc/mkdocs/mkdocs.yml @@ -1,7 +1,7 @@ # Project information site_name: JSON for Modern C++ site_author: Niels Lohmann -site_url: https://squidfunk.github.io/mkdocs-material/ +site_url: https://json.nlohmann.me/ # Repository repo_name: nlohmann/json diff --git a/include/nlohmann/byte_container_with_subtype.hpp b/include/nlohmann/byte_container_with_subtype.hpp index 69f9feb21..ee3ab4011 100644 --- a/include/nlohmann/byte_container_with_subtype.hpp +++ b/include/nlohmann/byte_container_with_subtype.hpp @@ -39,15 +39,15 @@ class byte_container_with_subtype : public BinaryType : container_type(std::move(b)) {} - byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b))) + byte_container_with_subtype(const container_type& b, std::uint8_t subtype_) noexcept(noexcept(container_type(b))) : container_type(b) - , m_subtype(subtype) + , m_subtype(subtype_) , m_has_subtype(true) {} - byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + byte_container_with_subtype(container_type&& b, std::uint8_t subtype_) noexcept(noexcept(container_type(std::move(b)))) : container_type(std::move(b)) - , m_subtype(subtype) + , m_subtype(subtype_) , m_has_subtype(true) {} @@ -80,9 +80,9 @@ class byte_container_with_subtype : public BinaryType @since version 3.8.0 */ - void set_subtype(std::uint8_t subtype) noexcept + void set_subtype(std::uint8_t subtype_) noexcept { - m_subtype = subtype; + m_subtype = subtype_; m_has_subtype = true; } diff --git a/include/nlohmann/detail/hash.hpp b/include/nlohmann/detail/hash.hpp index d2d5d332d..c32d5535c 100644 --- a/include/nlohmann/detail/hash.hpp +++ b/include/nlohmann/detail/hash.hpp @@ -3,6 +3,8 @@ #include // size_t, uint8_t #include // hash +#include + namespace nlohmann { namespace detail diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 6ae5882c7..806e36030 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -11,6 +11,7 @@ #include // numeric_limits #include // char_traits, string #include // make_pair, move +#include // vector #include #include @@ -2340,7 +2341,7 @@ class binary_reader break; } result.push_back(static_cast(current)); - }; + } return success; } diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 63921ca55..a78a6ec96 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -371,15 +371,37 @@ typename iterator_input_adapter_factory::adapter_type input_adapte } // Convenience shorthand from container to iterator -template -auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container))) -{ - // Enable ADL - using std::begin; - using std::end; +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template +struct container_input_adapter_factory {}; + +template +struct container_input_adapter_factory< ContainerType, + void_t()), end(std::declval()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); + + static adapter_type create(const ContainerType& container) +{ return input_adapter(begin(container), end(container)); } + }; + +} + +template +typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); +} // Special cases with fast paths inline file_input_adapter input_adapter(std::FILE* file) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 0a9601352..eae82eaa3 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -1541,17 +1541,17 @@ scan_number_done: // literals case 't': { - std::array true_literal = {{'t', 'r', 'u', 'e'}}; + std::array true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}}; return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); } case 'f': { - std::array false_literal = {{'f', 'a', 'l', 's', 'e'}}; + std::array false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}}; return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); } case 'n': { - std::array null_literal = {{'n', 'u', 'l', 'l'}}; + std::array null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}}; return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); } diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index b4faa88a5..67134166e 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -38,8 +38,10 @@ This class implements a both iterators (iterator and const_iterator) for the template class iter_impl { + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; /// allow basic_json to access private members - friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend other_iter_impl; friend BasicJsonType; friend iteration_proxy; friend iteration_proxy_value; @@ -390,10 +392,11 @@ class iter_impl } /*! - @brief comparison: equal + @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator==(const iter_impl& other) const + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const { // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) @@ -417,16 +420,17 @@ class iter_impl } /*! - @brief comparison: not equal + @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator!=(const iter_impl& other) const + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const { return !operator==(other); } /*! - @brief comparison: smaller + @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<(const iter_impl& other) const @@ -453,7 +457,7 @@ class iter_impl } /*! - @brief comparison: less than or equal + @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<=(const iter_impl& other) const @@ -462,7 +466,7 @@ class iter_impl } /*! - @brief comparison: greater than + @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>(const iter_impl& other) const @@ -471,7 +475,7 @@ class iter_impl } /*! - @brief comparison: greater than or equal + @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>=(const iter_impl& other) const @@ -480,7 +484,7 @@ class iter_impl } /*! - @brief add to iterator + @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator+=(difference_type i) @@ -509,7 +513,7 @@ class iter_impl } /*! - @brief subtract from iterator + @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator-=(difference_type i) @@ -518,7 +522,7 @@ class iter_impl } /*! - @brief add to iterator + @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator+(difference_type i) const @@ -529,7 +533,7 @@ class iter_impl } /*! - @brief addition of distance and iterator + @brief addition of distance and iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ friend iter_impl operator+(difference_type i, const iter_impl& it) @@ -540,7 +544,7 @@ class iter_impl } /*! - @brief subtract from iterator + @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator-(difference_type i) const @@ -551,7 +555,7 @@ class iter_impl } /*! - @brief return difference + @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ difference_type operator-(const iter_impl& other) const @@ -572,7 +576,7 @@ class iter_impl } /*! - @brief access to successor + @brief access to successor @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference operator[](difference_type n) const @@ -603,7 +607,7 @@ class iter_impl } /*! - @brief return the key of an object iterator + @brief return the key of an object iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ const typename object_t::key_type& key() const @@ -619,7 +623,7 @@ class iter_impl } /*! - @brief return the value of an iterator + @brief return the value of an iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference value() const diff --git a/include/nlohmann/detail/iterators/primitive_iterator.hpp b/include/nlohmann/detail/iterators/primitive_iterator.hpp index 16dcc9f97..ae7471ef5 100644 --- a/include/nlohmann/detail/iterators/primitive_iterator.hpp +++ b/include/nlohmann/detail/iterators/primitive_iterator.hpp @@ -3,6 +3,8 @@ #include // ptrdiff_t #include // numeric_limits +#include + namespace nlohmann { namespace detail diff --git a/include/nlohmann/detail/json_ref.hpp b/include/nlohmann/detail/json_ref.hpp index c9bf6cb22..26a490382 100644 --- a/include/nlohmann/detail/json_ref.hpp +++ b/include/nlohmann/detail/json_ref.hpp @@ -17,19 +17,14 @@ class json_ref json_ref(value_type&& value) : owned_value(std::move(value)) - , value_ref(&owned_value) - , is_rvalue(true) {} json_ref(const value_type& value) - : value_ref(const_cast(&value)) - , is_rvalue(false) + : value_ref(&value) {} json_ref(std::initializer_list init) : owned_value(init) - , value_ref(&owned_value) - , is_rvalue(true) {} template < @@ -37,8 +32,6 @@ class json_ref enable_if_t::value, int> = 0 > json_ref(Args && ... args) : owned_value(std::forward(args)...) - , value_ref(&owned_value) - , is_rvalue(true) {} // class should be movable only @@ -50,27 +43,26 @@ class json_ref value_type moved_or_copied() const { - if (is_rvalue) + if (value_ref == nullptr) { - return std::move(*value_ref); + return std::move(owned_value); } return *value_ref; } value_type const& operator*() const { - return *static_cast(value_ref); + return value_ref ? *value_ref : owned_value; } value_type const* operator->() const { - return static_cast(value_ref); + return &** this; } private: mutable value_type owned_value = nullptr; - value_type* value_ref = nullptr; - const bool is_rvalue = true; + value_type const* value_ref = nullptr; }; } // namespace detail } // namespace nlohmann diff --git a/include/nlohmann/detail/meta/cpp_future.hpp b/include/nlohmann/detail/meta/cpp_future.hpp index dd929ee14..4ba1a5571 100644 --- a/include/nlohmann/detail/meta/cpp_future.hpp +++ b/include/nlohmann/detail/meta/cpp_future.hpp @@ -2,19 +2,32 @@ #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +#include namespace nlohmann { namespace detail { -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; template using uncvref_t = typename std::remove_cv::type>::type; -// implementation of C++14 index_sequence and affiliates +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + // source: https://stackoverflow.com/a/32223343 template struct index_sequence @@ -45,6 +58,8 @@ template<> struct make_index_sequence<1> : index_sequence<0> {}; template using index_sequence_for = make_index_sequence; +#endif + // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; @@ -58,5 +73,6 @@ struct static_const template constexpr T static_const::value; + } // namespace detail } // namespace nlohmann diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index ac143becf..1706cbdc6 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -3,6 +3,7 @@ #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval +#include // tuple #include #include @@ -168,7 +169,9 @@ struct is_iterator_traits> is_detected::value; }; -// source: https://stackoverflow.com/a/37193089/4116453 +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. template struct is_complete_type : std::false_type {}; @@ -186,7 +189,6 @@ struct is_compatible_object_type_impl < enable_if_t < is_detected::value&& is_detected::value >> { - using object_t = typename BasicJsonType::object_t; // macOS's is_constructible does not play well with nonesuch... diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index f59e8ad31..0a34c8011 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -881,6 +881,7 @@ class serializer } }; + JSON_ASSERT(byte < utf8d.size()); const std::uint8_t type = utf8d[byte]; codep = (state != UTF8_ACCEPT) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index cd56dafdc..8c9bef03d 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -73,6 +73,10 @@ SOFTWARE. #include #include +#if defined(JSON_HAS_CPP_17) + #include +#endif + /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -923,14 +927,14 @@ class basic_json AllocatorType alloc; using AllocatorTraits = std::allocator_traits>; - auto deleter = [&](T * object) + auto deleter = [&](T * obj) { - AllocatorTraits::deallocate(alloc, object, 1); + AllocatorTraits::deallocate(alloc, obj, 1); }; - std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); - AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); - JSON_ASSERT(object != nullptr); - return object.release(); + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); } //////////////////////// diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index 7dd644543..330677c4d 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -168,6 +168,19 @@ template , Container::push_back(value); return {--this->end(), true}; } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } }; } // namespace nlohmann diff --git a/include/nlohmann/thirdparty/hedley/hedley_undef.hpp b/include/nlohmann/thirdparty/hedley/hedley_undef.hpp index 88ee044da..2f70f13c6 100644 --- a/include/nlohmann/thirdparty/hedley/hedley_undef.hpp +++ b/include/nlohmann/thirdparty/hedley/hedley_undef.hpp @@ -1,3 +1,5 @@ +#pragma once + #undef JSON_HEDLEY_ALWAYS_INLINE #undef JSON_HEDLEY_ARM_VERSION #undef JSON_HEDLEY_ARM_VERSION_CHECK diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e821c79a3..8b6344f92 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2736,19 +2736,33 @@ class other_error : public exception #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + namespace nlohmann { namespace detail { -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; template using uncvref_t = typename std::remove_cv::type>::type; -// implementation of C++14 index_sequence and affiliates +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + // source: https://stackoverflow.com/a/32223343 template struct index_sequence @@ -2779,6 +2793,8 @@ template<> struct make_index_sequence<1> : index_sequence<0> {}; template using index_sequence_for = make_index_sequence; +#endif + // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; @@ -2792,6 +2808,7 @@ struct static_const template constexpr T static_const::value; + } // namespace detail } // namespace nlohmann @@ -2801,6 +2818,7 @@ constexpr T static_const::value; #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval +#include // tuple // #include @@ -3174,7 +3192,9 @@ struct is_iterator_traits> is_detected::value; }; -// source: https://stackoverflow.com/a/37193089/4116453 +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. template struct is_complete_type : std::false_type {}; @@ -3192,7 +3212,6 @@ struct is_compatible_object_type_impl < enable_if_t < is_detected::value&& is_detected::value >> { - using object_t = typename BasicJsonType::object_t; // macOS's is_constructible does not play well with nonesuch... @@ -4515,15 +4534,15 @@ class byte_container_with_subtype : public BinaryType : container_type(std::move(b)) {} - byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b))) + byte_container_with_subtype(const container_type& b, std::uint8_t subtype_) noexcept(noexcept(container_type(b))) : container_type(b) - , m_subtype(subtype) + , m_subtype(subtype_) , m_has_subtype(true) {} - byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + byte_container_with_subtype(container_type&& b, std::uint8_t subtype_) noexcept(noexcept(container_type(std::move(b)))) : container_type(std::move(b)) - , m_subtype(subtype) + , m_subtype(subtype_) , m_has_subtype(true) {} @@ -4556,9 +4575,9 @@ class byte_container_with_subtype : public BinaryType @since version 3.8.0 */ - void set_subtype(std::uint8_t subtype) noexcept + void set_subtype(std::uint8_t subtype_) noexcept { - m_subtype = subtype; + m_subtype = subtype_; m_has_subtype = true; } @@ -4653,6 +4672,9 @@ class byte_container_with_subtype : public BinaryType #include // size_t, uint8_t #include // hash +// #include + + namespace nlohmann { namespace detail @@ -4781,6 +4803,7 @@ std::size_t hash(const BasicJsonType& j) #include // numeric_limits #include // char_traits, string #include // make_pair, move +#include // vector // #include @@ -5160,15 +5183,37 @@ typename iterator_input_adapter_factory::adapter_type input_adapte } // Convenience shorthand from container to iterator -template -auto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container))) -{ - // Enable ADL - using std::begin; - using std::end; +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template +struct container_input_adapter_factory {}; + +template +struct container_input_adapter_factory< ContainerType, + void_t()), end(std::declval()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); + + static adapter_type create(const ContainerType& container) +{ return input_adapter(begin(container), end(container)); } + }; + +} + +template +typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); +} // Special cases with fast paths inline file_input_adapter input_adapter(std::FILE* file) @@ -7488,17 +7533,17 @@ scan_number_done: // literals case 't': { - std::array true_literal = {{'t', 'r', 'u', 'e'}}; + std::array true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}}; return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); } case 'f': { - std::array false_literal = {{'f', 'a', 'l', 's', 'e'}}; + std::array false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}}; return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); } case 'n': { - std::array null_literal = {{'n', 'u', 'l', 'l'}}; + std::array null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}}; return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); } @@ -10047,7 +10092,7 @@ class binary_reader break; } result.push_back(static_cast(current)); - }; + } return success; } @@ -10689,6 +10734,9 @@ class parser #include // ptrdiff_t #include // numeric_limits +// #include + + namespace nlohmann { namespace detail @@ -10877,8 +10925,10 @@ This class implements a both iterators (iterator and const_iterator) for the template class iter_impl { + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; /// allow basic_json to access private members - friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend other_iter_impl; friend BasicJsonType; friend iteration_proxy; friend iteration_proxy_value; @@ -11229,10 +11279,11 @@ class iter_impl } /*! - @brief comparison: equal + @brief comparison: equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator==(const iter_impl& other) const + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const { // if objects are not the same, the comparison is undefined if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) @@ -11256,16 +11307,17 @@ class iter_impl } /*! - @brief comparison: not equal + @brief comparison: not equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ - bool operator!=(const iter_impl& other) const + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const { return !operator==(other); } /*! - @brief comparison: smaller + @brief comparison: smaller @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<(const iter_impl& other) const @@ -11292,7 +11344,7 @@ class iter_impl } /*! - @brief comparison: less than or equal + @brief comparison: less than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator<=(const iter_impl& other) const @@ -11301,7 +11353,7 @@ class iter_impl } /*! - @brief comparison: greater than + @brief comparison: greater than @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>(const iter_impl& other) const @@ -11310,7 +11362,7 @@ class iter_impl } /*! - @brief comparison: greater than or equal + @brief comparison: greater than or equal @pre The iterator is initialized; i.e. `m_object != nullptr`. */ bool operator>=(const iter_impl& other) const @@ -11319,7 +11371,7 @@ class iter_impl } /*! - @brief add to iterator + @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator+=(difference_type i) @@ -11348,7 +11400,7 @@ class iter_impl } /*! - @brief subtract from iterator + @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl& operator-=(difference_type i) @@ -11357,7 +11409,7 @@ class iter_impl } /*! - @brief add to iterator + @brief add to iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator+(difference_type i) const @@ -11368,7 +11420,7 @@ class iter_impl } /*! - @brief addition of distance and iterator + @brief addition of distance and iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ friend iter_impl operator+(difference_type i, const iter_impl& it) @@ -11379,7 +11431,7 @@ class iter_impl } /*! - @brief subtract from iterator + @brief subtract from iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ iter_impl operator-(difference_type i) const @@ -11390,7 +11442,7 @@ class iter_impl } /*! - @brief return difference + @brief return difference @pre The iterator is initialized; i.e. `m_object != nullptr`. */ difference_type operator-(const iter_impl& other) const @@ -11411,7 +11463,7 @@ class iter_impl } /*! - @brief access to successor + @brief access to successor @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference operator[](difference_type n) const @@ -11442,7 +11494,7 @@ class iter_impl } /*! - @brief return the key of an object iterator + @brief return the key of an object iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ const typename object_t::key_type& key() const @@ -11458,7 +11510,7 @@ class iter_impl } /*! - @brief return the value of an iterator + @brief return the value of an iterator @pre The iterator is initialized; i.e. `m_object != nullptr`. */ reference value() const @@ -12605,19 +12657,14 @@ class json_ref json_ref(value_type&& value) : owned_value(std::move(value)) - , value_ref(&owned_value) - , is_rvalue(true) {} json_ref(const value_type& value) - : value_ref(const_cast(&value)) - , is_rvalue(false) + : value_ref(&value) {} json_ref(std::initializer_list init) : owned_value(init) - , value_ref(&owned_value) - , is_rvalue(true) {} template < @@ -12625,8 +12672,6 @@ class json_ref enable_if_t::value, int> = 0 > json_ref(Args && ... args) : owned_value(std::forward(args)...) - , value_ref(&owned_value) - , is_rvalue(true) {} // class should be movable only @@ -12638,27 +12683,26 @@ class json_ref value_type moved_or_copied() const { - if (is_rvalue) + if (value_ref == nullptr) { - return std::move(*value_ref); + return std::move(owned_value); } return *value_ref; } value_type const& operator*() const { - return *static_cast(value_ref); + return value_ref ? *value_ref : owned_value; } value_type const* operator->() const { - return static_cast(value_ref); + return &** this; } private: mutable value_type owned_value = nullptr; - value_type* value_ref = nullptr; - const bool is_rvalue = true; + value_type const* value_ref = nullptr; }; } // namespace detail } // namespace nlohmann @@ -16392,6 +16436,7 @@ class serializer } }; + JSON_ASSERT(byte < utf8d.size()); const std::uint8_t type = utf8d[byte]; codep = (state != UTF8_ACCEPT) @@ -16634,11 +16679,28 @@ template , Container::push_back(value); return {--this->end(), true}; } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } }; } // namespace nlohmann +#if defined(JSON_HAS_CPP_17) + #include +#endif + /*! @brief namespace for Niels Lohmann @see https://github.com/nlohmann @@ -17489,14 +17551,14 @@ class basic_json AllocatorType alloc; using AllocatorTraits = std::allocator_traits>; - auto deleter = [&](T * object) + auto deleter = [&](T * obj) { - AllocatorTraits::deallocate(alloc, object, 1); + AllocatorTraits::deallocate(alloc, obj, 1); }; - std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); - AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); - JSON_ASSERT(object != nullptr); - return object.release(); + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); } //////////////////////// @@ -25382,6 +25444,8 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std #undef JSON_EXPLICIT // #include + + #undef JSON_HEDLEY_ALWAYS_INLINE #undef JSON_HEDLEY_ARM_VERSION #undef JSON_HEDLEY_ARM_VERSION_CHECK diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 6b2e76dad..3be72c7d4 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -867,8 +867,9 @@ TEST_CASE("Negative size of binary value") 0x00 // end marker }; - CHECK_THROWS_AS(json::from_bson(input), json::parse_error); - CHECK_THROWS_WITH(json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1"); + json _; + CHECK_THROWS_AS(_ = json::from_bson(input), json::parse_error); + CHECK_THROWS_WITH(_ = json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1"); } TEST_CASE("Unsupported BSON input") diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index ca4b781a1..9ed80c8f1 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1591,14 +1591,16 @@ TEST_CASE("CBOR") { // array with three empty byte strings std::vector input = {0x83, 0x40, 0x40, 0x40}; - CHECK_NOTHROW(json::from_cbor(input)); + json _; + CHECK_NOTHROW(_ = json::from_cbor(input)); } SECTION("binary in object") { // object mapping "foo" to empty byte string std::vector input = {0xA1, 0x63, 0x66, 0x6F, 0x6F, 0x40}; - CHECK_NOTHROW(json::from_cbor(input)); + json _; + CHECK_NOTHROW(_ = json::from_cbor(input)); } SECTION("SAX callback with binary") @@ -2551,8 +2553,9 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), b); // check that parsing fails in error mode - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); // check that parsing succeeds and gets original value in ignore mode auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore); @@ -2570,8 +2573,9 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xD8); // tag // check that parsing fails in error mode - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); // check that parsing succeeds and gets original value in ignore mode auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore); @@ -2585,9 +2589,10 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xD8); // tag // check that parsing fails in all modes - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); } } @@ -2602,8 +2607,9 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xD9); // tag // check that parsing fails in error mode - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); // check that parsing succeeds and gets original value in ignore mode auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore); @@ -2618,9 +2624,10 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xD9); // tag // check that parsing fails in all modes - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); } } @@ -2637,8 +2644,9 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xDA); // tag // check that parsing fails in error mode - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); // check that parsing succeeds and gets original value in ignore mode auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore); @@ -2655,9 +2663,10 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xDA); // tag // check that parsing fails in all modes - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); } } @@ -2678,8 +2687,9 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xDB); // tag // check that parsing fails in error mode - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); // check that parsing succeeds and gets original value in ignore mode auto j_tagged = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore); @@ -2700,9 +2710,10 @@ TEST_CASE("Tagged values") v_tagged.insert(v_tagged.begin(), 0xDB); // tag // check that parsing fails in all modes - CHECK_THROWS_AS(json::from_cbor(v_tagged), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); - CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::error), json::parse_error); + CHECK_THROWS_AS(_ = json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); } } @@ -2717,8 +2728,9 @@ TEST_CASE("Tagged values") CHECK(vec == std::vector {0xA1, 0x66, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, 0xD8, 0x2A, 0x44, 0xCA, 0xFE, 0xBA, 0xBE}); // parse error when parsing tagged value - CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error); - CHECK_THROWS_WITH(json::from_cbor(vec), "[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: invalid byte: 0xD8"); + json _; + CHECK_THROWS_AS(_ = json::from_cbor(vec), json::parse_error); + CHECK_THROWS_WITH(_ = json::from_cbor(vec), "[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: invalid byte: 0xD8"); // binary without subtype when tags are ignored json jb = json::from_cbor(vec, true, true, json::cbor_tag_handler_t::ignore); diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 1a4f8ed75..07d243a81 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -135,7 +135,7 @@ TEST_CASE("lexer class") // store scan() result const auto res = scan_string(s.c_str()); - CAPTURE(s); + CAPTURE(s) switch (c) { diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 0cffee02a..2df07d6d4 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -509,7 +509,7 @@ TEST_CASE("parser class") CHECK(parser_helper("\"€\"").get() == "€"); CHECK(parser_helper("\"🎈\"").get() == "🎈"); - CHECK(parser_helper("\"\\ud80c\\udc60\"").get() == u8"\U00013060"); + CHECK(parser_helper("\"\\ud80c\\udc60\"").get() == "\xf0\x93\x81\xa0"); CHECK(parser_helper("\"\\ud83c\\udf1e\"").get() == "🌞"); } } @@ -1879,7 +1879,8 @@ TEST_CASE("parser class") SECTION("error messages for comments") { - CHECK_THROWS_WITH_AS(json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error); - CHECK_THROWS_WITH_AS(json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*'", json::parse_error); + json _; + CHECK_THROWS_WITH_AS(_ = json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error); + CHECK_THROWS_WITH_AS(_ = json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*'", json::parse_error); } } diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index c71e230d9..7f59c63ec 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -48,10 +48,6 @@ using nlohmann::json; #define JSON_HAS_CPP_14 #endif -#if defined(JSON_HAS_CPP_17) - #include -#endif - TEST_CASE("value conversion") { SECTION("get an object (explicit)") @@ -1706,3 +1702,11 @@ TEST_CASE("JSON to enum mapping") CHECK(TS_INVALID == json("what?").get()); } } + +#ifdef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_17 +#endif + +#ifdef JSON_HAS_CPP_14 + #undef JSON_HAS_CPP_14 +#endif diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 716564c0a..d2db7e80c 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -537,7 +537,8 @@ TEST_CASE("deserialization") SECTION("with empty range") { std::vector v; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); SaxEventLogger l; @@ -553,7 +554,8 @@ TEST_CASE("deserialization") SECTION("case 1") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u'}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -569,7 +571,8 @@ TEST_CASE("deserialization") SECTION("case 2") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1'}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -585,7 +588,8 @@ TEST_CASE("deserialization") SECTION("case 3") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', '\\', 'u', '1', '1', '1', '1', '1', '1', '1', '1'}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -601,7 +605,8 @@ TEST_CASE("deserialization") SECTION("case 4") { uint8_t v[] = {'\"', 'a', 'a', 'a', 'a', 'a', 'a', 'u', '1', '1', '1', '1', '1', '1', '1', '1', '\\'}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -617,7 +622,8 @@ TEST_CASE("deserialization") SECTION("case 5") { uint8_t v[] = {'\"', 0x7F, 0xC1}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -652,7 +658,8 @@ TEST_CASE("deserialization") SECTION("case 7") { uint8_t v[] = {'\"', 0x7F, 0xDF, 0xC0}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -668,7 +675,8 @@ TEST_CASE("deserialization") SECTION("case 8") { uint8_t v[] = {'\"', 0x7F, 0xE0, 0x9F}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -684,7 +692,8 @@ TEST_CASE("deserialization") SECTION("case 9") { uint8_t v[] = {'\"', 0x7F, 0xEF, 0xC0}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -700,7 +709,8 @@ TEST_CASE("deserialization") SECTION("case 10") { uint8_t v[] = {'\"', 0x7F, 0xED, 0x7F}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -716,7 +726,8 @@ TEST_CASE("deserialization") SECTION("case 11") { uint8_t v[] = {'\"', 0x7F, 0xF0, 0x8F}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -732,7 +743,8 @@ TEST_CASE("deserialization") SECTION("case 12") { uint8_t v[] = {'\"', 0x7F, 0xF0, 0xC0}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -748,7 +760,8 @@ TEST_CASE("deserialization") SECTION("case 13") { uint8_t v[] = {'\"', 0x7F, 0xF3, 0x7F}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -764,7 +777,8 @@ TEST_CASE("deserialization") SECTION("case 14") { uint8_t v[] = {'\"', 0x7F, 0xF3, 0xC0}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -780,7 +794,8 @@ TEST_CASE("deserialization") SECTION("case 15") { uint8_t v[] = {'\"', 0x7F, 0xF4, 0x7F}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; @@ -796,7 +811,8 @@ TEST_CASE("deserialization") SECTION("case 16") { uint8_t v[] = {'{', '\"', '\"', ':', '1', '1'}; - CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&); + json _; + CHECK_THROWS_AS(_ = json::parse(std::begin(v), std::end(v)), json::parse_error&); CHECK(!json::accept(std::begin(v), std::end(v))); json j_error; diff --git a/test/src/unit-items.cpp b/test/src/unit-items.cpp index 10621ce7e..4caf76f23 100644 --- a/test/src/unit-items.cpp +++ b/test/src/unit-items.cpp @@ -1448,3 +1448,11 @@ TEST_CASE("items()") } } } + +#ifdef JSON_HAS_CPP_17 + #undef JSON_HAS_CPP_17 +#endif + +#ifdef JSON_HAS_CPP_14 + #undef JSON_HAS_CPP_14 +#endif diff --git a/test/src/unit-ordered_json.cpp b/test/src/unit-ordered_json.cpp index 9b242c825..9bd1187e4 100644 --- a/test/src/unit-ordered_json.cpp +++ b/test/src/unit-ordered_json.cpp @@ -76,4 +76,18 @@ TEST_CASE("ordered_json") CHECK(multi_ordered.dump() == "{\"z\":1,\"m\":2,\"y\":4}"); CHECK(multi_ordered.erase("m") == 1); CHECK(multi_ordered.dump() == "{\"z\":1,\"y\":4}"); + + // Ranged insert test. + // It seems that values shouldn't be overwritten. Only new values are added + json j1 {{"c", 1}, {"b", 2}, {"a", 3}}; + const json j2 {{"c", 77}, {"d", 42}, {"a", 4}}; + j1.insert( j2.cbegin(), j2.cend() ); + CHECK(j1.size() == 4); + CHECK(j1.dump() == "{\"a\":3,\"b\":2,\"c\":1,\"d\":42}"); + + ordered_json oj1 {{"c", 1}, {"b", 2}, {"a", 3}}; + const ordered_json oj2 {{"c", 77}, {"d", 42}, {"a", 4}}; + oj1.insert( oj2.cbegin(), oj2.cend() ); + CHECK(oj1.size() == 4); + CHECK(oj1.dump() == "{\"c\":1,\"b\":2,\"a\":3,\"d\":42}"); } diff --git a/test/src/unit-regression1.cpp b/test/src/unit-regression1.cpp index 9dcc75b09..df660ddb4 100644 --- a/test/src/unit-regression1.cpp +++ b/test/src/unit-regression1.cpp @@ -400,7 +400,7 @@ TEST_CASE("regression tests 1") SECTION("issue #146 - character following a surrogate pair is skipped") { - CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); + CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == "\xf0\x93\x81\xa0\x61\x62\x63"); } SECTION("issue #171 - Cannot index by key of type static constexpr const char*") diff --git a/test/src/unit-regression2.cpp b/test/src/unit-regression2.cpp index ca50cdd41..1e8c4922a 100644 --- a/test/src/unit-regression2.cpp +++ b/test/src/unit-regression2.cpp @@ -51,6 +51,10 @@ using nlohmann::json; #include #endif +#ifdef JSON_HAS_CPP_20 + #include +#endif + ///////////////////////////////////////////////////////////////////// // for #1021 ///////////////////////////////////////////////////////////////////// @@ -484,4 +488,14 @@ TEST_CASE("regression tests 2") json j = json::parse(ss, nullptr, true, true); CHECK(j.dump() == "{}"); } + +#ifdef JSON_HAS_CPP_20 + SECTION("issue #2546 - parsing containers of std::byte") + { + const char DATA[] = R"("Hello, world!")"; + const auto s = std::as_bytes(std::span(DATA)); + json j = json::parse(s); + CHECK(j.dump() == "\"Hello, world!\""); + } +#endif } diff --git a/test/src/unit-ubjson.cpp b/test/src/unit-ubjson.cpp index 42954479e..f60477a82 100644 --- a/test/src/unit-ubjson.cpp +++ b/test/src/unit-ubjson.cpp @@ -805,12 +805,13 @@ TEST_CASE("UBJSON") std::vector vec1 = {'H', 'i', '1'}; CHECK(json::from_ubjson(vec1, true, false).is_discarded()); + json _; std::vector vec2 = {'H', 'i', 2, '1', 'A', '3'}; - CHECK_THROWS_WITH_AS(json::from_ubjson(vec2), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A", json::parse_error); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec2), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A", json::parse_error); std::vector vec3 = {'H', 'i', 2, '1', '.'}; - CHECK_THROWS_WITH_AS(json::from_ubjson(vec3), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1.", json::parse_error); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec3), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1.", json::parse_error); std::vector vec4 = {'H', 2, '1', '0'}; - CHECK_THROWS_WITH_AS(json::from_ubjson(vec4), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x02", json::parse_error); + CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec4), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON size: expected length type specification (U, i, I, l, L) after '#'; last byte: 0x02", json::parse_error); } SECTION("serialization") diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index 7f74ac5f8..b23765505 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -112,13 +112,13 @@ static void to_json(BasicJsonType& j, country c) switch (c) { case country::china: - j = u8"中华人民共和国"; + j = "中华人民共和国"; return; case country::france: j = "France"; return; case country::russia: - j = u8"Российская Федерация"; + j = "Российская Федерация"; return; default: break; @@ -201,9 +201,9 @@ static void from_json(const BasicJsonType& j, country& c) const auto str = j.template get(); static const std::map m = { - {u8"中华人民共和国", country::china}, + {"中华人民共和国", country::china}, {"France", country::france}, - {u8"Российская Федерация", country::russia} + {"Российская Федерация", country::russia} }; const auto it = m.find(str); @@ -248,7 +248,7 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) const udt::name n{"theo"}; const udt::country c{udt::country::france}; const udt::person sfinae_addict{a, n, c}; - const udt::person senior_programmer{{42}, {u8"王芳"}, udt::country::china}; + const udt::person senior_programmer{{42}, {"王芳"}, udt::country::china}; const udt::address addr{"Paris"}; const udt::contact cpp_programmer{sfinae_addict, addr}; const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}}; @@ -265,14 +265,14 @@ TEST_CASE("basic usage" * doctest::test_suite("udt")) CHECK( json(book) == - u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); + R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json); } SECTION("conversion from json via free-functions") { const auto big_json = - u8R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json; + R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json; SECTION("via explicit calls to get") { const auto parsed_book = big_json.get(); diff --git a/test/src/unit-udt_macro.cpp b/test/src/unit-udt_macro.cpp index b56a5d15e..a13ac006b 100644 --- a/test/src/unit-udt_macro.cpp +++ b/test/src/unit-udt_macro.cpp @@ -106,7 +106,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, met class person_with_private_alphabet { public: - bool operator==(const person_with_private_alphabet& other) + bool operator==(const person_with_private_alphabet& other) const { return a == other.a && b == other.b && @@ -169,7 +169,7 @@ class person_with_private_alphabet class person_with_public_alphabet { public: - bool operator==(const person_with_public_alphabet& other) + bool operator==(const person_with_public_alphabet& other) const { return a == other.a && b == other.b && diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp index 63a9d5010..acaca2888 100644 --- a/test/src/unit-unicode.cpp +++ b/test/src/unit-unicode.cpp @@ -1201,7 +1201,8 @@ TEST_CASE("Unicode" * doctest::skip()) SECTION("with an iterator") { std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}"; - CHECK_NOTHROW(json::parse(i.begin(), i.end())); + json _; + CHECK_NOTHROW(_ = json::parse(i.begin(), i.end())); } } diff --git a/test/src/unit-user_defined_input.cpp b/test/src/unit-user_defined_input.cpp index 5a1138b00..4b84e8e71 100644 --- a/test/src/unit-user_defined_input.cpp +++ b/test/src/unit-user_defined_input.cpp @@ -63,7 +63,7 @@ const char* end(const MyContainer& c) return c.data + strlen(c.data); } -TEST_CASE("Custom container") +TEST_CASE("Custom container non-member begin/end") { MyContainer data{"[1,2,3,4]"}; @@ -75,6 +75,31 @@ TEST_CASE("Custom container") } +TEST_CASE("Custom container member begin/end") +{ + struct MyContainer2 + { + const char* data; + + const char* begin() const + { + return data; + } + + const char* end() const + { + return data + strlen(data); + } + }; + + MyContainer2 data{"[1,2,3,4]"}; + json as_json = json::parse(data); + CHECK(as_json.at(0) == 1); + CHECK(as_json.at(1) == 2); + CHECK(as_json.at(2) == 3); + CHECK(as_json.at(3) == 4); +} + TEST_CASE("Custom iterator") { const char* raw_data = "[1,2,3,4]"; diff --git a/test/thirdparty/doctest/LICENSE.txt b/test/thirdparty/doctest/LICENSE.txt index a20472146..155dbe942 100755 --- a/test/thirdparty/doctest/LICENSE.txt +++ b/test/thirdparty/doctest/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016-2019 Viktor Kirilov +Copyright (c) 2016-2020 Viktor Kirilov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/test/thirdparty/doctest/doctest.h b/test/thirdparty/doctest/doctest.h old mode 100755 new mode 100644 index 2f0ff2131..ae9c4d410 --- a/test/thirdparty/doctest/doctest.h +++ b/test/thirdparty/doctest/doctest.h @@ -4,7 +4,7 @@ // // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD // -// Copyright (c) 2016-2019 Viktor Kirilov +// Copyright (c) 2016-2020 Viktor Kirilov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at @@ -47,9 +47,9 @@ // ================================================================================================= #define DOCTEST_VERSION_MAJOR 2 -#define DOCTEST_VERSION_MINOR 3 -#define DOCTEST_VERSION_PATCH 7 -#define DOCTEST_VERSION_STR "2.3.7" +#define DOCTEST_VERSION_MINOR 4 +#define DOCTEST_VERSION_PATCH 3 +#define DOCTEST_VERSION_STR "2.4.3" #define DOCTEST_VERSION \ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) @@ -301,11 +301,23 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #define DOCTEST_NOINLINE __declspec(noinline) #define DOCTEST_UNUSED #define DOCTEST_ALIGNMENT(x) -#else // MSVC +#elif DOCTEST_CLANG && DOCTEST_CLANG < DOCTEST_COMPILER(3, 5, 0) +#define DOCTEST_NOINLINE +#define DOCTEST_UNUSED +#define DOCTEST_ALIGNMENT(x) +#else #define DOCTEST_NOINLINE __attribute__((noinline)) #define DOCTEST_UNUSED __attribute__((unused)) #define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) -#endif // MSVC +#endif + +#ifndef DOCTEST_NORETURN +#define DOCTEST_NORETURN [[noreturn]] +#endif // DOCTEST_NORETURN + +#ifndef DOCTEST_NOEXCEPT +#define DOCTEST_NOEXCEPT noexcept +#endif // DOCTEST_NOEXCEPT // ================================================================================================= // == FEATURE DETECTION END ======================================================================== @@ -347,8 +359,20 @@ DOCTEST_MSVC_SUPPRESS_WARNING(26812) // Prefer 'enum class' over 'enum' #ifndef DOCTEST_BREAK_INTO_DEBUGGER // should probably take a look at https://github.com/scottt/debugbreak -#ifdef DOCTEST_PLATFORM_MAC +#ifdef DOCTEST_PLATFORM_LINUX +#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) +// Break at the location of the failing check if possible #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) +#else +#include +#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP) +#endif +#elif defined(DOCTEST_PLATFORM_MAC) +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) +#else +#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); +#endif #elif DOCTEST_MSVC #define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() #elif defined(__MINGW32__) @@ -357,7 +381,7 @@ extern "C" __declspec(dllimport) void __stdcall DebugBreak(); DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() #else // linux -#define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0) +#define DOCTEST_BREAK_INTO_DEBUGGER() (static_cast(0)) #endif // linux #endif // DOCTEST_BREAK_INTO_DEBUGGER @@ -367,6 +391,9 @@ DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_USE_IOSFWD #ifdef DOCTEST_CONFIG_USE_STD_HEADERS +#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS +#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #include #include #include @@ -629,7 +656,7 @@ DOCTEST_INTERFACE const char* skipPathFromFilename(const char* file); struct DOCTEST_INTERFACE TestCaseData { - const char* m_file; // the file in which the test was registered + String m_file; // the file in which the test was registered unsigned m_line; // the line where the test was registered const char* m_name; // name of the test case const char* m_test_suite; // the test suite in which the test was added @@ -720,7 +747,9 @@ struct ContextOptions //!OCLINT too many fields bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): bool no_path_in_filenames; // if the path to files should be removed from the output bool no_line_numbers; // if source code line numbers should be omitted from the output + bool no_debug_output; // no output in the debug console when a debugger is attached bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! + bool no_time_in_output; // omit any time/timestamps from output !!! UNDOCUMENTED !!! bool help; // to print the help bool version; // to print the version @@ -731,7 +760,6 @@ struct ContextOptions //!OCLINT too many fields }; namespace detail { -#if defined(DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || defined(DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS) template struct enable_if {}; @@ -739,7 +767,6 @@ namespace detail { template struct enable_if { typedef TYPE type; }; -#endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING) || DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format off template struct remove_reference { typedef T type; }; @@ -748,6 +775,14 @@ namespace detail { template struct remove_const { typedef T type; }; template struct remove_const { typedef T type; }; +#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS + template struct is_enum : public std::is_enum {}; + template struct underlying_type : public std::underlying_type {}; +#else + // Use compiler intrinsics + template struct is_enum { constexpr static bool value = __is_enum(T); }; + template struct underlying_type { typedef __underlying_type(T) type; }; +#endif // clang-format on template @@ -756,33 +791,23 @@ namespace detail { { static const bool value = false; }; namespace has_insertion_operator_impl { - typedef char no; - typedef char yes[2]; + std::ostream &os(); + template + DOCTEST_REF_WRAP(T) val(); - struct any_t - { - template - // cppcheck-suppress noExplicitConstructor - any_t(const DOCTEST_REF_WRAP(T)); + template + struct check { + static constexpr bool value = false; }; - yes& testStreamable(std::ostream&); - no testStreamable(no); - - no operator<<(const std::ostream&, const any_t&); - - template - struct has_insertion_operator - { - static std::ostream& s; - static const DOCTEST_REF_WRAP(T) t; - static const bool value = sizeof(decltype(testStreamable(s << t))) == sizeof(yes); + template + struct check(), void())> { + static constexpr bool value = true; }; } // namespace has_insertion_operator_impl - template - struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator - {}; + template + using has_insertion_operator = has_insertion_operator_impl::check; DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num); @@ -846,7 +871,7 @@ struct StringMaker } }; -template +template ::value, bool>::type = true> String toString(const DOCTEST_REF_WRAP(T) value) { return StringMaker::convert(value); } @@ -873,6 +898,12 @@ DOCTEST_INTERFACE String toString(int long long in); DOCTEST_INTERFACE String toString(int long long unsigned in); DOCTEST_INTERFACE String toString(std::nullptr_t in); +template ::value, bool>::type = true> +String toString(const DOCTEST_REF_WRAP(T) value) { + typedef typename detail::underlying_type::type UT; + return toString(static_cast(value)); +} + #if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0) // see this issue on why this is needed: https://github.com/onqtam/doctest/issues/183 DOCTEST_INTERFACE String toString(const std::string& in); @@ -987,7 +1018,7 @@ namespace detail { DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - [[noreturn]] + DOCTEST_NORETURN #endif // DOCTEST_CONFIG_NO_EXCEPTIONS DOCTEST_INTERFACE void throwException(); @@ -1005,6 +1036,7 @@ namespace detail { template String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, const DOCTEST_REF_WRAP(R) rhs) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return toString(lhs) + op + toString(rhs); } @@ -1092,6 +1124,7 @@ namespace detail { #define DOCTEST_COMPARISON_RETURN_TYPE bool #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if::value || can_use_op::value, bool>::type + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } @@ -1284,12 +1317,12 @@ namespace detail { template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return op(lhs, rhs); } }; // clang-format on - DOCTEST_BINARY_RELATIONAL_OP(0, eq) - DOCTEST_BINARY_RELATIONAL_OP(1, ne) - DOCTEST_BINARY_RELATIONAL_OP(2, gt) - DOCTEST_BINARY_RELATIONAL_OP(3, lt) - DOCTEST_BINARY_RELATIONAL_OP(4, ge) - DOCTEST_BINARY_RELATIONAL_OP(5, le) + DOCTEST_BINARY_RELATIONAL_OP(0, doctest::detail::eq) + DOCTEST_BINARY_RELATIONAL_OP(1, doctest::detail::ne) + DOCTEST_BINARY_RELATIONAL_OP(2, doctest::detail::gt) + DOCTEST_BINARY_RELATIONAL_OP(3, doctest::detail::lt) + DOCTEST_BINARY_RELATIONAL_OP(4, doctest::detail::ge) + DOCTEST_BINARY_RELATIONAL_OP(5, doctest::detail::le) struct DOCTEST_INTERFACE ResultBuilder : public AssertData { @@ -1416,9 +1449,9 @@ namespace detail { } catch(T ex) { // NOLINT res = m_translateFunction(ex); //!OCLINT parameter reassignment return true; - } catch(...) {} //!OCLINT - empty catch statement -#endif // DOCTEST_CONFIG_NO_EXCEPTIONS - ((void)res); // to silence -Wunused-parameter + } catch(...) {} //!OCLINT - empty catch statement +#endif // DOCTEST_CONFIG_NO_EXCEPTIONS + static_cast(res); // to silence -Wunused-parameter return false; } @@ -1511,12 +1544,24 @@ namespace detail { MessageBuilder() = delete; ~MessageBuilder(); + // the preferred way of chaining parameters for stringification template - MessageBuilder& operator<<(const T& in) { + MessageBuilder& operator,(const T& in) { toStream(m_stream, in); return *this; } + // kept here just for backwards-compatibility - the comma operator should be preferred now + template + MessageBuilder& operator<<(const T& in) { return this->operator,(in); } + + // the `,` operator has the lowest operator precedence - if `<<` is used by the user then + // the `,` operator will be called last which is not what we want and thus the `*` operator + // is used first (has higher operator precedence compared to `<<`) so that we guarantee that + // an operator of the MessageBuilder class is called first before the rest of the parameters + template + MessageBuilder& operator*(const T& in) { return this->operator,(in); } + bool log(); void react(); }; @@ -1737,12 +1782,12 @@ int registerReporter(const char* name, int priority, bool isReporter) { #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #ifdef DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS -#define DOCTEST_CAST_TO_VOID(x) \ +#define DOCTEST_CAST_TO_VOID(...) \ DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wuseless-cast") \ - static_cast(x); \ + static_cast(__VA_ARGS__); \ DOCTEST_GCC_SUPPRESS_WARNING_POP #else // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS -#define DOCTEST_CAST_TO_VOID(x) x; +#define DOCTEST_CAST_TO_VOID(...) __VA_ARGS__; #endif // DOCTEST_CONFIG_VOID_CAST_EXPRESSIONS // registers the test by initializing a dummy var with a function @@ -1932,38 +1977,38 @@ int registerReporter(const char* name, int priority, bool isReporter) { DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for logging -#define DOCTEST_INFO(expression) \ +#define DOCTEST_INFO(...) \ DOCTEST_INFO_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), \ - DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), expression) + DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_), __VA_ARGS__) -#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, expression) \ +#define DOCTEST_INFO_IMPL(lambda_name, mb_name, s_name, ...) \ DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4626) \ auto lambda_name = [&](std::ostream* s_name) { \ doctest::detail::MessageBuilder mb_name(__FILE__, __LINE__, doctest::assertType::is_warn); \ mb_name.m_stream = s_name; \ - mb_name << expression; \ + mb_name * __VA_ARGS__; \ }; \ DOCTEST_MSVC_SUPPRESS_WARNING_POP \ auto DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_) = doctest::detail::MakeContextScope(lambda_name) -#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x) +#define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := ", x) -#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \ +#define DOCTEST_ADD_AT_IMPL(type, file, line, mb, ...) \ do { \ doctest::detail::MessageBuilder mb(file, line, doctest::assertType::type); \ - mb << x; \ + mb * __VA_ARGS__; \ DOCTEST_ASSERT_LOG_AND_REACT(mb); \ - } while((void)0, 0) + } while(false) // clang-format off -#define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) -#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) -#define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) +#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__) +#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__) +#define DOCTEST_ADD_FAIL_AT(file, line, ...) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), __VA_ARGS__) // clang-format on -#define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x) -#define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x) -#define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x) +#define DOCTEST_MESSAGE(...) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, __VA_ARGS__) +#define DOCTEST_FAIL_CHECK(...) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, __VA_ARGS__) +#define DOCTEST_FAIL(...) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, __VA_ARGS__) #define DOCTEST_TO_LVALUE(...) __VA_ARGS__ // Not removed to keep backwards compatibility. @@ -1982,7 +2027,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_ASSERT_IMPLEMENT_1(assert_type, ...) \ do { \ DOCTEST_ASSERT_IMPLEMENT_2(assert_type, __VA_ARGS__); \ - } while((void)0, 0) + } while(false) #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS @@ -2006,12 +2051,12 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1(DT_REQUIRE_FALSE, __VA_ARGS__) // clang-format off -#define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while((void)0, 0) -#define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while((void)0, 0) -#define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while((void)0, 0) -#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while((void)0, 0) -#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while((void)0, 0) -#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while((void)0, 0) +#define DOCTEST_WARN_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN, cond); } while(false) +#define DOCTEST_CHECK_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK, cond); } while(false) +#define DOCTEST_REQUIRE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE, cond); } while(false) +#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_WARN_FALSE, cond); } while(false) +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_CHECK_FALSE, cond); } while(false) +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_ASSERT_IMPLEMENT_2(DT_REQUIRE_FALSE, cond); } while(false) // clang-format on #define DOCTEST_ASSERT_THROWS_AS(expr, assert_type, message, ...) \ @@ -2021,73 +2066,73 @@ int registerReporter(const char* name, int priority, bool isReporter) { __LINE__, #expr, #__VA_ARGS__, message); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ - } catch(const doctest::detail::remove_const< \ - doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ + } catch(const typename doctest::detail::remove_const< \ + typename doctest::detail::remove_reference<__VA_ARGS__>::type>::type&) { \ _DOCTEST_RB.translateException(); \ _DOCTEST_RB.m_threw_as = true; \ } catch(...) { _DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ - } while((void)0, 0) + } while(false) -#define DOCTEST_ASSERT_THROWS_WITH(expr, assert_type, ...) \ +#define DOCTEST_ASSERT_THROWS_WITH(expr, expr_str, assert_type, ...) \ do { \ if(!doctest::getContextOptions()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, #expr, "", __VA_ARGS__); \ + __LINE__, expr_str, "", __VA_ARGS__); \ try { \ DOCTEST_CAST_TO_VOID(expr) \ } catch(...) { _DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ - } while((void)0, 0) + } while(false) -#define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \ +#define DOCTEST_ASSERT_NOTHROW(assert_type, ...) \ do { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::assertType::assert_type, __FILE__, \ - __LINE__, #expr); \ + __LINE__, #__VA_ARGS__); \ try { \ - DOCTEST_CAST_TO_VOID(expr) \ + DOCTEST_CAST_TO_VOID(__VA_ARGS__) \ } catch(...) { _DOCTEST_RB.translateException(); } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } while((void)0, 0) + } while(false) // clang-format off -#define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS, "") -#define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS, "") -#define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS, "") +#define DOCTEST_WARN_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_WARN_THROWS, "") +#define DOCTEST_CHECK_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_CHECK_THROWS, "") +#define DOCTEST_REQUIRE_THROWS(...) DOCTEST_ASSERT_THROWS_WITH((__VA_ARGS__), #__VA_ARGS__, DT_REQUIRE_THROWS, "") #define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_AS, "", __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_AS, "", __VA_ARGS__) -#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_WARN_THROWS_WITH, __VA_ARGS__) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) +#define DOCTEST_WARN_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_WARN_THROWS_WITH, __VA_ARGS__) +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_CHECK_THROWS_WITH, __VA_ARGS__) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) DOCTEST_ASSERT_THROWS_WITH(expr, #expr, DT_REQUIRE_THROWS_WITH, __VA_ARGS__) #define DOCTEST_WARN_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_WARN_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_CHECK_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_CHECK_THROWS_WITH_AS, message, __VA_ARGS__) #define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, message, ...) DOCTEST_ASSERT_THROWS_AS(expr, DT_REQUIRE_THROWS_WITH_AS, message, __VA_ARGS__) -#define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW) -#define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW) -#define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW) +#define DOCTEST_WARN_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_WARN_NOTHROW, __VA_ARGS__) +#define DOCTEST_CHECK_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_CHECK_NOTHROW, __VA_ARGS__) +#define DOCTEST_REQUIRE_NOTHROW(...) DOCTEST_ASSERT_NOTHROW(DT_REQUIRE_NOTHROW, __VA_ARGS__) -#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while((void)0, 0) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while((void)0, 0) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while((void)0, 0) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while((void)0, 0) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while((void)0, 0) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while((void)0, 0) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH(expr, with); } while((void)0, 0) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH(expr, with); } while((void)0, 0) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while((void)0, 0) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while((void)0, 0) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while((void)0, 0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while((void)0, 0) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while((void)0, 0) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while((void)0, 0) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while((void)0, 0) +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS(expr); } while(false) +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS(expr); } while(false) +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS(expr); } while(false) +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_AS(expr, ex); } while(false) +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_AS(expr, ex); } while(false) +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while(false) +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH(expr, with); } while(false) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH(expr, with); } while(false) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH(expr, with); } while(false) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_THROWS_WITH_AS(expr, with, ex); } while(false) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ex); } while(false) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ex); } while(false) +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_WARN_NOTHROW(expr); } while(false) +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_CHECK_NOTHROW(expr); } while(false) +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) do { DOCTEST_INFO(__VA_ARGS__); DOCTEST_REQUIRE_NOTHROW(expr); } while(false) // clang-format on #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS @@ -2100,7 +2145,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { _DOCTEST_RB.binary_assert( \ __VA_ARGS__)) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } while((void)0, 0) + } while(false) #define DOCTEST_UNARY_ASSERT(assert_type, ...) \ do { \ @@ -2108,7 +2153,7 @@ int registerReporter(const char* name, int priority, bool isReporter) { __LINE__, #__VA_ARGS__); \ DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(__VA_ARGS__)) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ - } while((void)0, 0) + } while(false) #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS @@ -2184,37 +2229,37 @@ int registerReporter(const char* name, int priority, bool isReporter) { #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS -#define DOCTEST_WARN_THROWS(expr) ((void)0) -#define DOCTEST_CHECK_THROWS(expr) ((void)0) -#define DOCTEST_REQUIRE_THROWS(expr) ((void)0) -#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_WARN_NOTHROW(expr) ((void)0) -#define DOCTEST_CHECK_NOTHROW(expr) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) +#define DOCTEST_WARN_THROWS(...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS(...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS(...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) +#define DOCTEST_WARN_NOTHROW(...) (static_cast(0)) +#define DOCTEST_CHECK_NOTHROW(...) (static_cast(0)) +#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) #else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS @@ -2305,86 +2350,86 @@ int registerReporter(const char* name, int priority, bool isReporter) { #define DOCTEST_REGISTER_REPORTER(name, priority, reporter) #define DOCTEST_REGISTER_LISTENER(name, priority, reporter) -#define DOCTEST_INFO(x) ((void)0) -#define DOCTEST_CAPTURE(x) ((void)0) -#define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0) -#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0) -#define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0) -#define DOCTEST_MESSAGE(x) ((void)0) -#define DOCTEST_FAIL_CHECK(x) ((void)0) -#define DOCTEST_FAIL(x) ((void)0) +#define DOCTEST_INFO(...) (static_cast(0)) +#define DOCTEST_CAPTURE(x) (static_cast(0)) +#define DOCTEST_ADD_MESSAGE_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_ADD_FAIL_CHECK_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_ADD_FAIL_AT(file, line, ...) (static_cast(0)) +#define DOCTEST_MESSAGE(...) (static_cast(0)) +#define DOCTEST_FAIL_CHECK(...) (static_cast(0)) +#define DOCTEST_FAIL(...) (static_cast(0)) -#define DOCTEST_WARN(...) ((void)0) -#define DOCTEST_CHECK(...) ((void)0) -#define DOCTEST_REQUIRE(...) ((void)0) -#define DOCTEST_WARN_FALSE(...) ((void)0) -#define DOCTEST_CHECK_FALSE(...) ((void)0) -#define DOCTEST_REQUIRE_FALSE(...) ((void)0) +#define DOCTEST_WARN(...) (static_cast(0)) +#define DOCTEST_CHECK(...) (static_cast(0)) +#define DOCTEST_REQUIRE(...) (static_cast(0)) +#define DOCTEST_WARN_FALSE(...) (static_cast(0)) +#define DOCTEST_CHECK_FALSE(...) (static_cast(0)) +#define DOCTEST_REQUIRE_FALSE(...) (static_cast(0)) -#define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0) -#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0) +#define DOCTEST_WARN_MESSAGE(cond, ...) (static_cast(0)) +#define DOCTEST_CHECK_MESSAGE(cond, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_MESSAGE(cond, ...) (static_cast(0)) +#define DOCTEST_WARN_FALSE_MESSAGE(cond, ...) (static_cast(0)) +#define DOCTEST_CHECK_FALSE_MESSAGE(cond, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, ...) (static_cast(0)) -#define DOCTEST_WARN_THROWS(expr) ((void)0) -#define DOCTEST_CHECK_THROWS(expr) ((void)0) -#define DOCTEST_REQUIRE_THROWS(expr) ((void)0) -#define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) ((void)0) -#define DOCTEST_WARN_NOTHROW(expr) ((void)0) -#define DOCTEST_CHECK_NOTHROW(expr) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) +#define DOCTEST_WARN_THROWS(...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS(...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS(...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_AS(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_AS(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_AS(expr, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH(expr, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH_AS(expr, with, ...) (static_cast(0)) +#define DOCTEST_WARN_NOTHROW(...) (static_cast(0)) +#define DOCTEST_CHECK_NOTHROW(...) (static_cast(0)) +#define DOCTEST_REQUIRE_NOTHROW(...) (static_cast(0)) -#define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, msg) ((void)0) -#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, msg) ((void)0) -#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) -#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) +#define DOCTEST_WARN_THROWS_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH_MESSAGE(expr, with, ...) (static_cast(0)) +#define DOCTEST_WARN_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) +#define DOCTEST_CHECK_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_THROWS_WITH_AS_MESSAGE(expr, with, ex, ...) (static_cast(0)) +#define DOCTEST_WARN_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) +#define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, ...) (static_cast(0)) -#define DOCTEST_WARN_EQ(...) ((void)0) -#define DOCTEST_CHECK_EQ(...) ((void)0) -#define DOCTEST_REQUIRE_EQ(...) ((void)0) -#define DOCTEST_WARN_NE(...) ((void)0) -#define DOCTEST_CHECK_NE(...) ((void)0) -#define DOCTEST_REQUIRE_NE(...) ((void)0) -#define DOCTEST_WARN_GT(...) ((void)0) -#define DOCTEST_CHECK_GT(...) ((void)0) -#define DOCTEST_REQUIRE_GT(...) ((void)0) -#define DOCTEST_WARN_LT(...) ((void)0) -#define DOCTEST_CHECK_LT(...) ((void)0) -#define DOCTEST_REQUIRE_LT(...) ((void)0) -#define DOCTEST_WARN_GE(...) ((void)0) -#define DOCTEST_CHECK_GE(...) ((void)0) -#define DOCTEST_REQUIRE_GE(...) ((void)0) -#define DOCTEST_WARN_LE(...) ((void)0) -#define DOCTEST_CHECK_LE(...) ((void)0) -#define DOCTEST_REQUIRE_LE(...) ((void)0) +#define DOCTEST_WARN_EQ(...) (static_cast(0)) +#define DOCTEST_CHECK_EQ(...) (static_cast(0)) +#define DOCTEST_REQUIRE_EQ(...) (static_cast(0)) +#define DOCTEST_WARN_NE(...) (static_cast(0)) +#define DOCTEST_CHECK_NE(...) (static_cast(0)) +#define DOCTEST_REQUIRE_NE(...) (static_cast(0)) +#define DOCTEST_WARN_GT(...) (static_cast(0)) +#define DOCTEST_CHECK_GT(...) (static_cast(0)) +#define DOCTEST_REQUIRE_GT(...) (static_cast(0)) +#define DOCTEST_WARN_LT(...) (static_cast(0)) +#define DOCTEST_CHECK_LT(...) (static_cast(0)) +#define DOCTEST_REQUIRE_LT(...) (static_cast(0)) +#define DOCTEST_WARN_GE(...) (static_cast(0)) +#define DOCTEST_CHECK_GE(...) (static_cast(0)) +#define DOCTEST_REQUIRE_GE(...) (static_cast(0)) +#define DOCTEST_WARN_LE(...) (static_cast(0)) +#define DOCTEST_CHECK_LE(...) (static_cast(0)) +#define DOCTEST_REQUIRE_LE(...) (static_cast(0)) -#define DOCTEST_WARN_UNARY(...) ((void)0) -#define DOCTEST_CHECK_UNARY(...) ((void)0) -#define DOCTEST_REQUIRE_UNARY(...) ((void)0) -#define DOCTEST_WARN_UNARY_FALSE(...) ((void)0) -#define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0) -#define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0) +#define DOCTEST_WARN_UNARY(...) (static_cast(0)) +#define DOCTEST_CHECK_UNARY(...) (static_cast(0)) +#define DOCTEST_REQUIRE_UNARY(...) (static_cast(0)) +#define DOCTEST_WARN_UNARY_FALSE(...) (static_cast(0)) +#define DOCTEST_CHECK_UNARY_FALSE(...) (static_cast(0)) +#define DOCTEST_REQUIRE_UNARY_FALSE(...) (static_cast(0)) #endif // DOCTEST_CONFIG_DISABLE @@ -2724,9 +2769,7 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #include #include #include -#ifdef DOCTEST_CONFIG_POSIX_SIGNALS #include -#endif // DOCTEST_CONFIG_POSIX_SIGNALS #include #include #include @@ -2751,7 +2794,11 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #ifdef __AFXDLL #include #else +#if defined(__MINGW32__) || defined(__MINGW64__) +#include +#else // MINGW #include +#endif // MINGW #endif #include @@ -2762,6 +2809,12 @@ DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN #endif // DOCTEST_PLATFORM_WINDOWS +// this is a fix for https://github.com/onqtam/doctest/issues/348 +// https://mail.gnome.org/archives/xml/2012-January/msg00000.html +#if !defined(HAVE_UNISTD_H) && !defined(STDOUT_FILENO) +#define STDOUT_FILENO fileno(stdout) +#endif // HAVE_UNISTD_H + DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END // counts the number of elements in a C array @@ -2913,7 +2966,7 @@ typedef timer_large_integer::type ticks_t; //unsigned int getElapsedMilliseconds() const { // return static_cast(getElapsedMicroseconds() / 1000); //} - double getElapsedSeconds() const { return static_cast((getCurrentTicks() - m_ticks)) / 1000000.0; } + double getElapsedSeconds() const { return static_cast(getCurrentTicks() - m_ticks) / 1000000.0; } private: ticks_t m_ticks = 0; @@ -3031,6 +3084,7 @@ String::String() { String::~String() { if(!isOnStack()) delete[] data.ptr; + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) } String::String(const char* in) @@ -3072,6 +3126,7 @@ String& String::operator+=(const String& other) { if(total_size < len) { // append to the current stack space memcpy(buf + my_old_size, other.c_str(), other_size + 1); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) setLast(last - total_size); } else { // alloc new chunk @@ -3113,6 +3168,7 @@ String& String::operator+=(const String& other) { return *this; } +// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) String String::operator+(const String& other) const { return String(*this) += other; } String::String(String&& other) { @@ -3267,6 +3323,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wnull-dereference") // depending on the current options this will remove the path of filenames const char* skipPathFromFilename(const char* file) { +#ifndef DOCTEST_CONFIG_DISABLE if(getContextOptions()->no_path_in_filenames) { auto back = std::strrchr(file, '\\'); auto forward = std::strrchr(file, '/'); @@ -3276,6 +3333,7 @@ const char* skipPathFromFilename(const char* file) { return forward + 1; } } +#endif // DOCTEST_CONFIG_DISABLE return file; } DOCTEST_CLANG_SUPPRESS_WARNING_POP @@ -3294,6 +3352,7 @@ IContextScope::~IContextScope() = default; #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(char* in) { return toString(static_cast(in)); } +// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(bool in) { return in ? "true" : "false"; } @@ -3366,6 +3425,7 @@ bool operator>(double lhs, const Approx& rhs) { return lhs > rhs.m_value && lhs bool operator>(const Approx& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } String toString(const Approx& in) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return String("Approx( ") + doctest::toString(in.m_value) + " )"; } const ContextOptions* getContextOptions() { return DOCTEST_BRANCH_ON_DISABLED(nullptr, g_cs); } @@ -3450,7 +3510,7 @@ namespace detail { } #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS - [[noreturn]] void throwException() { + DOCTEST_NORETURN void throwException() { g_cs->shouldLogCurrentException = false; throw TestFailureException(); } // NOLINT(cert-err60-cpp) @@ -3464,8 +3524,8 @@ namespace { // matching of a string against a wildcard mask (case sensitivity configurable) taken from // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing int wildcmp(const char* str, const char* wild, bool caseSensitive) { - const char* cp = nullptr; - const char* mp = nullptr; + const char* cp = str; + const char* mp = wild; while((*str) && (*wild != '*')) { if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && @@ -3554,6 +3614,10 @@ namespace detail { DOCTEST_ITERATE_THROUGH_REPORTERS(subcase_start, m_signature); } + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + Subcase::~Subcase() { if(m_entered) { // only mark the subcase stack as passed if no subcases have been skipped @@ -3561,7 +3625,7 @@ namespace detail { g_cs->subcasesPassed.insert(g_cs->subcasesStack); g_cs->subcasesStack.pop_back(); -#if __cplusplus >= 201703L && defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411 +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L if(std::uncaught_exceptions() > 0 #else if(std::uncaught_exception() @@ -3578,6 +3642,10 @@ namespace detail { } } + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP + Subcase::operator bool() const { return m_entered; } Result::Result(bool passed, const String& decomposition) @@ -3652,9 +3720,12 @@ namespace detail { bool TestCase::operator<(const TestCase& other) const { if(m_line != other.m_line) return m_line < other.m_line; - const int file_cmp = std::strcmp(m_file, other.m_file); + const int file_cmp = m_file.compare(other.m_file); if(file_cmp != 0) return file_cmp < 0; + const int name_cmp = strcmp(m_name, other.m_name); + if(name_cmp != 0) + return name_cmp < 0; return m_template_id < other.m_template_id; } } // namespace detail @@ -3662,13 +3733,9 @@ namespace { using namespace detail; // for sorting tests by file/line bool fileOrderComparator(const TestCase* lhs, const TestCase* rhs) { -#if DOCTEST_MSVC // this is needed because MSVC gives different case for drive letters // for __FILE__ when evaluated in a header and a source file - const int res = doctest::stricmp(lhs->m_file, rhs->m_file); -#else // MSVC - const int res = std::strcmp(lhs->m_file, rhs->m_file); -#endif // MSVC + const int res = lhs->m_file.compare(rhs->m_file, bool(DOCTEST_MSVC)); if(res != 0) return res < 0; if(lhs->m_line != rhs->m_line) @@ -3723,8 +3790,8 @@ namespace { DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") void color_to_stream(std::ostream& s, Color::Enum code) { - ((void)s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS - ((void)code); // for DOCTEST_CONFIG_COLORS_NONE + static_cast(s); // for DOCTEST_CONFIG_COLORS_NONE or DOCTEST_CONFIG_COLORS_WINDOWS + static_cast(code); // for DOCTEST_CONFIG_COLORS_NONE #ifdef DOCTEST_CONFIG_COLORS_ANSI if(g_no_colors || (isatty(STDOUT_FILENO) == false && getContextOptions()->force_colors == false)) @@ -3830,7 +3897,28 @@ namespace detail { #ifdef DOCTEST_IS_DEBUGGER_ACTIVE bool isDebuggerActive() { return DOCTEST_IS_DEBUGGER_ACTIVE(); } #else // DOCTEST_IS_DEBUGGER_ACTIVE -#ifdef DOCTEST_PLATFORM_MAC +#ifdef DOCTEST_PLATFORM_LINUX + class ErrnoGuard { + public: + ErrnoGuard() : m_oldErrno(errno) {} + ~ErrnoGuard() { errno = m_oldErrno; } + private: + int m_oldErrno; + }; + // See the comments in Catch2 for the reasoning behind this implementation: + // https://github.com/catchorg/Catch2/blob/v2.13.1/include/internal/catch_debugger.cpp#L79-L102 + bool isDebuggerActive() { + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for(std::string line; std::getline(in, line);) { + static const int PREFIX_LEN = 11; + if(line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + return false; + } +#elif defined(DOCTEST_PLATFORM_MAC) // The following function is taken directly from the following technical note: // https://developer.apple.com/library/archive/qa/qa1361/_index.html // Returns true if the current process is being debugged (either @@ -3857,7 +3945,7 @@ namespace detail { // We're being debugged if the P_TRACED flag is set. return ((info.kp_proc.p_flag & P_TRACED) != 0); } -#elif DOCTEST_MSVC || defined(__MINGW32__) +#elif DOCTEST_MSVC || defined(__MINGW32__) || defined(__MINGW64__) bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } #else bool isDebuggerActive() { return false; } @@ -3897,11 +3985,15 @@ namespace detail { g_infoContexts.push_back(this); } + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 + DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") + // destroy cannot be inlined into the destructor because that would mean calling stringify after // ContextScope has been destroyed (base class destructors run after derived class destructors). // Instead, ContextScope calls this method directly from its destructor. void ContextScopeBase::destroy() { -#if __cplusplus >= 201703L && defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411 +#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411L if(std::uncaught_exceptions() > 0) { #else if(std::uncaught_exception()) { @@ -3913,19 +4005,13 @@ namespace detail { g_infoContexts.pop_back(); } + DOCTEST_CLANG_SUPPRESS_WARNING_POP + DOCTEST_GCC_SUPPRESS_WARNING_POP + DOCTEST_MSVC_SUPPRESS_WARNING_POP } // namespace detail namespace { using namespace detail; - std::ostream& file_line_to_stream(std::ostream& s, const char* file, int line, - const char* tail = "") { - const auto opt = getContextOptions(); - s << Color::LightGrey << skipPathFromFilename(file) << (opt->gnu_file_line ? ":" : "(") - << (opt->no_line_numbers ? 0 : line) // 0 or the real num depending on the option - << (opt->gnu_file_line ? ":" : "):") << tail; - return s; - } - #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) struct FatalConditionHandler { @@ -3946,10 +4032,12 @@ namespace { // Windows can easily distinguish between SO and SigSegV, // but SigInt, SigTerm, etc are handled differently. SignalDefs signalDefs[] = { - {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"}, - {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"}, - {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"}, - {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"}, + {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), + "SIGILL - Illegal instruction signal"}, + {static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, + {static_cast(EXCEPTION_ACCESS_VIOLATION), + "SIGSEGV - Segmentation violation signal"}, + {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, }; struct FatalConditionHandler @@ -3975,6 +4063,28 @@ namespace { previousTop = SetUnhandledExceptionFilter(handleException); // Pass in guarantee size to be filled SetThreadStackGuarantee(&guaranteeSize); + + // On Windows uncaught exceptions from another thread, exceptions from + // destructors, or calls to std::terminate are not a SEH exception + + // The terminal handler gets called when: + // - std::terminate is called FROM THE TEST RUNNER THREAD + // - an exception is thrown from a destructor FROM THE TEST RUNNER THREAD + original_terminate_handler = std::get_terminate(); + std::set_terminate([]() noexcept { + reportFatal("Terminate handler called"); + std::exit(EXIT_FAILURE); // explicitly exit - otherwise the SIGABRT handler may be called as well + }); + + // SIGABRT is raised when: + // - std::terminate is called FROM A DIFFERENT THREAD + // - an exception is thrown from a destructor FROM A DIFFERENT THREAD + // - an uncaught exception is thrown FROM A DIFFERENT THREAD + prev_sigabrt_handler = std::signal(SIGABRT, [](int signal) noexcept { + if(signal == SIGABRT) { + reportFatal("SIGABRT - Abort (abnormal termination) signal"); + } + }); } static void reset() { @@ -3984,17 +4094,23 @@ namespace { SetThreadStackGuarantee(&guaranteeSize); previousTop = nullptr; isSet = false; + std::set_terminate(original_terminate_handler); + std::signal(SIGABRT, prev_sigabrt_handler); } } ~FatalConditionHandler() { reset(); } private: + static void (*prev_sigabrt_handler)(int); + static std::terminate_handler original_terminate_handler; static bool isSet; static ULONG guaranteeSize; static LPTOP_LEVEL_EXCEPTION_FILTER previousTop; }; + void (*FatalConditionHandler::prev_sigabrt_handler)(int); + std::terminate_handler FatalConditionHandler::original_terminate_handler; bool FatalConditionHandler::isSet = false; ULONG FatalConditionHandler::guaranteeSize = 0; LPTOP_LEVEL_EXCEPTION_FILTER FatalConditionHandler::previousTop = nullptr; @@ -4194,6 +4310,7 @@ namespace detail { // ################################################################################### DOCTEST_ASSERT_OUT_OF_TESTS(result.m_decomp); DOCTEST_ASSERT_IN_TESTS(result.m_decomp); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) } MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) { @@ -4232,7 +4349,7 @@ namespace { using namespace detail; template - [[noreturn]] void throw_exception(Ex const& e) { + DOCTEST_NORETURN void throw_exception(Ex const& e) { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS throw e; #else // DOCTEST_CONFIG_NO_EXCEPTIONS @@ -4242,9 +4359,11 @@ namespace { #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } +#ifndef DOCTEST_INTERNAL_ERROR #define DOCTEST_INTERNAL_ERROR(msg) \ throw_exception(std::logic_error( \ __FILE__ ":" DOCTEST_TOSTR(__LINE__) ": Internal doctest error: " msg)) +#endif // DOCTEST_INTERNAL_ERROR // clang-format off @@ -4275,8 +4394,8 @@ namespace { public: ScopedElement( XmlWriter* writer ); - ScopedElement( ScopedElement&& other ) noexcept; - ScopedElement& operator=( ScopedElement&& other ) noexcept; + ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT; + ScopedElement& operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT; ~ScopedElement(); @@ -4493,11 +4612,11 @@ namespace { : m_writer( writer ) {} - XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) DOCTEST_NOEXCEPT : m_writer( other.m_writer ){ other.m_writer = nullptr; } - XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) DOCTEST_NOEXCEPT { if ( m_writer ) { m_writer->endElement(); } @@ -4676,7 +4795,7 @@ namespace { tc = ∈ xml.startElement("TestCase") .writeAttribute("name", in.m_name) - .writeAttribute("filename", skipPathFromFilename(in.m_file)) + .writeAttribute("filename", skipPathFromFilename(in.m_file.c_str())) .writeAttribute("line", line(in.m_line)) .writeAttribute("description", in.m_description); @@ -4707,7 +4826,7 @@ namespace { for(unsigned i = 0; i < in.num_data; ++i) { xml.scopedElement("TestCase").writeAttribute("name", in.data[i]->m_name) .writeAttribute("testsuite", in.data[i]->m_test_suite) - .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file)) + .writeAttribute("filename", skipPathFromFilename(in.data[i]->m_file.c_str())) .writeAttribute("line", line(in.data[i]->m_line)); } xml.scopedElement("OverallResultsTestCases") @@ -4863,6 +4982,277 @@ namespace { DOCTEST_REGISTER_REPORTER("xml", 0, XmlReporter); + void fulltext_log_assert_to_stream(std::ostream& s, const AssertData& rb) { + if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == + 0) //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " + << Color::None; + + if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional + s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; + } else if((rb.m_at & assertType::is_throws_as) && + (rb.m_at & assertType::is_throws_with)) { //!OCLINT + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" + << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None; + if(rb.m_threw) { + if(!rb.m_failed) { + s << "threw as expected!\n"; + } else { + s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; + } + } else { + s << "did NOT throw at all!\n"; + } + } else if(rb.m_at & + assertType::is_throws_as) { //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " + << rb.m_exception_type << " ) " << Color::None + << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : + "threw a DIFFERENT exception: ") : + "did NOT throw at all!") + << Color::Cyan << rb.m_exception << "\n"; + } else if(rb.m_at & + assertType::is_throws_with) { //!OCLINT bitwise operator in conditional + s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" + << rb.m_exception_string << "\" ) " << Color::None + << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : + "threw a DIFFERENT exception: ") : + "did NOT throw at all!") + << Color::Cyan << rb.m_exception << "\n"; + } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional + s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan + << rb.m_exception << "\n"; + } else { + s << (rb.m_threw ? "THREW exception: " : + (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); + if(rb.m_threw) + s << rb.m_exception << "\n"; + else + s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; + } + } + + // TODO: + // - log_message() + // - respond to queries + // - honor remaining options + // - more attributes in tags + struct JUnitReporter : public IReporter + { + XmlWriter xml; + std::mutex mutex; + Timer timer; + std::vector deepestSubcaseStackNames; + + struct JUnitTestCaseData + { +DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") // gmtime + static std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); + + char timeStamp[timeStampSize]; + const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + return std::string(timeStamp); + } +DOCTEST_CLANG_SUPPRESS_WARNING_POP + + struct JUnitTestMessage + { + JUnitTestMessage(const std::string& _message, const std::string& _type, const std::string& _details) + : message(_message), type(_type), details(_details) {} + + JUnitTestMessage(const std::string& _message, const std::string& _details) + : message(_message), type(), details(_details) {} + + std::string message, type, details; + }; + + struct JUnitTestCase + { + JUnitTestCase(const std::string& _classname, const std::string& _name) + : classname(_classname), name(_name), time(0), failures() {} + + std::string classname, name; + double time; + std::vector failures, errors; + }; + + void add(const std::string& classname, const std::string& name) { + testcases.emplace_back(classname, name); + } + + void appendSubcaseNamesToLastTestcase(std::vector nameStack) { + for(auto& curr: nameStack) + if(curr.size()) + testcases.back().name += std::string("/") + curr.c_str(); + } + + void addTime(double time) { + if(time < 1e-4) + time = 0; + testcases.back().time = time; + totalSeconds += time; + } + + void addFailure(const std::string& message, const std::string& type, const std::string& details) { + testcases.back().failures.emplace_back(message, type, details); + ++totalFailures; + } + + void addError(const std::string& message, const std::string& details) { + testcases.back().errors.emplace_back(message, details); + ++totalErrors; + } + + std::vector testcases; + double totalSeconds = 0; + int totalErrors = 0, totalFailures = 0; + }; + + JUnitTestCaseData testCaseData; + + // caching pointers/references to objects of these types - safe to do + const ContextOptions& opt; + const TestCaseData* tc = nullptr; + + JUnitReporter(const ContextOptions& co) + : xml(*co.cout) + , opt(co) {} + + unsigned line(unsigned l) const { return opt.no_line_numbers ? 0 : l; } + + // ========================================================================================= + // WHAT FOLLOWS ARE OVERRIDES OF THE VIRTUAL METHODS OF THE REPORTER INTERFACE + // ========================================================================================= + + void report_query(const QueryData&) override {} + + void test_run_start() override {} + + void test_run_end(const TestRunStats& p) override { + // remove .exe extension - mainly to have the same output on UNIX and Windows + std::string binary_name = skipPathFromFilename(opt.binary_name.c_str()); +#ifdef DOCTEST_PLATFORM_WINDOWS + if(binary_name.rfind(".exe") != std::string::npos) + binary_name = binary_name.substr(0, binary_name.length() - 4); +#endif // DOCTEST_PLATFORM_WINDOWS + xml.startElement("testsuites"); + xml.startElement("testsuite").writeAttribute("name", binary_name) + .writeAttribute("errors", testCaseData.totalErrors) + .writeAttribute("failures", testCaseData.totalFailures) + .writeAttribute("tests", p.numAsserts); + if(opt.no_time_in_output == false) { + xml.writeAttribute("time", testCaseData.totalSeconds); + xml.writeAttribute("timestamp", JUnitTestCaseData::getCurrentTimestamp()); + } + if(opt.no_version == false) + xml.writeAttribute("doctest_version", DOCTEST_VERSION_STR); + + for(const auto& testCase : testCaseData.testcases) { + xml.startElement("testcase") + .writeAttribute("classname", testCase.classname) + .writeAttribute("name", testCase.name); + if(opt.no_time_in_output == false) + xml.writeAttribute("time", testCase.time); + // This is not ideal, but it should be enough to mimic gtest's junit output. + xml.writeAttribute("status", "run"); + + for(const auto& failure : testCase.failures) { + xml.scopedElement("failure") + .writeAttribute("message", failure.message) + .writeAttribute("type", failure.type) + .writeText(failure.details, false); + } + + for(const auto& error : testCase.errors) { + xml.scopedElement("error") + .writeAttribute("message", error.message) + .writeText(error.details); + } + + xml.endElement(); + } + xml.endElement(); + xml.endElement(); + } + + void test_case_start(const TestCaseData& in) override { + testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); + timer.start(); + } + + void test_case_reenter(const TestCaseData& in) override { + testCaseData.addTime(timer.getElapsedSeconds()); + testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); + deepestSubcaseStackNames.clear(); + + timer.start(); + testCaseData.add(skipPathFromFilename(in.m_file.c_str()), in.m_name); + } + + void test_case_end(const CurrentTestCaseStats&) override { + testCaseData.addTime(timer.getElapsedSeconds()); + testCaseData.appendSubcaseNamesToLastTestcase(deepestSubcaseStackNames); + deepestSubcaseStackNames.clear(); + } + + void test_case_exception(const TestCaseException& e) override { + std::lock_guard lock(mutex); + testCaseData.addError("exception", e.error_string.c_str()); + } + + void subcase_start(const SubcaseSignature& in) override { + std::lock_guard lock(mutex); + deepestSubcaseStackNames.push_back(in.m_name); + } + + void subcase_end() override {} + + void log_assert(const AssertData& rb) override { + if(!rb.m_failed) // report only failures & ignore the `success` option + return; + + std::lock_guard lock(mutex); + + std::ostringstream os; + os << skipPathFromFilename(rb.m_file) << (opt.gnu_file_line ? ":" : "(") + << line(rb.m_line) << (opt.gnu_file_line ? ":" : "):") << std::endl; + + fulltext_log_assert_to_stream(os, rb); + log_contexts(os); + testCaseData.addFailure(rb.m_decomp.c_str(), assertString(rb.m_at), os.str()); + } + + void log_message(const MessageData&) override {} + + void test_case_skipped(const TestCaseData&) override {} + + void log_contexts(std::ostringstream& s) { + int num_contexts = get_num_active_contexts(); + if(num_contexts) { + auto contexts = get_active_contexts(); + + s << " logged: "; + for(int i = 0; i < num_contexts; ++i) { + s << (i == 0 ? "" : " "); + contexts[i]->stringify(&s); + s << std::endl; + } + } + } + }; + + DOCTEST_REGISTER_REPORTER("junit", 0, JUnitReporter); + struct Whitespace { int nrSpaces; @@ -4881,6 +5271,7 @@ namespace { std::ostream& s; bool hasLoggedCurrentTestStart; std::vector subcasesStack; + size_t currentSubcaseLevel; std::mutex mutex; // caching pointers/references to objects of these types - safe to do @@ -4939,23 +5330,40 @@ namespace { s << "\n"; } + // this was requested to be made virtual so users could override it + virtual void file_line_to_stream(const char* file, int line, + const char* tail = "") { + s << Color::LightGrey << skipPathFromFilename(file) << (opt.gnu_file_line ? ":" : "(") + << (opt.no_line_numbers ? 0 : line) // 0 or the real num depending on the option + << (opt.gnu_file_line ? ":" : "):") << tail; + } + void logTestStart() { if(hasLoggedCurrentTestStart) return; separator_to_stream(); - file_line_to_stream(s, tc->m_file, tc->m_line, "\n"); + file_line_to_stream(tc->m_file.c_str(), tc->m_line, "\n"); if(tc->m_description) s << Color::Yellow << "DESCRIPTION: " << Color::None << tc->m_description << "\n"; if(tc->m_test_suite && tc->m_test_suite[0] != '\0') s << Color::Yellow << "TEST SUITE: " << Color::None << tc->m_test_suite << "\n"; if(strncmp(tc->m_name, " Scenario:", 11) != 0) - s << Color::None << "TEST CASE: "; + s << Color::Yellow << "TEST CASE: "; s << Color::None << tc->m_name << "\n"; - for(auto& curr : subcasesStack) - if(curr.m_name[0] != '\0') - s << " " << curr.m_name << "\n"; + for(size_t i = 0; i < currentSubcaseLevel; ++i) { + if(subcasesStack[i].m_name[0] != '\0') + s << " " << subcasesStack[i].m_name << "\n"; + } + + if(currentSubcaseLevel != subcasesStack.size()) { + s << Color::Yellow << "\nDEEPEST SUBCASE STACK REACHED (DIFFERENT FROM THE CURRENT ONE):\n" << Color::None; + for(size_t i = 0; i < subcasesStack.size(); ++i) { + if(subcasesStack[i].m_name[0] != '\0') + s << " " << subcasesStack[i].m_name << "\n"; + } + } s << "\n"; @@ -5166,25 +5574,28 @@ namespace { separator_to_stream(); s << std::dec; + auto totwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters, static_cast(p.numAsserts))) + 1))); + auto passwidth = int(std::ceil(log10((std::max(p.numTestCasesPassingFilters - p.numTestCasesFailed, static_cast(p.numAsserts - p.numAssertsFailed))) + 1))); + auto failwidth = int(std::ceil(log10((std::max(p.numTestCasesFailed, static_cast(p.numAssertsFailed))) + 1))); const bool anythingFailed = p.numTestCasesFailed > 0 || p.numAssertsFailed > 0; - s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6) + s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(totwidth) << p.numTestCasesPassingFilters << " | " << ((p.numTestCasesPassingFilters == 0 || anythingFailed) ? Color::None : Color::Green) - << std::setw(6) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" + << std::setw(passwidth) << p.numTestCasesPassingFilters - p.numTestCasesFailed << " passed" << Color::None << " | " << (p.numTestCasesFailed > 0 ? Color::Red : Color::None) - << std::setw(6) << p.numTestCasesFailed << " failed" << Color::None << " | "; + << std::setw(failwidth) << p.numTestCasesFailed << " failed" << Color::None << " |"; if(opt.no_skipped_summary == false) { const int numSkipped = p.numTestCases - p.numTestCasesPassingFilters; - s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped + s << " " << (numSkipped == 0 ? Color::None : Color::Yellow) << numSkipped << " skipped" << Color::None; } s << "\n"; - s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6) + s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(totwidth) << p.numAsserts << " | " << ((p.numAsserts == 0 || anythingFailed) ? Color::None : Color::Green) - << std::setw(6) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None - << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(6) + << std::setw(passwidth) << (p.numAsserts - p.numAssertsFailed) << " passed" << Color::None + << " | " << (p.numAssertsFailed > 0 ? Color::Red : Color::None) << std::setw(failwidth) << p.numAssertsFailed << " failed" << Color::None << " |\n"; s << Color::Cyan << "[doctest] " << Color::None << "Status: " << (p.numTestCasesFailed > 0 ? Color::Red : Color::Green) @@ -5194,9 +5605,13 @@ namespace { void test_case_start(const TestCaseData& in) override { hasLoggedCurrentTestStart = false; tc = ∈ + subcasesStack.clear(); + currentSubcaseLevel = 0; } - void test_case_reenter(const TestCaseData&) override {} + void test_case_reenter(const TestCaseData&) override { + subcasesStack.clear(); + } void test_case_end(const CurrentTestCaseStats& st) override { // log the preamble of the test case only if there is something @@ -5235,7 +5650,7 @@ namespace { void test_case_exception(const TestCaseException& e) override { logTestStart(); - file_line_to_stream(s, tc->m_file, tc->m_line, " "); + file_line_to_stream(tc->m_file.c_str(), tc->m_line, " "); successOrFailColoredStringToStream(false, e.is_crash ? assertType::is_require : assertType::is_check); s << Color::Red << (e.is_crash ? "test case CRASHED: " : "test case THREW exception: ") @@ -5256,12 +5671,13 @@ namespace { void subcase_start(const SubcaseSignature& subc) override { std::lock_guard lock(mutex); subcasesStack.push_back(subc); + ++currentSubcaseLevel; hasLoggedCurrentTestStart = false; } void subcase_end() override { std::lock_guard lock(mutex); - subcasesStack.pop_back(); + --currentSubcaseLevel; hasLoggedCurrentTestStart = false; } @@ -5273,55 +5689,10 @@ namespace { logTestStart(); - file_line_to_stream(s, rb.m_file, rb.m_line, " "); + file_line_to_stream(rb.m_file, rb.m_line, " "); successOrFailColoredStringToStream(!rb.m_failed, rb.m_at); - if((rb.m_at & (assertType::is_throws_as | assertType::is_throws_with)) == - 0) //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << " ) " - << Color::None; - if(rb.m_at & assertType::is_throws) { //!OCLINT bitwise operator in conditional - s << (rb.m_threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; - } else if((rb.m_at & assertType::is_throws_as) && - (rb.m_at & assertType::is_throws_with)) { //!OCLINT - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" - << rb.m_exception_string << "\", " << rb.m_exception_type << " ) " << Color::None; - if(rb.m_threw) { - if(!rb.m_failed) { - s << "threw as expected!\n"; - } else { - s << "threw a DIFFERENT exception! (contents: " << rb.m_exception << ")\n"; - } - } else { - s << "did NOT throw at all!\n"; - } - } else if(rb.m_at & - assertType::is_throws_as) { //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", " - << rb.m_exception_type << " ) " << Color::None - << (rb.m_threw ? (rb.m_threw_as ? "threw as expected!" : - "threw a DIFFERENT exception: ") : - "did NOT throw at all!") - << Color::Cyan << rb.m_exception << "\n"; - } else if(rb.m_at & - assertType::is_throws_with) { //!OCLINT bitwise operator in conditional - s << Color::Cyan << assertString(rb.m_at) << "( " << rb.m_expr << ", \"" - << rb.m_exception_string << "\" ) " << Color::None - << (rb.m_threw ? (!rb.m_failed ? "threw as expected!" : - "threw a DIFFERENT exception: ") : - "did NOT throw at all!") - << Color::Cyan << rb.m_exception << "\n"; - } else if(rb.m_at & assertType::is_nothrow) { //!OCLINT bitwise operator in conditional - s << (rb.m_threw ? "THREW exception: " : "didn't throw!") << Color::Cyan - << rb.m_exception << "\n"; - } else { - s << (rb.m_threw ? "THREW exception: " : - (!rb.m_failed ? "is correct!\n" : "is NOT correct!\n")); - if(rb.m_threw) - s << rb.m_exception << "\n"; - else - s << " values: " << assertString(rb.m_at) << "( " << rb.m_decomp << " )\n"; - } + fulltext_log_assert_to_stream(s, rb); log_contexts(); } @@ -5331,7 +5702,7 @@ namespace { logTestStart(); - file_line_to_stream(s, mb.m_file, mb.m_line, " "); + file_line_to_stream(mb.m_file, mb.m_line, " "); s << getSuccessOrFailColor(false, mb.m_severity) << getSuccessOrFailString(mb.m_severity & assertType::is_warn, mb.m_severity, "MESSAGE") << ": "; @@ -5591,7 +5962,9 @@ void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { DOCTEST_PARSE_AS_BOOL_OR_FLAG("gnu-file-line", "gfl", gnu_file_line, !bool(DOCTEST_MSVC)); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-path-filenames", "npf", no_path_in_filenames, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-line-numbers", "nln", no_line_numbers, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-debug-output", "ndo", no_debug_output, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-skipped-summary", "nss", no_skipped_summary, false); + DOCTEST_PARSE_AS_BOOL_OR_FLAG("no-time-in-output", "ntio", no_time_in_output, false); // clang-format on if(withDefaults) { @@ -5647,6 +6020,7 @@ void Context::clearFilters() { // allows the user to override procedurally the int/bool options from the command line void Context::setOption(const char* option, int value) { setOption(option, toString(value).c_str()); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) } // allows the user to override procedurally the string options from the command line @@ -5722,7 +6096,7 @@ int Context::run() { p->reporters_currently_used.insert(p->reporters_currently_used.begin(), curr.second(*g_cs)); #ifdef DOCTEST_PLATFORM_WINDOWS - if(isDebuggerActive()) + if(isDebuggerActive() && p->no_debug_output == false) p->reporters_currently_used.push_back(new DebugOutputWindowReporter(*g_cs)); #endif // DOCTEST_PLATFORM_WINDOWS @@ -5778,9 +6152,9 @@ int Context::run() { if(tc.m_skip && !p->no_skip) skip_me = true; - if(!matchesAny(tc.m_file, p->filters[0], true, p->case_sensitive)) + if(!matchesAny(tc.m_file.c_str(), p->filters[0], true, p->case_sensitive)) skip_me = true; - if(matchesAny(tc.m_file, p->filters[1], false, p->case_sensitive)) + if(matchesAny(tc.m_file.c_str(), p->filters[1], false, p->case_sensitive)) skip_me = true; if(!matchesAny(tc.m_test_suite, p->filters[2], true, p->case_sensitive)) skip_me = true;