diff --git a/CMakeLists.txt b/CMakeLists.txt index 918927c4..84929314 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.11.0 # CML version placeholder, don't delete + VERSION 3.12.0 # CML version placeholder, don't delete LANGUAGES CXX HOMEPAGE_URL "https://github.com/catchorg/Catch2" DESCRIPTION "A modern, C++-native, unit test framework." diff --git a/docs/configuration.md b/docs/configuration.md index 71496db0..527a3f82 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -322,7 +322,7 @@ are not meant to be runnable, only "scannable". > Introduced in Catch2 3.9.0 -> Made non-experimental in Catch2 vX.Y.Z +> Made non-experimental in Catch2 3.12.0 Catch2 can optionally support thread-safe assertions, that means, multiple user-spawned threads can use the assertion macros at the same time. Due diff --git a/docs/release-notes.md b/docs/release-notes.md index 9da1a2e1..35a76774 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,7 @@ # Release notes **Contents**
+[3.12.0](#3120)
[3.11.0](#3110)
[3.10.0](#3100)
[3.9.1](#391)
@@ -71,6 +72,31 @@ [Even Older versions](#even-older-versions)
+## 3.12.0 + +### Fixes +* Fixed unscoped messages after a passing fast-pathed assertion being lost. +* Fixed the help string for `--order` to mention random order as the default. (#3045) +* Fixed small documentation typos. (#3039) +* Fixed compilation with `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS` for older C++ standards. +* Fixed a thread-safety issue with message macros being used too early after the process starts. +* Fixed automatic configuration to properly handle PlayStation platform. (#3054) +* **Fixed the _weird_ behaviour of section filtering when specifying multiple filters.** (#3038) + * See #3038 for more details. + +### Improvements +* Added `lifetimebound` attribute to various places. + * As an example, compiler that supports lifetime analysis will now diagnose invalid use of Matcher combinators. +* Minor compile-time improvements to stringification. (#3028) + * `std::tuple` printer does not recurse. + * Some implementation details were outlined into the cpp file. +* Global variables will only be marked with `thread_local` in thread-safe builds. (#3044) + +### Miscellaneous +* The thread safety support is no longer experimental. + * The new CMake option and C++ define is now `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS`. + + ## 3.11.0 ### Fixes diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index 892d637d..a4406d0c 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.11.0 -// Generated: 2025-09-30 10:49:12.549018 +// Catch v3.12.0 +// Generated: 2025-12-28 22:27:25.828797 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -2045,6 +2045,36 @@ namespace Detail { } } // end unnamed namespace + std::size_t catch_strnlen( const char* str, std::size_t n ) { + auto ret = std::char_traits::find( str, n, '\0' ); + if ( ret != nullptr ) { return static_cast( ret - str ); } + return n; + } + + std::string formatTimeT(std::time_t time) { +#ifdef _MSC_VER + std::tm timeInfo = {}; + const auto err = gmtime_s( &timeInfo, &time ); + if ( err ) { + return "gmtime from provided timepoint has failed. This " + "happens e.g. with pre-1970 dates using Microsoft libc"; + } +#else + std::tm* timeInfo = std::gmtime( &time ); +#endif + + auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" ); + char timeStamp[timeStampSize]; + const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime( timeStamp, timeStampSize, fmt, &timeInfo ); +#else + std::strftime( timeStamp, timeStampSize, fmt, timeInfo ); +#endif + return std::string( timeStamp, timeStampSize - 1 ); + } + std::string convertIntoString(StringRef string, bool escapeInvisibles) { std::string ret; // This is enough for the "don't escape invisibles" case, and a good @@ -2354,7 +2384,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 11, 0, "", 0 ); + static Version version( 3, 12, 0, "", 0 ); return version; } @@ -3402,7 +3432,7 @@ namespace Catch { ( "list all listeners" ) | Opt( setTestOrder, "decl|lex|rand" ) ["--order"] - ( "test case order (defaults to decl)" ) + ( "test case order (defaults to rand)" ) | Opt( setRngSeed, "'time'|'random-device'|number" ) ["--rng-seed"] ( "set a specific seed for random numbers" ) @@ -3602,7 +3632,9 @@ namespace { #if defined( CATCH_PLATFORM_LINUX ) \ || defined( CATCH_PLATFORM_MAC ) \ || defined( __GLIBC__ ) \ - || defined( __FreeBSD__ ) \ + || (defined( __FreeBSD__ ) \ + /* PlayStation platform does not have `isatty()` */ \ + && !defined(CATCH_PLATFORM_PLAYSTATION)) \ || defined( CATCH_PLATFORM_QNX ) # define CATCH_INTERNAL_HAS_ISATTY # include @@ -4886,19 +4918,22 @@ int main (int argc, char * argv[]) { namespace Catch { + namespace { + // Messages are owned by their individual threads, so the counter should + // be thread-local as well. Alternative consideration: atomic counter, + // so threads don't share IDs and things are easier to debug. + static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0; + } + MessageInfo::MessageInfo( StringRef _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ) : macroName( _macroName ), lineInfo( _lineInfo ), type( _type ), - sequence( ++globalCount ) + sequence( ++messageIDCounter ) {} - // Messages are owned by their individual threads, so the counter should be thread-local as well. - // Alternative consideration: atomic, so threads don't share IDs and things are easier to debug. - thread_local unsigned int MessageInfo::globalCount = 0; - } // end namespace Catch @@ -5814,12 +5849,8 @@ namespace Catch { for ( auto const& child : m_children ) { if ( child->isSectionTracker() && - std::find( filters.begin(), - filters.end(), - static_cast( - *child ) - .trimmedName() ) != - filters.end() ) { + static_cast( *child ) + .trimmedName() == filters[0] ) { return true; } } @@ -5862,27 +5893,98 @@ namespace Catch { // should also be thread local. For now we just use naked globals // below, in the future we will want to allocate piece of memory // from heap, to avoid consuming too much thread-local storage. + // + // Note that we also don't want non-trivial the thread-local variables + // below be initialized for every thread, only for those that touch + // Catch2. To make this work with both GCC/Clang and MSVC, we have to + // make them thread-local magic statics. (Class-level statics have the + // desired semantics on GCC, but not on MSVC). // This is used for the "if" part of CHECKED_IF/CHECKED_ELSE - static thread_local bool g_lastAssertionPassed = false; + static CATCH_INTERNAL_THREAD_LOCAL bool g_lastAssertionPassed = false; // This is the source location for last encountered macro. It is // used to provide the users with more precise location of error // when an unexpected exception/fatal error happens. - static thread_local SourceLineInfo g_lastKnownLineInfo("DummyLocation", static_cast(-1)); + static CATCH_INTERNAL_THREAD_LOCAL SourceLineInfo + g_lastKnownLineInfo( "DummyLocation", static_cast( -1 ) ); // Should we clear message scopes before sending off the messages to // reporter? Set in `assertionPassedFastPath` to avoid doing the full // clear there for performance reasons. - static thread_local bool g_clearMessageScopes = false; + static CATCH_INTERNAL_THREAD_LOCAL bool g_clearMessageScopes = false; + + + // Holds the data for both scoped and unscoped messages together, + // to avoid issues where their lifetimes start in wrong order, + // and then are destroyed in wrong order. + class MessageHolder { + // The actual message vector passed to the reporters + std::vector messages; + // IDs of messages from UNSCOPED_X macros, which we have to + // remove manually. + std::vector unscoped_ids; + + public: + // We do not need to special-case the unscoped messages when + // we only keep around the raw msg ids. + ~MessageHolder() = default; + + + void addUnscopedMessage(MessageBuilder&& builder) { + repairUnscopedMessageInvariant(); + MessageInfo info( CATCH_MOVE( builder.m_info ) ); + info.message = builder.m_stream.str(); + unscoped_ids.push_back( info.sequence ); + messages.push_back( CATCH_MOVE( info ) ); + } + + void addScopedMessage(MessageInfo&& info) { + messages.push_back( CATCH_MOVE( info ) ); + } + + std::vector const& getMessages() const { + return messages; + } + + void removeMessage( unsigned int messageId ) { + // Note: On average, it would probably be better to look for + // the message backwards. However, we do not expect to have + // to deal with more messages than low single digits, so + // the improvement is tiny, and we would have to hand-write + // the loop to avoid terrible codegen of reverse iterators + // in debug mode. + auto iter = + std::find_if( messages.begin(), + messages.end(), + [messageId]( MessageInfo const& msg ) { + return msg.sequence == messageId; + } ); + assert( iter != messages.end() && + "Trying to remove non-existent message." ); + messages.erase( iter ); + } + + void removeUnscopedMessages() { + for ( const auto messageId : unscoped_ids ) { + removeMessage( messageId ); + } + unscoped_ids.clear(); + g_clearMessageScopes = false; + } + + void repairUnscopedMessageInvariant() { + if ( g_clearMessageScopes ) { removeUnscopedMessages(); } + g_clearMessageScopes = false; + } + }; CATCH_INTERNAL_START_WARNINGS_SUPPRESSION CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - // Actual messages to be provided to the reporter - static thread_local std::vector g_messages; - - // Owners for the UNSCOPED_X information macro - static thread_local std::vector g_messageScopes; + static MessageHolder& g_messageHolder() { + static CATCH_INTERNAL_THREAD_LOCAL MessageHolder value; + return value; + } CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION } // namespace Detail @@ -5899,6 +6001,13 @@ namespace Catch { { getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); + + // TODO: HACK! + // We need to make sure the underlying cache is initialized + // while we are guaranteed to be running in a single thread, + // because the initialization is not thread-safe. + ReusableStringStream rss; + (void)rss; } RunContext::~RunContext() { @@ -6021,21 +6130,19 @@ namespace Catch { Detail::g_lastAssertionPassed = true; } - if ( Detail::g_clearMessageScopes ) { - Detail::g_messageScopes.clear(); - Detail::g_clearMessageScopes = false; - } + auto& msgHolder = Detail::g_messageHolder(); + msgHolder.repairUnscopedMessageInvariant(); // From here, we are touching shared state and need mutex. Detail::LockGuard lock( m_assertionMutex ); { auto _ = scopedDeactivate( *m_outputRedirect ); updateTotalsFromAtomics(); - m_reporter->assertionEnded( AssertionStats( result, Detail::g_messages, m_totals ) ); + m_reporter->assertionEnded( AssertionStats( result, msgHolder.getMessages(), m_totals ) ); } if ( result.getResultType() != ResultWas::Warning ) { - Detail::g_messageScopes.clear(); + msgHolder.removeUnscopedMessages(); } // Reset working state. assertion info will be reset after @@ -6324,10 +6431,10 @@ namespace Catch { m_testCaseTracker->close(); handleUnfinishedSections(); - Detail::g_messageScopes.clear(); - // TBD: At this point, m_messages should be empty. Do we want to - // assert that this is true, or keep the defensive clear call? - Detail::g_messages.clear(); + auto& msgHolder = Detail::g_messageHolder(); + msgHolder.removeUnscopedMessages(); + assert( msgHolder.getMessages().empty() && + "There should be no leftover messages after the test ends" ); SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions); m_reporter->sectionEnded(testCaseSectionStats); @@ -6495,25 +6602,15 @@ namespace Catch { } void IResultCapture::pushScopedMessage( MessageInfo&& message ) { - Detail::g_messages.push_back( CATCH_MOVE( message ) ); + Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) ); } void IResultCapture::popScopedMessage( unsigned int messageId ) { - // Note: On average, it would probably be better to look for the message - // backwards. However, we do not expect to have to deal with more - // messages than low single digits, so the optimization is tiny, - // and we would have to hand-write the loop to avoid terrible - // codegen of reverse iterators in debug mode. - Detail::g_messages.erase( std::find_if( Detail::g_messages.begin(), - Detail::g_messages.end(), - [=]( MessageInfo const& msg ) { - return msg.sequence == - messageId; - } ) ); + Detail::g_messageHolder().removeMessage( messageId ); } void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) { - Detail::g_messageScopes.emplace_back( CATCH_MOVE( builder ) ); + Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) ); } void seedRng(IConfig const& config) { @@ -7219,9 +7316,9 @@ namespace TestCaseTracking { bool SectionTracker::isComplete() const { bool complete = true; - if (m_filters.empty() + if ( m_filters.empty() || m_filters[0].empty() - || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { + || m_filters[0] == m_trimmed_name ) { complete = TrackerBase::isComplete(); } return complete; diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index d5eb1bb7..00fc3ee1 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.11.0 -// Generated: 2025-09-30 10:49:11.225746 +// Catch v3.12.0 +// Generated: 2025-12-28 22:27:25.408132 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -694,11 +694,30 @@ namespace Catch { #ifndef CATCH_STRINGREF_HPP_INCLUDED #define CATCH_STRINGREF_HPP_INCLUDED + + + +#ifndef CATCH_LIFETIMEBOUND_HPP_INCLUDED +#define CATCH_LIFETIMEBOUND_HPP_INCLUDED + +#if !defined( __has_cpp_attribute ) +# define CATCH_ATTR_LIFETIMEBOUND +#elif __has_cpp_attribute( msvc::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]] +#elif __has_cpp_attribute( clang::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] +#elif __has_cpp_attribute( lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]] +#else +# define CATCH_ATTR_LIFETIMEBOUND +#endif + +#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED + #include #include #include #include - #include namespace Catch { @@ -722,14 +741,16 @@ namespace Catch { public: // construction constexpr StringRef() noexcept = default; - StringRef( char const* rawChars ) noexcept; + StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept; - constexpr StringRef( char const* rawChars, size_type size ) noexcept + constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND, + size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} - StringRef( std::string const& stdString ) noexcept + StringRef( + std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} @@ -775,7 +796,7 @@ namespace Catch { } // Returns the current start pointer. May not be null-terminated. - constexpr char const* data() const noexcept { + constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND { return m_start; } @@ -2305,7 +2326,7 @@ namespace Catch { #ifndef CATCH_TOSTRING_HPP_INCLUDED #define CATCH_TOSTRING_HPP_INCLUDED - +#include #include #include #include @@ -2473,13 +2494,9 @@ namespace Catch { namespace Detail { - inline std::size_t catch_strnlen(const char *str, std::size_t n) { - auto ret = std::char_traits::find(str, n, '\0'); - if (ret != nullptr) { - return static_cast(ret - str); - } - return n; - } + std::size_t catch_strnlen(const char *str, std::size_t n); + + std::string formatTimeT( std::time_t time ); constexpr StringRef unprintableString = "{?}"_sr; @@ -2844,44 +2861,38 @@ namespace Catch { // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) -#include +# include +# include namespace Catch { namespace Detail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct TupleElementPrinter { - static void print(const Tuple& tuple, std::ostream& os) { - os << (N ? ", " : " ") - << ::Catch::Detail::stringify(std::get(tuple)); - TupleElementPrinter::print(tuple, os); - } - }; + template + void PrintTuple( const Tuple& tuple, + std::ostream& os, + std::index_sequence ) { + // 1 + Account for when the tuple is empty + char a[1 + sizeof...( Is )] = { + ( ( os << ( Is ? ", " : " " ) + << ::Catch::Detail::stringify( std::get( tuple ) ) ), + '\0' )... }; + (void)a; + } - template< - typename Tuple, - std::size_t N - > - struct TupleElementPrinter { - static void print(const Tuple&, std::ostream&) {} - }; + } // namespace Detail - } - - - template + template struct StringMaker> { - static std::string convert(const std::tuple& tuple) { + static std::string convert( const std::tuple& tuple ) { ReusableStringStream rss; rss << '{'; - Detail::TupleElementPrinter>::print(tuple, rss.get()); + Detail::PrintTuple( + tuple, + rss.get(), + std::make_index_sequence{} ); rss << " }"; return rss.str(); } }; -} +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) @@ -3068,28 +3079,7 @@ struct ratio_string { const auto systemish = std::chrono::time_point_cast< std::chrono::system_clock::duration>( time_point ); const auto as_time_t = std::chrono::system_clock::to_time_t( systemish ); - -#ifdef _MSC_VER - std::tm timeInfo = {}; - const auto err = gmtime_s( &timeInfo, &as_time_t ); - if ( err ) { - return "gmtime from provided timepoint has failed. This " - "happens e.g. with pre-1970 dates using Microsoft libc"; - } -#else - std::tm* timeInfo = std::gmtime( &as_time_t ); -#endif - - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif - return std::string(timeStamp, timeStampSize - 1); + return ::Catch::Detail::formatTimeT( as_time_t ); } }; } @@ -3984,8 +3974,6 @@ namespace Catch { bool operator < (MessageInfo const& other) const { return sequence < other.sequence; } - private: - static thread_local unsigned int globalCount; }; } // end namespace Catch @@ -7478,7 +7466,7 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 11 +#define CATCH_VERSION_MINOR 12 #define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -10094,8 +10082,8 @@ namespace Catch { class JsonValueWriter { public: - JsonValueWriter( std::ostream& os ); - JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); + JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonObjectWriter writeObject() &&; JsonArrayWriter writeArray() &&; @@ -10129,8 +10117,8 @@ namespace Catch { class JsonObjectWriter { public: - JsonObjectWriter( std::ostream& os ); - JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); + JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonObjectWriter( JsonObjectWriter&& source ) noexcept; JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; @@ -10148,8 +10136,8 @@ namespace Catch { class JsonArrayWriter { public: - JsonArrayWriter( std::ostream& os ); - JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); + JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); + JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level ); JsonArrayWriter( JsonArrayWriter&& source ) noexcept; JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; @@ -10422,7 +10410,7 @@ namespace TestCaseTracking { StringRef name; SourceLineInfo location; - constexpr NameAndLocationRef( StringRef name_, + constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND, SourceLineInfo location_ ): name( name_ ), location( location_ ) {} @@ -10622,7 +10610,7 @@ using TestCaseTracking::SectionTracker; #define CATCH_THREAD_SUPPORT_HPP_INCLUDED -#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) # include # include #endif @@ -10630,14 +10618,14 @@ using TestCaseTracking::SectionTracker; namespace Catch { namespace Detail { -#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) using Mutex = std::mutex; using LockGuard = std::lock_guard; struct AtomicCounts { - std::atomic passed = 0; - std::atomic failed = 0; - std::atomic failedButOk = 0; - std::atomic skipped = 0; + std::atomic passed{ 0 }; + std::atomic failed{ 0 }; + std::atomic failedButOk{ 0 }; + std::atomic skipped{ 0 }; }; #else // ^^ Use actual mutex, lock and atomics // vv Dummy implementations for single-thread performance @@ -10942,10 +10930,10 @@ namespace Catch { //! Returns a new string without whitespace at the start/end std::string trim( std::string const& str ); //! Returns a substring of the original ref without whitespace. Beware lifetimes! - StringRef trim(StringRef ref); + StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND ); // !!! Be aware, returns refs into original string - make sure original string outlives them - std::vector splitStringRef( StringRef str, char delimiter ); + std::vector splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); /** @@ -10963,7 +10951,7 @@ namespace Catch { StringRef m_label; public: - constexpr pluralise(std::uint64_t count, StringRef label): + constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND): m_count(count), m_label(label) {} @@ -11442,6 +11430,19 @@ namespace Catch { #endif // CATCH_TEXTFLOW_HPP_INCLUDED +#ifndef CATCH_THREAD_LOCAL_HPP_INCLUDED +#define CATCH_THREAD_LOCAL_HPP_INCLUDED + + +#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) +#define CATCH_INTERNAL_THREAD_LOCAL thread_local +#else +#define CATCH_INTERNAL_THREAD_LOCAL +#endif + +#endif // CATCH_THREAD_LOCAL_HPP_INCLUDED + + #ifndef CATCH_TO_STRING_HPP_INCLUDED #define CATCH_TO_STRING_HPP_INCLUDED @@ -11510,7 +11511,7 @@ namespace Catch { public: enum ForWhat { ForTextNodes, ForAttributes }; - constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ): + constexpr XmlEncode( StringRef str CATCH_ATTR_LIFETIMEBOUND, ForWhat forWhat = ForTextNodes ): m_str( str ), m_forWhat( forWhat ) {} @@ -11528,7 +11529,7 @@ namespace Catch { class ScopedElement { public: - ScopedElement( XmlWriter* writer, XmlFormatting fmt ); + ScopedElement( XmlWriter* writer CATCH_ATTR_LIFETIMEBOUND, XmlFormatting fmt ); ScopedElement( ScopedElement&& other ) noexcept; ScopedElement& operator=( ScopedElement&& other ) noexcept; @@ -11560,7 +11561,7 @@ namespace Catch { XmlFormatting m_fmt; }; - XmlWriter( std::ostream& os ); + XmlWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND ); ~XmlWriter(); XmlWriter( XmlWriter const& ) = delete; @@ -11818,11 +11819,15 @@ namespace Matchers { return description; } - friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase const& rhs) { + friend MatchAllOf operator&&( MatchAllOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAllOf operator&& (MatcherBase const& lhs, MatchAllOf&& rhs) { + friend MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -11870,11 +11875,15 @@ namespace Matchers { return description; } - friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase const& rhs) { + friend MatchAnyOf operator||( MatchAnyOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAnyOf operator|| (MatcherBase const& lhs, MatchAnyOf&& rhs) { + friend MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -11894,7 +11903,8 @@ namespace Matchers { MatcherBase const& m_underlyingMatcher; public: - explicit MatchNotOf( MatcherBase const& underlyingMatcher ): + explicit MatchNotOf( MatcherBase const& underlyingMatcher + CATCH_ATTR_LIFETIMEBOUND ): m_underlyingMatcher( underlyingMatcher ) {} @@ -11910,16 +11920,22 @@ namespace Matchers { } // namespace Detail template - Detail::MatchAllOf operator&& (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAllOf{} && lhs && rhs; } + template - Detail::MatchAnyOf operator|| (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAnyOf{} || lhs || rhs; } template - Detail::MatchNotOf operator! (MatcherBase const& matcher) { + Detail::MatchNotOf + operator!( MatcherBase const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOf{ matcher }; } @@ -12086,7 +12102,8 @@ namespace Matchers { MatchAllOfGeneric(MatchAllOfGeneric&&) = default; MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} + MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND) + : m_matchers{ {std::addressof(matchers)...} } {} explicit MatchAllOfGeneric(std::array matchers) : m_matchers{matchers} {} template @@ -12108,8 +12125,8 @@ namespace Matchers { template friend MatchAllOfGeneric operator && ( - MatchAllOfGeneric&& lhs, - MatchAllOfGeneric&& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } @@ -12117,8 +12134,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAllOfGeneric> operator && ( - MatchAllOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(&rhs))}; } @@ -12126,8 +12143,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAllOfGeneric> operator && ( - MatcherLHS const& lhs, - MatchAllOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -12141,7 +12158,8 @@ namespace Matchers { MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} + MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND) + : m_matchers{ {std::addressof(matchers)...} } {} explicit MatchAnyOfGeneric(std::array matchers) : m_matchers{matchers} {} template @@ -12162,8 +12180,8 @@ namespace Matchers { //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case template friend MatchAnyOfGeneric operator || ( - MatchAnyOfGeneric&& lhs, - MatchAnyOfGeneric&& rhs) { + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } @@ -12171,8 +12189,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAnyOfGeneric> operator || ( - MatchAnyOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(std::addressof(rhs)))}; } @@ -12180,8 +12198,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAnyOfGeneric> operator || ( - MatcherLHS const& lhs, - MatchAnyOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND) { return MatchAnyOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -12197,7 +12215,8 @@ namespace Matchers { MatchNotOfGeneric(MatchNotOfGeneric&&) = default; MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; - explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {} + explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND) + : m_matcher{matcher} {} template bool match(Arg&& arg) const { @@ -12209,7 +12228,9 @@ namespace Matchers { } //! Negating negation can just unwrap and return underlying matcher - friend MatcherT const& operator ! (MatchNotOfGeneric const& matcher) { + friend MatcherT const& + operator!( MatchNotOfGeneric const& matcher + CATCH_ATTR_LIFETIMEBOUND ) { return matcher.m_matcher; } }; @@ -12219,20 +12240,22 @@ namespace Matchers { // compose only generic matchers template std::enable_if_t, Detail::MatchAllOfGeneric> - operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric> - operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } //! Wrap provided generic matcher in generic negator template std::enable_if_t, Detail::MatchNotOfGeneric> - operator ! (MatcherT const& matcher) { + operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOfGeneric{matcher}; } @@ -12240,25 +12263,29 @@ namespace Matchers { // compose mixed generic and non-generic matchers template std::enable_if_t, Detail::MatchAllOfGeneric>> - operator && (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAllOfGeneric, MatcherRHS>> - operator && (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric>> - operator || (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric, MatcherRHS>> - operator || (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } diff --git a/meson.build b/meson.build index 16b38796..8a50e339 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.11.0', # CML version placeholder, don't delete + version: '3.12.0', # CML version placeholder, don't delete license: 'BSL-1.0', meson_version: '>=0.54.1', ) diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp index 39b0c0be..3b558a37 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, 11, 0, "", 0 ); + static Version version( 3, 12, 0, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 630f5b0e..b78f9cfd 100644 --- a/src/catch2/catch_version_macros.hpp +++ b/src/catch2/catch_version_macros.hpp @@ -9,7 +9,7 @@ #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 11 +#define CATCH_VERSION_MINOR 12 #define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED