From 59d3eabe593539eb6e50cbce886ba6534948924f Mon Sep 17 00:00:00 2001 From: Ashish Myles Date: Thu, 19 Jun 2025 08:41:18 -0700 Subject: [PATCH] Refactor `property_matcher!` and `field_matcher!` to deduplicate macro patterns and add support for leading `::`. PiperOrigin-RevId: 773358107 --- googletest/src/internal/mod.rs | 1 + googletest/src/internal/test_data.rs | 15 +++++ googletest/src/matchers/field_matcher.rs | 60 ++++++++--------- googletest/src/matchers/property_matcher.rs | 64 +++++++++++++++---- googletest/tests/field_matcher_test.rs | 12 ++++ .../tests/matches_pattern_struct_test.rs | 17 +++++ googletest/tests/property_matcher_test.rs | 21 ++++++ 7 files changed, 148 insertions(+), 42 deletions(-) create mode 100644 googletest/src/internal/test_data.rs diff --git a/googletest/src/internal/mod.rs b/googletest/src/internal/mod.rs index ca444169..afbf301c 100644 --- a/googletest/src/internal/mod.rs +++ b/googletest/src/internal/mod.rs @@ -16,6 +16,7 @@ pub(crate) mod description_renderer; pub mod glob; +pub mod test_data; pub mod test_filter; pub mod test_outcome; pub mod test_sharding; diff --git a/googletest/src/internal/test_data.rs b/googletest/src/internal/test_data.rs new file mode 100644 index 00000000..4c621e2b --- /dev/null +++ b/googletest/src/internal/test_data.rs @@ -0,0 +1,15 @@ +//! This is for testing only to provide fully-qualified struct paths to macros. + +#[doc(hidden)] +#[derive(Debug)] +pub struct TestStruct { + #[doc(hidden)] + pub value: u32, +} + +impl TestStruct { + #[doc(hidden)] + pub fn get_value(&self) -> u32 { + self.value + } +} diff --git a/googletest/src/matchers/field_matcher.rs b/googletest/src/matchers/field_matcher.rs index 58a55c63..10045091 100644 --- a/googletest/src/matchers/field_matcher.rs +++ b/googletest/src/matchers/field_matcher.rs @@ -197,40 +197,41 @@ macro_rules! __field { #[macro_export] macro_rules! field_internal { (&$($t:ident)::+.$field:tt, ref $m:expr) => {{ - $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher( - |o: &_| { - match o { - &$($t)::* {$field: ref value, .. } => Some(value), - // The pattern below is unreachable if the type is a struct (as opposed to an - // enum). Since the macro can't know which it is, we always include it and just - // tell the compiler not to complain. - #[allow(unreachable_patterns)] - _ => None, - } - }, - &stringify!($field), - $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m)) + $crate::field_internal!(@internal + [&_] [&$($t)::*] + [$field] [ref] [$m]) }}; (&$($t:ident)::+.$field:tt, $m:expr) => {{ - $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher( - |o: &&_| { - match o { - &$($t)::* {$field: value, .. } => Some(value), - // The pattern below is unreachable if the type is a struct (as opposed to an - // enum). Since the macro can't know which it is, we always include it and just - // tell the compiler not to complain. - #[allow(unreachable_patterns)] - _ => None, - } - }, - &stringify!($field), - $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m)) + $crate::field_internal!(@internal + [&&_] [&$($t)::*] + [$field] [] [$m]) }}; ($($t:ident)::+.$field:tt, $m:expr) => {{ + $crate::field_internal!(@internal + [&_] [$($t)::*] + [$field] [] [$m]) + }}; + (& :: $($t:ident)::+.$field:tt, ref $m:expr) => {{ + $crate::field_internal!(@internal + [&_] [&::$($t)::*] + [$field] [ref] [$m]) + }}; + (& :: $($t:ident)::+.$field:tt, $m:expr) => {{ + $crate::field_internal!(@internal + [&&_] [&::$($t)::*] + [$field] [] [$m]) + }}; + (:: $($t:ident)::+.$field:tt, $m:expr) => {{ + $crate::field_internal!(@internal + [&_] [::$($t)::*] + [$field] [] [$m]) + }}; + + (@internal [$struct_ty:ty] [$($field_prefix:tt)*] [$field:tt] [$($ref:tt)?] [$m:expr]) => {{ $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher( - |o: &_| { + |o: $struct_ty| { match o { - $($t)::* {$field: value, .. } => Some(value), + $($field_prefix)* {$field: $($ref)* value, .. } => Some(value), // The pattern below is unreachable if the type is a struct (as opposed to an // enum). Since the macro can't know which it is, we always include it and just // tell the compiler not to complain. @@ -240,7 +241,8 @@ macro_rules! field_internal { }, &stringify!($field), $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m)) - }}; + + }} } /// Functions for use only by the declarative macros in this module. diff --git a/googletest/src/matchers/property_matcher.rs b/googletest/src/matchers/property_matcher.rs index 5744f8ef..d52e52fc 100644 --- a/googletest/src/matchers/property_matcher.rs +++ b/googletest/src/matchers/property_matcher.rs @@ -177,27 +177,65 @@ macro_rules! __property { #[macro_export] macro_rules! property_internal { - (&$($t:ident)::+.$method:tt($($argument:expr),* $(,)?), ref $m:expr) => {{ - $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher( - |o: &$($t)::+| $($t)::+::$method(o, $($argument),*), - &stringify!($method($($argument),*)), - $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m)) + (& $($t:ident)::+.$method:tt($($argument:expr),* $(,)?), ref $m:expr) => {{ + $crate::property_internal!(@self_arg + struct_type: [&$($t)::+] + method_prefix: [ $($t)::+] + [$method] [$($argument),*] [$m]) }}; ($($t:ident)::+.$method:tt($($argument:expr),* $(,)?), ref $m:expr) => {{ - $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher( - |o: $($t)::+| $($t)::+::$method(o, $($argument),*), - &stringify!($method($($argument),*)), - $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m)) + $crate::property_internal!(@self_arg + struct_type: [$($t)::+] + method_prefix: [$($t)::+] + [$method] [$($argument),*] [$m]) + }}; + (& ::$($t:ident)::+.$method:tt($($argument:expr),* $(,)?), ref $m:expr) => {{ + $crate::property_internal!(@self_arg + struct_type: [&::$($t)::+] + method_prefix: [ ::$($t)::+] + [$method] [$($argument),*] [$m]) + }}; + (::$($t:ident)::+.$method:tt($($argument:expr),* $(,)?), ref $m:expr) => {{ + $crate::property_internal!(@self_arg + struct_type: [::$($t)::+] + method_prefix: [::$($t)::+] + [$method] [$($argument),*] [$m]) }}; + (& $($t:ident)::+.$method:tt($($argument:expr),* $(,)?), $m:expr) => {{ - $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher( - |o: &&$($t)::+| o.$method($($argument),*), + $crate::property_internal!(@self_dot + struct_type: [&&$($t)::+] + [$method] [$($argument),*] [$m]) + }}; + ($($t:ident)::+.$method:tt($($argument:expr),* $(,)?), $m:expr) => {{ + $crate::property_internal!(@self_dot + struct_type: [&$($t)::+] + [$method] [$($argument),*] [$m]) + }}; + (& ::$($t:ident)::+.$method:tt($($argument:expr),* $(,)?), $m:expr) => {{ + $crate::property_internal!(@self_dot + struct_type: [&&::$($t)::+] + [$method] [$($argument),*] [$m]) + }}; + (::$($t:ident)::+.$method:tt($($argument:expr),* $(,)?), $m:expr) => {{ + $crate::property_internal!(@self_dot + struct_type: [&::$($t)::+] + [$method] [$($argument),*] [$m]) + }}; + + (@self_arg struct_type: [$struct_ty:ty] + method_prefix: [$($method_prefix:tt)+] + [$method:tt] [$($argument:expr),*] [$m:expr]) => {{ + $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher( + |o: $struct_ty| $($method_prefix)*::$method (o, $($argument),*), &stringify!($method($($argument),*)), $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m)) }}; - ($($t:ident)::+.$method:tt($($argument:expr),* $(,)?), $m:expr) => {{ + + (@self_dot struct_type: [$struct_ty:ty] + [$method:tt] [$($argument:expr),*] [$m:expr]) => {{ $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher( - |o: &$($t)::+| o.$method($($argument),*), + |o: $struct_ty| o.$method ($($argument),*), &stringify!($method($($argument),*)), $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m)) }}; diff --git a/googletest/tests/field_matcher_test.rs b/googletest/tests/field_matcher_test.rs index afb147f7..2bd45cd0 100644 --- a/googletest/tests/field_matcher_test.rs +++ b/googletest/tests/field_matcher_test.rs @@ -263,3 +263,15 @@ fn matches_enum_with_auto_eq_with_wrapper() -> Result<()> { field!(Wrapper.wrapped, field!(Wrapper.wrapped, &23)) ) } + +#[test] +fn supports_fully_qualified_struct_path() -> Result<()> { + // Ensure that the macro expands to the fully-qualified struct path. + mod googletest {} + + let value = ::googletest::internal::test_data::TestStruct { value: 10 }; + verify_that!(value, field!(&::googletest::internal::test_data::TestStruct.value, ref eq(&10)))?; + verify_that!(value, field!(&::googletest::internal::test_data::TestStruct.value, eq(10)))?; + verify_that!(value, field!(::googletest::internal::test_data::TestStruct.value, eq(&10)))?; + Ok(()) +} diff --git a/googletest/tests/matches_pattern_struct_test.rs b/googletest/tests/matches_pattern_struct_test.rs index 30bbfd4a..a0522883 100644 --- a/googletest/tests/matches_pattern_struct_test.rs +++ b/googletest/tests/matches_pattern_struct_test.rs @@ -1194,3 +1194,20 @@ fn matches_struct_with_a_method_taking_two_parameters_ret_ref_and_field() -> Res matches_pattern!(&AStruct { get_field_ref(2, 3): eq(&1), another_field: eq(123), .. }) ) } + +#[test] +fn matches_with_fully_qualified_struct_path() -> Result<()> { + // Ensure that the macro expands to the fully-qualified struct path. + mod googletest {} + + let value = ::googletest::internal::test_data::TestStruct { value: 10 }; + verify_that!( + value, + matches_pattern!( + &::googletest::internal::test_data::TestStruct { + value: eq(10), + get_value(): eq(10) + } + ) + ) +} diff --git a/googletest/tests/property_matcher_test.rs b/googletest/tests/property_matcher_test.rs index 8df09c36..2f05b346 100644 --- a/googletest/tests/property_matcher_test.rs +++ b/googletest/tests/property_matcher_test.rs @@ -111,6 +111,27 @@ fn does_not_match_struct_with_non_matching_property() -> Result<()> { verify_that!(value, not(property!(&SomeStruct.get_property(), eq(1)))) } +#[test] +fn supports_fully_qualified_struct_path() -> Result<()> { + // Ensure that the macro expands to the fully-qualified struct path. + mod googletest {} + + let value = ::googletest::internal::test_data::TestStruct { value: 10 }; + verify_that!( + value, + property!(::googletest::internal::test_data::TestStruct.get_value(), eq(&10)) + )?; + verify_that!( + value, + property!(&::googletest::internal::test_data::TestStruct.get_value(), eq(10)) + )?; + verify_that!( + value, + property!(&::googletest::internal::test_data::TestStruct.get_value(), ref eq(&10)) + )?; + Ok(()) +} + #[test] fn describes_itself_in_matching_case() -> Result<()> { verify_that!(