diff options
| author | Leitwolf11 | 2026-10-11 10:11:18 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-10-11 10:11:18 +0300 |
| commit | e7aa5ff697a27f140136d3643322301c3a811a69 (patch) | |
| tree | 0fcdeeaa66b2eb69ca1b7e2bff08552be1d531d8 /src/catch2 | |
| parent | ab1214937f57fb81a56e615a0aef8269ade5da91 (diff) | |
| parent | ce6fa1996a8e358efdcff53a7628584077d9855c (diff) | |
| download | catch3-e7aa5ff697a27f140136d3643322301c3a811a69.tar.gz | |
Merge devel into the platforms branch
devel gained the Catch2 3.16.1 backport, which dropped the retired
macos-14 runner from the same line this branch changes. The macOS
builds run on macos-15 and macos-26, as this branch has them. The
upstream status names the platforms that are now the gate.
Diffstat (limited to 'src/catch2')
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 ) \ - template <typename RhsT> \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - -> std::enable_if_t< \ - !capture_by_value<Detail::RemoveCVRef_t<RhsT>>::value, \ - BinaryExpr<LhsT, RhsT const&>> { \ - return { \ - static_cast<bo |