diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index bdd8084d5..5cfa0b71c 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -275,6 +275,7 @@ #include "gmock/internal/gmock-internal-utils.h" #include "gmock/internal/gmock-pp.h" #include "gtest/gtest.h" +#include "gtest/internal/gtest-internal.h" // MSVC warning C5046 is new as of VS2017 version 15.8. #if defined(_MSC_VER) && _MSC_VER >= 1915 @@ -2059,6 +2060,15 @@ class [[nodiscard]] PointerMatcher { }; #if GTEST_HAS_RTTI +// A type trait to essentially determine if `DynamicCastMessage` is available, +// since older versions of protobuf don't have this function. +template +static constexpr bool kHasDynamicCastMessage = false; +template +static constexpr bool kHasDynamicCastMessage< + T, std::void_t( + std::declval()))>> = true; + // Implements the WhenDynamicCastTo(m) matcher that matches a pointer or // reference that matches inner_matcher when dynamic_cast is applied. // The result of dynamic_cast is forwarded to the inner matcher. @@ -2086,6 +2096,27 @@ class [[nodiscard]] WhenDynamicCastToMatcherBase { static std::string GetToName() { return GetTypeName(); } + template + static auto DoDynamicCast(From& from) { + using ToType = + std::remove_const_t>>; + + if constexpr (std::is_base_of_v && + kHasDynamicCastMessage) { + if constexpr (std::is_pointer_v) { + return proto2::DynamicCastMessage(from); + } else { + // We don't want an std::bad_cast here, so do the cast with pointers. + return proto2::DynamicCastMessage(&from); + } + } else if constexpr (std::is_pointer_v) { + return dynamic_cast(from); + } else { + // We don't want an std::bad_cast here, so do the cast with pointers. + return dynamic_cast*>(&from); + } + } + private: static void GetCastTypeDescription(::std::ostream* os) { *os << "when dynamic_cast to " << GetToName() << ", "; @@ -2103,7 +2134,7 @@ class [[nodiscard]] WhenDynamicCastToMatcher template bool MatchAndExplain(From from, MatchResultListener* listener) const { - To to = dynamic_cast(from); + To to = this->DoDynamicCast(from); return MatchPrintAndExplain(to, this->matcher_, listener); } }; @@ -2119,8 +2150,7 @@ WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase { template bool MatchAndExplain(From& from, MatchResultListener* listener) const { - // We don't want an std::bad_cast here, so do the cast with pointers. - To* to = dynamic_cast(&from); + To* to = this->DoDynamicCast(from); if (to == nullptr) { *listener << "which cannot be dynamic_cast to " << this->GetToName(); return false; diff --git a/googletest/include/gtest/internal/gtest-internal.h b/googletest/include/gtest/internal/gtest-internal.h index 7096355d5..8f66d1b35 100644 --- a/googletest/include/gtest/internal/gtest-internal.h +++ b/googletest/include/gtest/internal/gtest-internal.h @@ -96,6 +96,12 @@ namespace proto2 { class [[nodiscard]] MessageLite; + +// Dummy forward declaration of `DynamicCastMessage`. Does not match any actual +// overloads of `DynamicCastMessage`, but can be used to assist name resolution +// in templates. +template +T DynamicCastMessage() = delete; } namespace testing { diff --git a/googletest/test/custom/gmock_dynamic_cast_message_test.proto b/googletest/test/custom/gmock_dynamic_cast_message_test.proto new file mode 100644 index 000000000..eacc45945 --- /dev/null +++ b/googletest/test/custom/gmock_dynamic_cast_message_test.proto @@ -0,0 +1,9 @@ +edition = "2024"; + +package testing.internal.dynamic_cast_message_test; + +message FooMessage { + int32 int_field = 1; +} + +message BarMessage {}