Compare commits

...

7 Commits

Author SHA1 Message Date
ZXShady
33e6fd217a Remove recursion when stringifying std::tuple 2025-10-04 22:10:36 +02:00
ZXShady
a58df2d7c5 Outline part of formatting system_clock's time_point into cpp file 2025-10-04 22:10:36 +02:00
ZXShady
a9223b2bb3 Outline catch_strnlen's definition into catch_tostring.cpp 2025-10-04 22:10:36 +02:00
Martin Hořeňovský
363ca5af18 Add lifetime annotations to more places using StringRef 2025-10-04 16:38:07 +02:00
Martin Hořeňovský
cb6d713774 Add lifetimebound annotation to StringRef 2025-10-04 16:12:17 +02:00
Martin Hořeňovský
8e4ab5dd8f Annotate matcher combinators with CATCH_ATTR_LIFETIMEBOUND
The matcher combinators do not take ownership of the matchers
being combined, which can catch the users off-guard, when code like
this

```cpp
using Catch::Matchers::EndsWith;
using Catch::Matchers::ContainsSubstring;

auto combinedMatcher = EndsWith("as a service")
                       && ContainsSubstring("web scale");

REQUIRE_THAT( getSomeString(), combinedMatcher );
```

leads to use-after-free, as the `combinedMatcher` refers to matcher
temporaries that no longer exists. With this commit, users of Clang,
MSVC or other compiler that understands the `lifetimebound` attribute,
should get a warning.
2025-10-03 22:27:29 +02:00
Martin Hořeňovský
8219ed79f2 Add CATCH_ATTR_LIFETIMEBOUND macro polyfill over lifetimebound attr 2025-10-03 22:15:27 +02:00
11 changed files with 156 additions and 97 deletions

View File

@@ -98,6 +98,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_jsonwriter.hpp ${SOURCES_DIR}/internal/catch_jsonwriter.hpp
${SOURCES_DIR}/internal/catch_lazy_expr.hpp ${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp ${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_lifetimebound.hpp
${SOURCES_DIR}/internal/catch_list.hpp ${SOURCES_DIR}/internal/catch_list.hpp
${SOURCES_DIR}/internal/catch_logical_traits.hpp ${SOURCES_DIR}/internal/catch_logical_traits.hpp
${SOURCES_DIR}/internal/catch_message_info.hpp ${SOURCES_DIR}/internal/catch_message_info.hpp

View File

@@ -79,6 +79,7 @@
#include <catch2/internal/catch_jsonwriter.hpp> #include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_lazy_expr.hpp> #include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp> #include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_list.hpp> #include <catch2/internal/catch_list.hpp>
#include <catch2/internal/catch_logical_traits.hpp> #include <catch2/internal/catch_logical_traits.hpp>
#include <catch2/internal/catch_message_info.hpp> #include <catch2/internal/catch_message_info.hpp>

View File

@@ -57,6 +57,36 @@ namespace Detail {
} }
} // end unnamed namespace } // end unnamed namespace
std::size_t catch_strnlen( const char* str, std::size_t n ) {
auto ret = std::char_traits<char>::find( str, n, '\0' );
if ( ret != nullptr ) { return static_cast<std::size_t>( 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 convertIntoString(StringRef string, bool escapeInvisibles) {
std::string ret; std::string ret;
// This is enough for the "don't escape invisibles" case, and a good // This is enough for the "don't escape invisibles" case, and a good

View File

@@ -8,7 +8,7 @@
#ifndef CATCH_TOSTRING_HPP_INCLUDED #ifndef CATCH_TOSTRING_HPP_INCLUDED
#define CATCH_TOSTRING_HPP_INCLUDED #define CATCH_TOSTRING_HPP_INCLUDED
#include <ctime>
#include <vector> #include <vector>
#include <cstddef> #include <cstddef>
#include <type_traits> #include <type_traits>
@@ -40,13 +40,9 @@ namespace Catch {
namespace Detail { namespace Detail {
inline std::size_t catch_strnlen(const char *str, std::size_t n) { std::size_t catch_strnlen(const char *str, std::size_t n);
auto ret = std::char_traits<char>::find(str, n, '\0');
if (ret != nullptr) { std::string formatTimeT( std::time_t time );
return static_cast<std::size_t>(ret - str);
}
return n;
}
constexpr StringRef unprintableString = "{?}"_sr; constexpr StringRef unprintableString = "{?}"_sr;
@@ -411,44 +407,38 @@ namespace Catch {
// Separate std::tuple specialization // Separate std::tuple specialization
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
#include <tuple> # include <tuple>
# include <utility>
namespace Catch { namespace Catch {
namespace Detail { namespace Detail {
template< template <typename Tuple, std::size_t... Is>
typename Tuple, void PrintTuple( const Tuple& tuple,
std::size_t N = 0, std::ostream& os,
bool = (N < std::tuple_size<Tuple>::value) std::index_sequence<Is...> ) {
> // 1 + Account for when the tuple is empty
struct TupleElementPrinter { char a[1 + sizeof...( Is )] = {
static void print(const Tuple& tuple, std::ostream& os) { ( ( os << ( Is ? ", " : " " )
os << (N ? ", " : " ") << ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
<< ::Catch::Detail::stringify(std::get<N>(tuple)); '\0' )... };
TupleElementPrinter<Tuple, N + 1>::print(tuple, os); (void)a;
} }
};
template< } // namespace Detail
typename Tuple,
std::size_t N
>
struct TupleElementPrinter<Tuple, N, false> {
static void print(const Tuple&, std::ostream&) {}
};
} template <typename... Types>
template<typename ...Types>
struct StringMaker<std::tuple<Types...>> { struct StringMaker<std::tuple<Types...>> {
static std::string convert(const std::tuple<Types...>& tuple) { static std::string convert( const std::tuple<Types...>& tuple ) {
ReusableStringStream rss; ReusableStringStream rss;
rss << '{'; rss << '{';
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get()); Detail::PrintTuple(
tuple,
rss.get(),
std::make_index_sequence<sizeof...( Types )>{} );
rss << " }"; rss << " }";
return rss.str(); return rss.str();
} }
}; };
} } // namespace Catch
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
@@ -635,28 +625,7 @@ struct ratio_string<std::milli> {
const auto systemish = std::chrono::time_point_cast< const auto systemish = std::chrono::time_point_cast<
std::chrono::system_clock::duration>( time_point ); std::chrono::system_clock::duration>( time_point );
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish ); const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
return ::Catch::Detail::formatTimeT( as_time_t );
#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);
} }
}; };
} }

View File

@@ -0,0 +1,24 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#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

View File

@@ -8,6 +8,7 @@
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED #ifndef CATCH_STRING_MANIP_HPP_INCLUDED
#define CATCH_STRING_MANIP_HPP_INCLUDED #define CATCH_STRING_MANIP_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
#include <cstdint> #include <cstdint>
@@ -28,10 +29,10 @@ namespace Catch {
//! Returns a new string without whitespace at the start/end //! Returns a new string without whitespace at the start/end
std::string trim( std::string const& str ); std::string trim( std::string const& str );
//! Returns a substring of the original ref without whitespace. Beware lifetimes! //! 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 // !!! Be aware, returns refs into original string - make sure original string outlives them
std::vector<StringRef> splitStringRef( StringRef str, char delimiter ); std::vector<StringRef> splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter );
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
/** /**
@@ -49,7 +50,7 @@ namespace Catch {
StringRef m_label; StringRef m_label;
public: public:
constexpr pluralise(std::uint64_t count, StringRef label): constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND):
m_count(count), m_count(count),
m_label(label) m_label(label)
{} {}

View File

@@ -8,11 +8,12 @@
#ifndef CATCH_STRINGREF_HPP_INCLUDED #ifndef CATCH_STRINGREF_HPP_INCLUDED
#define CATCH_STRINGREF_HPP_INCLUDED #define CATCH_STRINGREF_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <iosfwd> #include <iosfwd>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
namespace Catch { namespace Catch {
@@ -36,14 +37,16 @@ namespace Catch {
public: // construction public: // construction
constexpr StringRef() noexcept = default; 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_start( rawChars ),
m_size( size ) m_size( size )
{} {}
StringRef( std::string const& stdString ) noexcept StringRef(
std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept
: m_start( stdString.c_str() ), : m_start( stdString.c_str() ),
m_size( stdString.size() ) m_size( stdString.size() )
{} {}
@@ -89,7 +92,7 @@ namespace Catch {
} }
// Returns the current start pointer. May not be null-terminated. // 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; return m_start;
} }

View File

@@ -8,6 +8,7 @@
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_source_line_info.hpp> #include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_unique_ptr.hpp> #include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
@@ -48,7 +49,7 @@ namespace TestCaseTracking {
StringRef name; StringRef name;
SourceLineInfo location; SourceLineInfo location;
constexpr NameAndLocationRef( StringRef name_, constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND,
SourceLineInfo location_ ): SourceLineInfo location_ ):
name( name_ ), location( location_ ) {} name( name_ ), location( location_ ) {}

View File

@@ -10,6 +10,7 @@
#include <catch2/matchers/internal/catch_matchers_impl.hpp> #include <catch2/matchers/internal/catch_matchers_impl.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -79,11 +80,15 @@ namespace Matchers {
return description; return description;
} }
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) { friend MatchAllOf operator&&( MatchAllOf&& lhs,
MatcherBase<ArgT> const& rhs
CATCH_ATTR_LIFETIMEBOUND ) {
lhs.m_matchers.push_back(&rhs); lhs.m_matchers.push_back(&rhs);
return CATCH_MOVE(lhs); return CATCH_MOVE(lhs);
} }
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) { friend MatchAllOf
operator&&( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOf&& rhs ) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
return CATCH_MOVE(rhs); return CATCH_MOVE(rhs);
} }
@@ -131,11 +136,15 @@ namespace Matchers {
return description; return description;
} }
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) { friend MatchAnyOf operator||( MatchAnyOf&& lhs,
MatcherBase<ArgT> const& rhs
CATCH_ATTR_LIFETIMEBOUND ) {
lhs.m_matchers.push_back(&rhs); lhs.m_matchers.push_back(&rhs);
return CATCH_MOVE(lhs); return CATCH_MOVE(lhs);
} }
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) { friend MatchAnyOf
operator||( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOf&& rhs ) {
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
return CATCH_MOVE(rhs); return CATCH_MOVE(rhs);
} }
@@ -155,7 +164,8 @@ namespace Matchers {
MatcherBase<ArgT> const& m_underlyingMatcher; MatcherBase<ArgT> const& m_underlyingMatcher;
public: public:
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ): explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher
CATCH_ATTR_LIFETIMEBOUND ):
m_underlyingMatcher( underlyingMatcher ) m_underlyingMatcher( underlyingMatcher )
{} {}
@@ -171,16 +181,22 @@ namespace Matchers {
} // namespace Detail } // namespace Detail
template <typename T> template <typename T>
Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) { Detail::MatchAllOf<T>
operator&&( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchAllOf<T>{} && lhs && rhs; return Detail::MatchAllOf<T>{} && lhs && rhs;
} }
template <typename T> template <typename T>
Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) { Detail::MatchAnyOf<T>
operator||( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchAnyOf<T>{} || lhs || rhs; return Detail::MatchAnyOf<T>{} || lhs || rhs;
} }
template <typename T> template <typename T>
Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) { Detail::MatchNotOf<T>
operator!( MatcherBase<T> const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchNotOf<T>{ matcher }; return Detail::MatchNotOf<T>{ matcher };
} }

View File

@@ -11,6 +11,7 @@
#include <catch2/matchers/catch_matchers.hpp> #include <catch2/matchers/catch_matchers.hpp>
#include <catch2/internal/catch_stringref.hpp> #include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_logical_traits.hpp> #include <catch2/internal/catch_logical_traits.hpp>
#include <array> #include <array>
@@ -114,7 +115,8 @@ namespace Matchers {
MatchAllOfGeneric(MatchAllOfGeneric&&) = default; MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
MatchAllOfGeneric& operator=(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<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {} explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
template<typename Arg> template<typename Arg>
@@ -136,8 +138,8 @@ namespace Matchers {
template<typename... MatchersRHS> template<typename... MatchersRHS>
friend friend
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && ( MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs, MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOfGeneric<MatchersRHS...>&& rhs) { MatchAllOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
} }
@@ -145,8 +147,8 @@ namespace Matchers {
template<typename MatcherRHS> template<typename MatcherRHS>
friend std::enable_if_t<is_matcher_v<MatcherRHS>, friend std::enable_if_t<is_matcher_v<MatcherRHS>,
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && ( MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs, MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs) { MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))}; return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
} }
@@ -154,8 +156,8 @@ namespace Matchers {
template<typename MatcherLHS> template<typename MatcherLHS>
friend std::enable_if_t<is_matcher_v<MatcherLHS>, friend std::enable_if_t<is_matcher_v<MatcherLHS>,
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && ( MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
MatcherLHS const& lhs, MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAllOfGeneric<MatcherTs...>&& rhs) { MatchAllOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
} }
}; };
@@ -169,7 +171,8 @@ namespace Matchers {
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default; MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
MatchAnyOfGeneric& operator=(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<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {} explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
template<typename Arg> template<typename Arg>
@@ -190,8 +193,8 @@ namespace Matchers {
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
template<typename... MatchersRHS> template<typename... MatchersRHS>
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || ( friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs, MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOfGeneric<MatchersRHS...>&& rhs) { MatchAnyOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
} }
@@ -199,8 +202,8 @@ namespace Matchers {
template<typename MatcherRHS> template<typename MatcherRHS>
friend std::enable_if_t<is_matcher_v<MatcherRHS>, friend std::enable_if_t<is_matcher_v<MatcherRHS>,
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || ( MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs, MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs) { MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))}; return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
} }
@@ -208,8 +211,8 @@ namespace Matchers {
template<typename MatcherLHS> template<typename MatcherLHS>
friend std::enable_if_t<is_matcher_v<MatcherLHS>, friend std::enable_if_t<is_matcher_v<MatcherLHS>,
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || ( MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
MatcherLHS const& lhs, MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatchAnyOfGeneric<MatcherTs...>&& rhs) { MatchAnyOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND) {
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
} }
}; };
@@ -225,7 +228,8 @@ namespace Matchers {
MatchNotOfGeneric(MatchNotOfGeneric&&) = default; MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
MatchNotOfGeneric& operator=(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<typename Arg> template<typename Arg>
bool match(Arg&& arg) const { bool match(Arg&& arg) const {
@@ -237,7 +241,9 @@ namespace Matchers {
} }
//! Negating negation can just unwrap and return underlying matcher //! Negating negation can just unwrap and return underlying matcher
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) { friend MatcherT const&
operator!( MatchNotOfGeneric<MatcherT> const& matcher
CATCH_ATTR_LIFETIMEBOUND ) {
return matcher.m_matcher; return matcher.m_matcher;
} }
}; };
@@ -247,20 +253,22 @@ namespace Matchers {
// compose only generic matchers // compose only generic matchers
template<typename MatcherLHS, typename MatcherRHS> template<typename MatcherLHS, typename MatcherRHS>
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>> std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs }; return { lhs, rhs };
} }
template<typename MatcherLHS, typename MatcherRHS> template<typename MatcherLHS, typename MatcherRHS>
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>> std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs }; return { lhs, rhs };
} }
//! Wrap provided generic matcher in generic negator //! Wrap provided generic matcher in generic negator
template<typename MatcherT> template<typename MatcherT>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>> std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
operator ! (MatcherT const& matcher) { operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
return Detail::MatchNotOfGeneric<MatcherT>{matcher}; return Detail::MatchNotOfGeneric<MatcherT>{matcher};
} }
@@ -268,25 +276,29 @@ namespace Matchers {
// compose mixed generic and non-generic matchers // compose mixed generic and non-generic matchers
template<typename MatcherLHS, typename ArgRHS> template<typename MatcherLHS, typename ArgRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>> std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) { operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs }; return { lhs, rhs };
} }
template<typename ArgLHS, typename MatcherRHS> template<typename ArgLHS, typename MatcherRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>> std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) { operator&&( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs }; return { lhs, rhs };
} }
template<typename MatcherLHS, typename ArgRHS> template<typename MatcherLHS, typename ArgRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>> std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) { operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs }; return { lhs, rhs };
} }
template<typename ArgLHS, typename MatcherRHS> template<typename ArgLHS, typename MatcherRHS>
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>> std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) { operator||( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
return { lhs, rhs }; return { lhs, rhs };
} }

View File

@@ -105,6 +105,7 @@ internal_headers = [
'internal/catch_jsonwriter.hpp', 'internal/catch_jsonwriter.hpp',
'internal/catch_lazy_expr.hpp', 'internal/catch_lazy_expr.hpp',
'internal/catch_leak_detector.hpp', 'internal/catch_leak_detector.hpp',
'internal/catch_lifetimebound.hpp',
'internal/catch_list.hpp', 'internal/catch_list.hpp',
'internal/catch_logical_traits.hpp', 'internal/catch_logical_traits.hpp',
'internal/catch_message_info.hpp', 'internal/catch_message_info.hpp',