diff options
| author | Leitwolf11 | 2026-10-11 02:56:25 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-10-11 02:56:25 +0300 |
| commit | 1f78d8903c2fb18afea8c3055f8df83d8c60d9b5 (patch) | |
| tree | d76f5945311828511e3ea55712487f21fbf8f70f /src | |
| parent | 5633c5c98ec073ae938253bcfc72430d5d9aee89 (diff) | |
| download | catch3-1f78d8903c2fb18afea8c3055f8df83d8c60d9b5.tar.gz | |
Backport Catch2 3.16.1
Review Catch2 devel through 08092139210881f01a34e60c5f2fd6afa0ea4024,
the commit of 3.16.1, and apply the eleven changes since the last
reviewed revision in upstream order:
d7683484 better message when a reporter cannot be initialized
ea5020f0 custom argument validation in every first-party reporter
aa01559e throwing-reporter test also throws in assertionEnded
45cd3987 noexcept on handleUnexpectedInflightException
6adbd539 MSVC optimizations enabled again after the benchmark headers
e87f2882 no quadratic scaling for NTTP test registration
73d546cd REQUIRE expands its arguments fewer times under GCC and Clang
b710039a decomposer operators as plain functions
f899467b type-erased streaming of reconstructed expressions
8f0e70ca retired macos-14 runner dropped
08092139 version 3.16.1 and its release notes
All of them applied to the sources without a conflict. The amalgamated
distribution under extras/ conflicted because it is generated; it is
generated again from the sources of this repository, which also brings
it up to date with fork changes it had not picked up: the list-based
stream pool, the Clang diagnostic boundary and the cassert include.
No file under src/Progmasoft changes. The fork-owned texts that name
the engine version say 3.16.1, and docs/upstream-status.md records the
review. Upstream files keep their names, licenses and compiler options.
Built with Clang 22 on Windows and tested: 148 of 149 tests pass. The
one that fails, Reporters::CrashInJunitReporter, fails the same way on
devel without this change with that compiler.
Diffstat (limited to 'src')
28 files changed, 311 insertions, 223 deletions
diff --git a/src/catch2/benchmark/catch_optimizer.hpp b/src/catch2/benchmark/catch_optimizer.hpp index 5535b2a2..281256a3 100644 --- a/src/catch2/benchmark/catch_optimizer.hpp +++ b/src/catch2/benchmark/catch_optimizer.hpp @@ -46,7 +46,7 @@ namespace Catch { *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p); } // TODO equivalent keep_memory() -#if defined(_MSVC_VER) +#if defined(_MSC_VER) #pragma optimize("", on) #endif diff --git a/src/catch2/catch_session.cpp b/src/catch2/catch_session.cpp index 1c993ce1..34fd2809 100644 --- a/src/catch2/catch_session.cpp +++ b/src/catch2/catch_session.cpp @@ -458,7 +458,17 @@ namespace Catch { getCurrentMutableContext().setConfig(m_config.get()); // Create reporter(s) so we can route listings through them - auto reporter = prepareReporters(m_config.get()); + IEventListenerPtr reporter; + CATCH_TRY { + auto reporter_ = prepareReporters( m_config.get() ); + swap( reporter, reporter_ ); + } +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + catch ( std::exception& ex ) { + Catch::cerr() << "Error when creating reporter: " << ex.what() << '\n' << std::flush; + return ReporterConstructionErrorExitCode; + } +#endif auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs(); if ( !invalidSpecs.empty() ) { diff --git a/src/catch2/catch_session.hpp b/src/catch2/catch_session.hpp index 23fc6f80..4da7c66f 100644 --- a/src/catch2/catch_session.hpp +++ b/src/catch2/catch_session.hpp @@ -22,6 +22,7 @@ namespace Catch { constexpr int UnmatchedTestSpecExitCode = 3; constexpr int AllTestsSkippedExitCode = 4; constexpr int InvalidTestSpecExitCode = 5; + constexpr int ReporterConstructionErrorExitCode = 6; constexpr int TestFailureExitCode = 42; class Session : Detail::NonCopyable { diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp index d0b44d72..cc332bc2 100644 --- a/src/catch2/catch_version.cpp +++ b/src/catch2/catch_version.cpp @@ -36,7 +36,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 16, 0, "", 0 ); + static Version version( 3, 16, 1, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 3219afb2..241928ab 100644 --- a/src/catch2/catch_version_macros.hpp +++ b/src/catch2/catch_version_macros.hpp @@ -10,6 +10,6 @@ #define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MINOR 16 -#define CATCH_VERSION_PATCH 0 +#define CATCH_VERSION_PATCH 1 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED diff --git a/src/catch2/internal/catch_assertion_handler.cpp b/src/catch2/internal/catch_assertion_handler.cpp index 79d5dcd8..e028c371 100644 --- a/src/catch2/internal/catch_assertion_handler.cpp +++ b/src/catch2/internal/catch_assertion_handler.cpp @@ -61,7 +61,7 @@ namespace Catch { } } - void AssertionHandler::handleUnexpectedInflightException() { + void AssertionHandler::handleUnexpectedInflightException() noexcept { m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); } diff --git a/src/catch2/internal/catch_assertion_handler.hpp b/src/catch2/internal/catch_assertion_handler.hpp index 93a70b1a..4b7607ad 100644 --- a/src/catch2/internal/catch_assertion_handler.hpp +++ b/src/catch2/internal/catch_assertion_handler.hpp @@ -59,7 +59,20 @@ namespace Catch { void handleUnexpectedExceptionNotThrown(); void handleExceptionNotThrownAsExpected(); void handleThrowingCallSkipped(); - void handleUnexpectedInflightException(); + + // Marking this as `noexcept` gives us measurable improvement + // in optimized compilation times. Making it `noexcept` changes + // what happens if the underlying code throws, **but** the only + // way this can throw is if the reporter itself throws from + // `assertionEnded`. However, in such case the process is going + // to abort anyway, because we will enter `assertionEnded` again + // from the (noexcept) destructor of `AssertionHandler` above. + // In the future, all reporter event handlers will be marked + // `noexcept` to make this limitation explicit. + void handleUnexpectedInflightException() noexcept; + // Ideally we could extend the logic above to more of the handle* + // function, but right now it would worsen the behaviour of + // preexisting custom reporters that throw from event handlers. void complete(); diff --git a/src/catch2/internal/catch_decomposer.cpp b/src/catch2/internal/catch_decomposer.cpp index 17a7bc95..e702c745 100644 --- a/src/catch2/internal/catch_decomposer.cpp +++ b/src/catch2/internal/catch_decomposer.cpp @@ -10,13 +10,6 @@ namespace Catch { - void ITransientExpression::streamReconstructedExpression( - std::ostream& os ) const { - // We can't make this function pure virtual to keep ITransientExpression - // constexpr, so we write error message instead - os << "Some class derived from ITransientExpression without overriding streamReconstructedExpression"; - } - void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { if( lhs.size() + rhs.size() < 40 && lhs.find('\n') == std::string::npos && diff --git a/src/catch2/internal/catch_decomposer.hpp b/src/catch2/internal/catch_decomposer.hpp index ff628482..9c004b02 100644 --- a/src/catch2/internal/catch_decomposer.hpp +++ b/src/catch2/internal/catch_decomposer.hpp @@ -164,28 +164,27 @@ namespace Catch { struct always_false : std::false_type {}; class ITransientExpression { + //! Type-erased streaming function for the derived expression + using StreamFn = void ( * )( void const*, std::ostream& ); + + StreamFn m_streamFn; bool m_isBinaryExpression; bool m_result; - protected: - ~ITransientExpression() = default; - public: constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } constexpr auto getResult() const -> bool { return m_result; } - //! This function **has** to be overridden by the derived class. - virtual void streamReconstructedExpression( std::ostream& os ) const; - - constexpr ITransientExpression( bool isBinaryExpression, bool result ) - : m_isBinaryExpression( isBinaryExpression ), - m_result( result ) - {} - constexpr ITransientExpression( ITransientExpression const& ) = default; - constexpr ITransientExpression& operator=( ITransientExpression const& ) = default; + constexpr ITransientExpression( StreamFn streamFn, + bool isBinaryExpression, + bool result ): + m_streamFn( streamFn ), + m_isBinaryExpression( isBinaryExpression ), + m_result( result ) {} - friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { - expr.streamReconstructedExpression(out); + friend std::ostream& operator<<( std::ostream& out, + ITransientExpression const& expr ) { + expr.m_streamFn( &expr, out ); return out; } }; @@ -198,87 +197,67 @@ namespace Catch { StringRef m_op; RhsT m_rhs; - void streamReconstructedExpression( std::ostream &os ) const override { - formatReconstructedExpression - ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + static void streamFn( void const* self, std::ostream& out ) { + auto const& expr = *static_cast<BinaryExpr const*>( + static_cast<ITransientExpression const*>( self ) ); + formatReconstructedExpression( + out, + Catch::Detail::stringify( expr.m_lhs ), + expr.m_op, + Catch::Detail::stringify( expr.m_rhs ) ); } public: constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) - : ITransientExpression{ true, comparisonResult }, + : ITransientExpression{ &streamFn, true, comparisonResult }, m_lhs( lhs ), m_op( op ), m_rhs( rhs ) {} + }; - template<typename T> - auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } +#define CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( op ) \ + template <typename LhsT, typename RhsT, typename T> \ + auto operator op( BinaryExpr<LhsT, RhsT> const&, T ) \ + ->BinaryExpr<LhsT, RhsT const&> const { \ + static_assert( always_false<T>::value, \ + "chained comparisons are not supported inside " \ + "assertions, wrap the expression inside " \ + "parentheses, or decompose it" ); \ + } - template<typename T> - auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - }; + /* + * The operators are **intentionally** not hidden friends. + * + * If they were hidden friends, all of them would get stamped out when + * a `BinaryExpr` is instantiated with a new type. In practice, only + * few operators are used for each different `BinaryExpr<LhsT, RhsT>`, + * so this avoids bunch of wasted work. + */ + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( && ) + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( || ) + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( == ) + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( != ) + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( > ) + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( < ) + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( >= ) + CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( <= ) + +#undef CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR template<typename LhsT> class UnaryExpr final : public ITransientExpression { LhsT m_lhs; - void streamReconstructedExpression( std::ostream &os ) const override { - os << Catch::Detail::stringify( m_lhs ); + static void streamFn( void const* self, std::ostream& os ) { + auto const& expr = *static_cast<UnaryExpr const*>( + static_cast<ITransientExpression const*>( self ) ); + os << Catch::Detail::stringify( expr.m_lhs ); } public: explicit constexpr UnaryExpr( LhsT lhs ) - : ITransientExpression{ false, static_cast<bool>(lhs) }, + : ITransientExpression{ &streamFn, false, static_cast<bool>(lhs) }, m_lhs( lhs ) {} }; @@ -290,153 +269,186 @@ namespace Catch { public: explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + // We rely on reference collapsing if LhsT is already a reference. + // Used in the out-of-class decomposition operators + constexpr LhsT& getLhs() { return m_lhs; } + + constexpr auto makeUnaryExpr() const -> UnaryExpr<LhsT> { + return UnaryExpr<LhsT>{ m_lhs }; + } + }; + +/* + * The comparison operators are **intentionally** not hidden friends. + * + * If they were hidden friends, all of them would get stamped out when + * a `ExprLhs` is instantiated with a new type. In practice, only few + * operators are used for each different `ExprLhs<T>`, so this avoids + * a lot of wasted work. + */ #define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT&& rhs ) \ + ->std::enable_if_t< \ Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \ Detail::negation<capture_by_value< \ Detail::RemoveCVRef_t<RhsT>>>>::value, \ BinaryExpr<LhsT, RhsT const&>> { \ - return { \ - static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + return { static_cast<bool>( lhs.getLhs() op rhs ), \ + lhs.getLhs(), \ + #op##_sr, \ + rhs }; \ } \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT rhs ) \ + ->std::enable_if_t< \ Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \ capture_by_value<RhsT>>::value, \ BinaryExpr<LhsT, RhsT>> { \ - return { \ - static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + return { static_cast<bool>( lhs.getLhs() op rhs ), \ + lhs.getLhs(), \ + #op##_sr, \ + rhs }; \ } \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT rhs ) \ + ->std::enable_if_t< \ Detail::conjunction< \ Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \ - Detail::is_eq_0_comparable<LhsT>, \ - /* We allow long because we want `ptr op NULL` to be accepted */ \ + Detail::is_eq_0_comparable<LhsT>, /* We allow long because we \ + want `ptr op NULL` to be \ + accepted */ \ Detail::disjunction<std::is_same<RhsT, int>, \ std::is_same<RhsT, long>>>::value, \ BinaryExpr<LhsT, RhsT>> { \ if ( rhs != 0 ) { throw_test_failure_exception(); } \ - return { \ - static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ + return { static_cast<bool>( lhs.getLhs() op 0 ), \ + lhs.getLhs(), \ + #op##_sr, \ + rhs }; \ } \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT rhs ) \ + ->std::enable_if_t< \ Detail::conjunction< \ Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \ - Detail::is_eq_0_comparable<RhsT>, \ - /* We allow long because we want `ptr op NULL` to be accepted */ \ + Detail::is_eq_0_comparable<RhsT>, /* We allow long because we \ + want `ptr op NULL` to be \ + accepted */ \ Detail::disjunction<std::is_same<LhsT, int>, \ std::is_same<LhsT, long>>>::value, \ BinaryExpr<LhsT, RhsT>> { \ - if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ - return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + if ( lhs.getLhs() != 0 ) { throw_test_failure_exception(); } \ + return { static_cast<bool>( 0 op rhs ), lhs.getLhs(), #op##_sr, rhs }; \ } - CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) - CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) - - #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR + CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) + CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) +#undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT&& rhs ) \ + ->std::enable_if_t< \ Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \ Detail::negation<capture_by_value< \ Detail::RemoveCVRef_t<RhsT>>>>::value, \ BinaryExpr<LhsT, RhsT const&>> { \ - return { \ - static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + return { static_cast<bool>( lhs.getLhs() op rhs ), \ + lhs.getLhs(), \ + #op##_sr, \ + rhs }; \ } \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT rhs ) \ + ->std::enable_if_t< \ Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \ capture_by_value<RhsT>>::value, \ BinaryExpr<LhsT, RhsT>> { \ - return { \ - static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + return { static_cast<bool>( lhs.getLhs() op rhs ), \ + lhs.getLhs(), \ + #op##_sr, \ + rhs }; \ } \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT rhs ) \ + ->std::enable_if_t< \ Detail::conjunction< \ Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \ Detail::is_##id##_0_comparable<LhsT>, \ std::is_same<RhsT, int>>::value, \ BinaryExpr<LhsT, RhsT>> { \ if ( rhs != 0 ) { throw_test_failure_exception(); } \ - return { \ - static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ + return { static_cast<bool>( lhs.getLhs() op 0 ), \ + lhs.getLhs(), \ + #op##_sr, \ + rhs }; \ } \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ + template <typename LhsT, typename RhsT> \ + constexpr auto operator op( ExprLhs<LhsT>&& lhs, RhsT rhs ) \ + ->std::enable_if_t< \ Detail::conjunction< \ Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \ Detail::is_##id##_0_comparable<RhsT>, \ std::is_same<LhsT, int>>::value, \ BinaryExpr<LhsT, RhsT>> { \ - if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ - return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + if ( lhs.getLhs() != 0 ) { throw_test_failure_exception(); } \ + return { static_cast<bool>( 0 op rhs ), lhs.getLhs(), #op##_sr, rhs }; \ } - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < ) - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= ) - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > ) - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= ) - - #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR - - -#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) |