summaryrefslogtreecommitdiffstats
path: root/googlemock/include/gmock/gmock-matchers.h
diff options
context:
space:
mode:
Diffstat (limited to 'googlemock/include/gmock/gmock-matchers.h')
-rw-r--r--googlemock/include/gmock/gmock-matchers.h333
1 files changed, 284 insertions, 49 deletions
diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h
index 8f82b27..236d3e5 100644
--- a/googlemock/include/gmock/gmock-matchers.h
+++ b/googlemock/include/gmock/gmock-matchers.h
@@ -257,7 +257,7 @@
#include <algorithm>
#include <cmath>
-#include <exception>
+#include <cstddef>
#include <functional>
#include <initializer_list>
#include <ios>
@@ -267,12 +267,12 @@
#include <ostream> // NOLINT
#include <sstream>
#include <string>
+#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#include "gmock/internal/gmock-internal-utils.h"
-#include "gmock/internal/gmock-port.h"
#include "gmock/internal/gmock-pp.h"
#include "gtest/gtest.h"
@@ -375,11 +375,16 @@ class MatcherCastImpl {
// M can't be implicitly converted to Matcher<T>, so M isn't a polymorphic
// matcher. It's a value of a type implicitly convertible to T. Use direct
- // initialization to create a matcher.
+ // initialization or `ImplicitCastEqMatcher` to create a matcher.
static Matcher<T> CastImpl(const M& value,
std::false_type /* convertible_to_matcher */,
std::true_type /* convertible_to_T */) {
- return Matcher<T>(ImplicitCast_<T>(value));
+ using NoRefT = std::remove_cv_t<std::remove_reference_t<T>>;
+ if constexpr (std::is_same_v<M, NoRefT>) {
+ return Matcher<T>(value);
+ } else {
+ return ImplicitCastEqMatcher<NoRefT, std::decay_t<const M&>>(value);
+ }
}
// M can't be implicitly converted to either Matcher<T> or T. Attempt to use
@@ -390,11 +395,11 @@ class MatcherCastImpl {
// latter calls bool operator==(const Lhs& lhs, const Rhs& rhs) in the end
// which might be undefined even when Rhs is implicitly convertible to Lhs
// (e.g. std::pair<const int, int> vs. std::pair<int, int>).
- //
- // We don't define this method inline as we need the declaration of Eq().
static Matcher<T> CastImpl(const M& value,
std::false_type /* convertible_to_matcher */,
- std::false_type /* convertible_to_T */);
+ std::false_type /* convertible_to_T */) {
+ return Eq(value);
+ }
};
// This more specialized version is used when MatcherCast()'s argument
@@ -408,13 +413,22 @@ class MatcherCastImpl<T, Matcher<U>> {
}
private:
- class Impl : public MatcherInterface<T> {
+ // If it's possible to implicitly convert a `const T&` to U, then `Impl` can
+ // take that as input to avoid a copy. Otherwise, such as when `T` is a
+ // non-const reference type or a type explicitly constructible only from a
+ // non-const reference, then `Impl` must use `T` as-is (potentially copying).
+ using ImplArgT =
+ typename std::conditional<std::is_convertible<const T&, const U&>::value,
+ const T&, T>::type;
+
+ class Impl : public MatcherInterface<ImplArgT> {
public:
explicit Impl(const Matcher<U>& source_matcher)
: source_matcher_(source_matcher) {}
// We delegate the matching logic to the source matcher.
- bool MatchAndExplain(T x, MatchResultListener* listener) const override {
+ bool MatchAndExplain(ImplArgT x,
+ MatchResultListener* listener) const override {
using FromType = typename std::remove_cv<typename std::remove_pointer<
typename std::remove_reference<T>::type>::type>::type;
using ToType = typename std::remove_cv<typename std::remove_pointer<
@@ -431,9 +445,8 @@ class MatcherCastImpl<T, Matcher<U>> {
// Do the cast to `U` explicitly if necessary.
// Otherwise, let implicit conversions do the trick.
- using CastType =
- typename std::conditional<std::is_convertible<T&, const U&>::value,
- T&, U>::type;
+ using CastType = typename std::conditional<
+ std::is_convertible<ImplArgT&, const U&>::value, ImplArgT&, U>::type;
return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
listener);
@@ -528,18 +541,16 @@ inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher_or_value) {
// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
// contravariant): just keep a copy of the original Matcher<U>, convert the
// argument from type T to U, and then pass it to the underlying Matcher<U>.
-// The only exception is when U is a reference and T is not, as the
+// The only exception is when U is a non-const reference and T is not, as the
// underlying Matcher<U> may be interested in the argument's address, which
-// is not preserved in the conversion from T to U.
+// cannot be preserved in the conversion from T to U (since a copy of the input
+// T argument would be required to provide a non-const reference U).
template <typename T, typename U>
inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
// Enforce that T can be implicitly converted to U.
static_assert(std::is_convertible<const T&, const U&>::value,
- "T must be implicitly convertible to U");
- // Enforce that we are not converting a non-reference type T to a reference
- // type U.
- static_assert(std::is_reference<T>::value || !std::is_reference<U>::value,
- "cannot convert non reference arg to reference");
+ "T must be implicitly convertible to U (and T must be a "
+ "non-const reference if U is a non-const reference)");
// In case both T and U are arithmetic types, enforce that the
// conversion is not lossy.
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
@@ -1305,6 +1316,15 @@ class AllOfMatcherImpl : public MatcherInterface<const T&> {
bool MatchAndExplain(const T& x,
MatchResultListener* listener) const override {
+ if (!listener->IsInterested()) {
+ // Fast path to avoid unnecessary formatting.
+ for (const Matcher<T>& matcher : matchers_) {
+ if (!matcher.Matches(x)) {
+ return false;
+ }
+ }
+ return true;
+ }
// This method uses matcher's explanation when explaining the result.
// However, if matcher doesn't provide one, this method uses matcher's
// description.
@@ -1424,6 +1444,15 @@ class AnyOfMatcherImpl : public MatcherInterface<const T&> {
bool MatchAndExplain(const T& x,
MatchResultListener* listener) const override {
+ if (!listener->IsInterested()) {
+ // Fast path to avoid unnecessary formatting of match explanations.
+ for (const Matcher<T>& matcher : matchers_) {
+ if (matcher.Matches(x)) {
+ return true;
+ }
+ }
+ return false;
+ }
// This method uses matcher's explanation when explaining the result.
// However, if matcher doesn't provide one, this method uses matcher's
// description.
@@ -2091,11 +2120,11 @@ class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> {
template <typename Class, typename FieldType>
class FieldMatcher {
public:
- FieldMatcher(FieldType Class::*field,
+ FieldMatcher(FieldType Class::* field,
const Matcher<const FieldType&>& matcher)
: field_(field), matcher_(matcher), whose_field_("whose given field ") {}
- FieldMatcher(const std::string& field_name, FieldType Class::*field,
+ FieldMatcher(const std::string& field_name, FieldType Class::* field,
const Matcher<const FieldType&>& matcher)
: field_(field),
matcher_(matcher),
@@ -2139,7 +2168,7 @@ class FieldMatcher {
return MatchAndExplainImpl(std::false_type(), *p, listener);
}
- const FieldType Class::*field_;
+ const FieldType Class::* field_;
const Matcher<const FieldType&> matcher_;
// Contains either "whose given field " if the name of the field is unknown
@@ -2275,6 +2304,9 @@ class ResultOfMatcher {
class Impl : public MatcherInterface<T> {
using ResultType = decltype(CallableTraits<Callable>::template Invoke<T>(
std::declval<CallableStorageType>(), std::declval<T>()));
+ using InnerType = std::conditional_t<
+ std::is_lvalue_reference<ResultType>::value,
+ const typename std::remove_reference<ResultType>::type&, ResultType>;
public:
template <typename M>
@@ -2282,7 +2314,7 @@ class ResultOfMatcher {
const CallableStorageType& callable, const M& matcher)
: result_description_(result_description),
callable_(callable),
- matcher_(MatcherCast<ResultType>(matcher)) {}
+ matcher_(MatcherCast<InnerType>(matcher)) {}
void DescribeTo(::std::ostream* os) const override {
if (result_description_.empty()) {
@@ -2312,7 +2344,7 @@ class ResultOfMatcher {
// takes a non-const reference as argument.
// Also, specifying template argument explicitly is needed because T could
// be a non-const reference (e.g. Matcher<Uncopyable&>).
- ResultType result =
+ InnerType result =
CallableTraits<Callable>::template Invoke<T>(callable_, obj);
return MatchPrintAndExplain(result, matcher_, listener);
}
@@ -2325,7 +2357,7 @@ class ResultOfMatcher {
// use stateful callables with ResultOf(), which doesn't guarantee
// how many times the callable will be invoked.
mutable CallableStorageType callable_;
- const Matcher<ResultType> matcher_;
+ const Matcher<InnerType> matcher_;
}; // class Impl
const std::string result_description_;
@@ -2846,6 +2878,54 @@ class ContainsMatcherImpl : public QuantifierMatcherImpl<Container> {
}
};
+// Implements DistanceFrom(target, get_distance, distance_matcher) for the given
+// argument types:
+// * V is the type of the value to be matched.
+// * T is the type of the target value.
+// * Distance is the type of the distance between V and T.
+// * GetDistance is the type of the functor for computing the distance between
+// V and T.
+template <typename V, typename T, typename Distance, typename GetDistance>
+class DistanceFromMatcherImpl : public MatcherInterface<V> {
+ public:
+ // Arguments:
+ // * target: the target value.
+ // * get_distance: the functor for computing the distance between the value
+ // being matched and target.
+ // * distance_matcher: the matcher for checking the distance.
+ DistanceFromMatcherImpl(T target, GetDistance get_distance,
+ Matcher<const Distance&> distance_matcher)
+ : target_(std::move(target)),
+ get_distance_(std::move(get_distance)),
+ distance_matcher_(std::move(distance_matcher)) {}
+
+ // Describes what this matcher does.
+ void DescribeTo(::std::ostream* os) const override {
+ distance_matcher_.DescribeTo(os);
+ *os << " away from " << PrintToString(target_);
+ }
+
+ void DescribeNegationTo(::std::ostream* os) const override {
+ distance_matcher_.DescribeNegationTo(os);
+ *os << " away from " << PrintToString(target_);
+ }
+
+ bool MatchAndExplain(V value, MatchResultListener* listener) const override {
+ const auto distance = get_distance_(value, target_);
+ const bool match = distance_matcher_.Matches(distance);
+ if (!match && listener->IsInterested()) {
+ *listener << "which is " << PrintToString(distance) << " away from "
+ << PrintToString(target_);
+ }
+ return match;
+ }
+
+ private:
+ const T target_;
+ const GetDistance get_distance_;
+ const Matcher<const Distance&> distance_matcher_;
+};
+
// Implements Each(element_matcher) for the given argument type Container.
// Symmetric to ContainsMatcherImpl.
template <typename Container>
@@ -2981,6 +3061,52 @@ auto Second(T& x, Rank1) -> decltype((x.second)) { // NOLINT
}
} // namespace pair_getters
+// Default functor for computing the distance between two values.
+struct DefaultGetDistance {
+ template <typename T, typename U>
+ auto operator()(const T& lhs, const U& rhs) const {
+ using std::abs;
+ // Allow finding abs() in the type's namespace via ADL.
+ return abs(lhs - rhs);
+ }
+};
+
+// Implements polymorphic DistanceFrom(target, get_distance, distance_matcher)
+// matcher. Template arguments:
+// * T is the type of the target value.
+// * GetDistance is the type of the functor for computing the distance between
+// the value being matched and the target.
+// * DistanceMatcher is the type of the matcher for checking the distance.
+template <typename T, typename GetDistance, typename DistanceMatcher>
+class DistanceFromMatcher {
+ public:
+ // Arguments:
+ // * target: the target value.
+ // * get_distance: the functor for computing the distance between the value
+ // being matched and target.
+ // * distance_matcher: the matcher for checking the distance.
+ DistanceFromMatcher(T target, GetDistance get_distance,
+ DistanceMatcher distance_matcher)
+ : target_(std::move(target)),
+ get_distance_(std::move(get_distance)),
+ distance_matcher_(std::move(distance_matcher)) {}
+
+ DistanceFromMatcher(const DistanceFromMatcher& other) = default;
+
+ // Implicitly converts to a monomorphic matcher of the given type.
+ template <typename V>
+ operator Matcher<V>() const { // NOLINT
+ using Distance = decltype(get_distance_(std::declval<V>(), target_));
+ return Matcher<V>(new DistanceFromMatcherImpl<V, T, Distance, GetDistance>(
+ target_, get_distance_, distance_matcher_));
+ }
+
+ private:
+ const T target_;
+ const GetDistance get_distance_;
+ const DistanceMatcher distance_matcher_;
+};
+
// Implements Key(inner_matcher) for the given argument pair type.
// Key(inner_matcher) matches an std::pair whose 'first' field matches
// inner_matcher. For example, Contains(Key(Ge(5))) can be used to match an
@@ -3188,8 +3314,8 @@ class PairMatcher {
};
template <typename T, size_t... I>
-auto UnpackStructImpl(const T& t, std::index_sequence<I...>,
- int) -> decltype(std::tie(get<I>(t)...)) {
+auto UnpackStructImpl(const T& t, std::index_sequence<I...>, int)
+ -> decltype(std::tie(get<I>(t)...)) {
static_assert(std::tuple_size<T>::value == sizeof...(I),
"Number of arguments doesn't match the number of fields.");
return std::tie(get<I>(t)...);
@@ -3296,6 +3422,50 @@ auto UnpackStructImpl(const T& u, std::make_index_sequence<20>, char) {
const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t] = u;
return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t);
}
+template <typename T>
+auto UnpackStructImpl(const T& in, std::make_index_sequence<21>, char) {
+ const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u] =
+ in;
+ return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t,
+ u);
+}
+
+template <typename T>
+auto UnpackStructImpl(const T& in, std::make_index_sequence<22>, char) {
+ const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
+ v] = in;
+ return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
+ v);
+}
+
+template <typename T>
+auto UnpackStructImpl(const T& in, std::make_index_sequence<23>, char) {
+ const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v,
+ w] = in;
+ return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
+ v, w);
+}
+template <typename T>
+auto UnpackStructImpl(const T& in, std::make_index_sequence<24>, char) {
+ const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v,
+ w, x] = in;
+ return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
+ v, w, x);
+}
+template <typename T>
+auto UnpackStructImpl(const T& in, std::make_index_sequence<25>, char) {
+ const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v,
+ w, x, y] = in;
+ return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
+ v, w, x, y);
+}
+template <typename T>
+auto UnpackStructImpl(const T& in, std::make_index_sequence<26>, char) {
+ const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v,
+ w, x, y, z] = in;
+ return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u,
+ v, w, x, y, z);
+}
#endif // defined(__cpp_structured_bindings)
template <size_t I, typename T>
@@ -3471,7 +3641,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
StlContainerReference stl_container = View::ConstReference(container);
auto it = stl_container.begin();
size_t exam_pos = 0;
- bool mismatch_found = false; // Have we found a mismatched element yet?
+ bool unmatched_found = false;
// Go through the elements and matchers in pairs, until we reach
// the end of either the elements or the matchers, or until we find a
@@ -3487,11 +3657,23 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
}
if (!match) {
- mismatch_found = true;
+ unmatched_found = true;
+ // We cannot store the iterator for the unmatched element to be used
+ // later, as some users use ElementsAre() with a "container" whose
+ // iterator is not copy-constructible or copy-assignable.
+ //
+ // We cannot store a pointer to the element either, as some container's
+ // iterators return a temporary.
+ //
+ // We cannot store the element itself either, as the element may not be
+ // copyable.
+ //
+ // Therefore, we just remember the index of the unmatched element,
+ // and use it later to print the unmatched element.
break;
}
}
- // If mismatch_found is true, 'exam_pos' is the index of the mismatch.
+ // If unmatched_found is true, exam_pos is the index of the mismatch.
// Find how many elements the actual container has. We avoid
// calling size() s.t. this code works for stream-like "containers"
@@ -3512,10 +3694,27 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
return false;
}
- if (mismatch_found) {
+ if (unmatched_found) {
// The element count matches, but the exam_pos-th element doesn't match.
if (listener_interested) {
- *listener << "whose element #" << exam_pos << " doesn't match";
+ // Find the unmatched element.
+ auto unmatched_it = stl_container.begin();
+ // We cannot call std::advance() on the iterator, as some users use
+ // ElementsAre() with a "container" whose iterator is incompatible with
+ // std::advance() (e.g. it may not have the difference_type member
+ // type).
+ for (size_t i = 0; i != exam_pos; ++i) {
+ ++unmatched_it;
+ }
+
+ // If the array is long or the elements' print-out is large, it may be
+ // hard for the user to find the mismatched element and its
+ // corresponding matcher description. Therefore we print the index, the
+ // value of the mismatched element, and the corresponding matcher
+ // description to ease debugging.
+ *listener << "whose element #" << exam_pos << " ("
+ << PrintToString(*unmatched_it) << ") ";
+ matchers_[exam_pos].DescribeNegationTo(listener->stream());
PrintIfNotEmpty(explanations[exam_pos], listener->stream());
}
return false;
@@ -3921,15 +4120,15 @@ GTEST_API_ std::string FormatMatcherDescription(
// Overloads to support `OptionalMatcher` being used with a type that either
// supports implicit conversion to bool or a `has_value()` method.
template <typename Optional>
-auto IsOptionalEngaged(const Optional& optional,
- Rank1) -> decltype(!!optional) {
+auto IsOptionalEngaged(const Optional& optional, Rank1)
+ -> decltype(!!optional) {
// The use of double-negation here is to preserve historical behavior where
// the matcher used `operator!` rather than directly using `operator bool`.
return !static_cast<bool>(!optional);
}
template <typename Optional>
-auto IsOptionalEngaged(const Optional& optional,
- Rank0) -> decltype(!optional.has_value()) {
+auto IsOptionalEngaged(const Optional& optional, Rank0)
+ -> decltype(!optional.has_value()) {
return optional.has_value();
}
@@ -3970,6 +4169,10 @@ class OptionalMatcher {
return false;
}
const ValueType& value = *optional;
+ if (!listener->IsInterested()) {
+ // Fast path to avoid unnecessary generation of match explanation.
+ return value_matcher_.Matches(value);
+ }
StringMatchResultListener value_listener;
const bool match = value_matcher_.MatchAndExplain(value, &value_listener);
*listener << "whose value " << PrintToString(value)
@@ -4305,13 +4508,6 @@ inline Matcher<T> An() {
return _;
}
-template <typename T, typename M>
-Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl(
- const M& value, std::false_type /* convertible_to_matcher */,
- std::false_type /* convertible_to_T */) {
- return Eq(value);
-}
-
// Creates a polymorphic matcher that matches any NULL pointer.
inline PolymorphicMatcher<internal::IsNullMatcher> IsNull() {
return MakePolymorphicMatcher(internal::IsNullMatcher());
@@ -4356,6 +4552,42 @@ inline internal::FloatingEqMatcher<double> DoubleNear(double rhs,
return internal::FloatingEqMatcher<double>(rhs, false, max_abs_error);
}
+// The DistanceFrom(target, get_distance, m) and DistanceFrom(target, m)
+// matchers work on arbitrary types that have the "distance" concept. What they
+// do:
+//
+// 1. compute the distance between the value and the target using
+// get_distance(value, target) if get_distance is provided; otherwise compute
+// the distance as abs(value - target).
+// 2. match the distance against the user-provided matcher m; if the match
+// succeeds, the DistanceFrom() match succeeds.
+//
+// Examples:
+//
+// // 0.5's distance from 0.6 should be <= 0.2.
+// EXPECT_THAT(0.5, DistanceFrom(0.6, Le(0.2)));
+//
+// Vector2D v1(3.0, 4.0), v2(3.2, 6.0);
+// // v1's distance from v2, as computed by EuclideanDistance(v1, v2),
+// // should be >= 1.0.
+// EXPECT_THAT(v1, DistanceFrom(v2, EuclideanDistance, Ge(1.0)));
+
+template <typename T, typename GetDistance, typename DistanceMatcher>
+inline internal::DistanceFromMatcher<T, GetDistance, DistanceMatcher>
+DistanceFrom(T target, GetDistance get_distance,
+ DistanceMatcher distance_matcher) {
+ return internal::DistanceFromMatcher<T, GetDistance, DistanceMatcher>(
+ std::move(target), std::move(get_distance), std::move(distance_matcher));
+}
+
+template <typename T, typename DistanceMatcher>
+inline internal::DistanceFromMatcher<T, internal::DefaultGetDistance,
+ DistanceMatcher>
+DistanceFrom(T target, DistanceMatcher distance_matcher) {
+ return DistanceFrom(std::move(target), internal::DefaultGetDistance(),
+ std::move(distance_matcher));
+}
+
// Creates a matcher that matches any double argument approximately equal to
// rhs, up to the specified max absolute error bound, including NaN values when
// rhs is NaN. The max absolute error bound must be non-negative.
@@ -4421,7 +4653,7 @@ WhenDynamicCastTo(const Matcher<To>& inner_matcher) {
// matches a Foo object x if and only if x.number >= 5.
template <typename Class, typename FieldType, typename FieldMatcher>
inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
- FieldType Class::*field, const FieldMatcher& matcher) {
+ FieldType Class::* field, const FieldMatcher& matcher) {
return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
field, MatcherCast<const FieldType&>(matcher)));
// The call to MatcherCast() is required for supporting inner
@@ -4434,7 +4666,7 @@ inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
// messages.
template <typename Class, typename FieldType, typename FieldMatcher>
inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
- const std::string& field_name, FieldType Class::*field,
+ const std::string& field_name, FieldType Class::* field,
const FieldMatcher& matcher) {
return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
field_name, field, MatcherCast<const FieldType&>(matcher)));
@@ -4444,6 +4676,10 @@ inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType>> Field(
// matches 'matcher'. For example,
// Property(&Foo::str, StartsWith("hi"))
// matches a Foo object x if and only if x.str() starts with "hi".
+//
+// Warning: Don't use `Property()` against member functions that you do not
+// own, because taking addresses of functions is fragile and generally not part
+// of the contract of the function.
template <typename Class, typename PropertyType, typename PropertyMatcher>
inline PolymorphicMatcher<internal::PropertyMatcher<
Class, PropertyType, PropertyType (Class::*)() const>>
@@ -5542,8 +5778,7 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
template <typename arg_type> \
bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain( \
const arg_type& arg, \
- GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED ::testing::MatchResultListener* \
- result_listener) const
+ [[maybe_unused]] ::testing::MatchResultListener* result_listener) const
#define MATCHER_P(name, p0, description) \
GMOCK_INTERNAL_MATCHER(name, name##MatcherP, description, (#p0), (p0))
@@ -5628,8 +5863,8 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
bool full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>:: \
gmock_Impl<arg_type>::MatchAndExplain( \
const arg_type& arg, \
- GTEST_INTERNAL_ATTRIBUTE_MAYBE_UNUSED ::testing:: \
- MatchResultListener* result_listener) const
+ [[maybe_unused]] ::testing::MatchResultListener* result_listener) \
+ const
#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args) \
GMOCK_PP_TAIL( \