mirror of
https://github.com/google/googletest.git
synced 2025-12-06 08:46:50 +08:00
In MatcherCast, store the input value as its own type rather than as the Matcher type, to avoid dangling references
PiperOrigin-RevId: 769240838 Change-Id: I7b1ac23a0a88ba90b5d1ae6e20b97f679f381f31
This commit is contained in:
parent
28e9d1f267
commit
6230d316e1
@ -376,11 +376,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
|
||||
@ -391,11 +396,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
|
||||
@ -4483,13 +4488,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());
|
||||
|
||||
@ -622,15 +622,42 @@ struct IntReferenceWrapper {
|
||||
const int* value;
|
||||
};
|
||||
|
||||
// Compared the contained values
|
||||
bool operator==(const IntReferenceWrapper& a, const IntReferenceWrapper& b) {
|
||||
return a.value == b.value;
|
||||
return *a.value == *b.value;
|
||||
}
|
||||
|
||||
TEST(MatcherCastTest, ValueIsNotCopied) {
|
||||
TEST(MatcherCastTest, ValueIsCopied) {
|
||||
{
|
||||
// When an IntReferenceWrapper is passed.
|
||||
int n = 42;
|
||||
Matcher<IntReferenceWrapper> m =
|
||||
MatcherCast<IntReferenceWrapper>(IntReferenceWrapper(n));
|
||||
{
|
||||
int value = 42;
|
||||
EXPECT_TRUE(m.Matches(value));
|
||||
value = 10;
|
||||
EXPECT_FALSE(m.Matches(value));
|
||||
// This changes the stored reference.
|
||||
n = 10;
|
||||
EXPECT_TRUE(m.Matches(value));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// When an int is passed.
|
||||
int n = 42;
|
||||
Matcher<IntReferenceWrapper> m = MatcherCast<IntReferenceWrapper>(n);
|
||||
// Verify that the matcher holds a reference to n, not to its temporary copy.
|
||||
EXPECT_TRUE(m.Matches(n));
|
||||
{
|
||||
int value = 42;
|
||||
EXPECT_TRUE(m.Matches(value));
|
||||
value = 10;
|
||||
EXPECT_FALSE(m.Matches(value));
|
||||
// This does not change the stored int.
|
||||
n = 10;
|
||||
EXPECT_FALSE(m.Matches(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Base {
|
||||
|
||||
@ -773,6 +773,35 @@ class GeMatcher
|
||||
static const char* NegatedDesc() { return "isn't >="; }
|
||||
};
|
||||
|
||||
// Same as `EqMatcher<Rhs>`, except that the `rhs` is stored as `StoredRhs` and
|
||||
// must be implicitly convertible to `Rhs`.
|
||||
template <typename Rhs, typename StoredRhs>
|
||||
class ImplicitCastEqMatcher {
|
||||
public:
|
||||
explicit ImplicitCastEqMatcher(const StoredRhs& rhs) : stored_rhs_(rhs) {}
|
||||
|
||||
using is_gtest_matcher = void;
|
||||
|
||||
template <typename Lhs>
|
||||
bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
|
||||
return lhs == rhs();
|
||||
}
|
||||
|
||||
void DescribeTo(std::ostream* os) const {
|
||||
*os << "is equal to ";
|
||||
UniversalPrint(rhs(), os);
|
||||
}
|
||||
void DescribeNegationTo(std::ostream* os) const {
|
||||
*os << "isn't equal to ";
|
||||
UniversalPrint(rhs(), os);
|
||||
}
|
||||
|
||||
private:
|
||||
Rhs rhs() const { return ImplicitCast_<Rhs>(stored_rhs_); }
|
||||
|
||||
StoredRhs stored_rhs_;
|
||||
};
|
||||
|
||||
template <typename T, typename = typename std::enable_if<
|
||||
std::is_constructible<std::string, T>::value>::type>
|
||||
using StringLike = T;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user