Skip to content

Adding lifetime parameter to Matcher #353

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions googletest/crate_docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ struct MyEqMatcher<T> {
expected: T,
}

impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
impl<T: PartialEq + Debug> Matcher<'_> for MyEqMatcher<T> {
type ActualT = T;

fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
Expand Down Expand Up @@ -250,7 +250,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# expected: T,
# }
#
# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# impl<T: PartialEq + Debug> Matcher<'_> for MyEqMatcher<T> {
# type ActualT = T;
#
# fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
Expand All @@ -273,7 +273,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# }
# }
#
pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> MyEqMatcher<T> {
MyEqMatcher { expected }
}
```
Expand All @@ -289,7 +289,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# expected: T,
# }
#
# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# impl<T: PartialEq + Debug> Matcher<'_> for MyEqMatcher<T> {
# type ActualT = T;
#
# fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
Expand All @@ -312,7 +312,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# }
# }
#
# pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
# pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> MyEqMatcher<T> {
# MyEqMatcher { expected }
# }
# /* The attribute macro would prevent the function from being compiled in a doctest.
Expand Down
6 changes: 3 additions & 3 deletions googletest/src/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,9 @@ pub mod internal {
///
/// **For internal use only. API stablility is not guaranteed!**
#[must_use = "The assertion result must be evaluated to affect the test result."]
pub fn check_matcher<T: Debug + ?Sized>(
actual: &T,
expected: impl Matcher<ActualT = T>,
pub fn check_matcher<'a, T: Debug + ?Sized>(
actual: &'a T,
expected: impl Matcher<'a, ActualT = T>,
actual_expr: &'static str,
source_location: SourceLocation,
) -> Result<(), TestAssertionFailure> {
Expand Down
16 changes: 8 additions & 8 deletions googletest/src/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatc
use std::fmt::Debug;

/// An interface for checking an arbitrary condition on a datum.
pub trait Matcher {
pub trait Matcher<'a> {
/// The type against which this matcher matches.
type ActualT: Debug + ?Sized;

Expand All @@ -32,7 +32,7 @@ pub trait Matcher {
/// matching condition is based on data stored in the matcher. For example,
/// `eq` matches when its stored expected value is equal (in the sense of
/// the `==` operator) to the value `actual`.
fn matches(&self, actual: &Self::ActualT) -> MatcherResult;
fn matches(&self, actual: &'a Self::ActualT) -> MatcherResult;

/// Returns a description of `self` or a negative description if
/// `matcher_result` is `DoesNotMatch`.
Expand Down Expand Up @@ -133,7 +133,7 @@ pub trait Matcher {
/// .nested(self.expected.explain_match(actual.deref()))
/// }
/// ```
fn explain_match(&self, actual: &Self::ActualT) -> Description {
fn explain_match(&self, actual: &'a Self::ActualT) -> Description {
format!("which {}", self.describe(self.matches(actual))).into()
}

Expand All @@ -160,7 +160,7 @@ pub trait Matcher {
// TODO(b/264518763): Replace the return type with impl Matcher and reduce
// visibility of ConjunctionMatcher once impl in return position in trait
// methods is stable.
fn and<Right: Matcher<ActualT = Self::ActualT>>(
fn and<Right: Matcher<'a, ActualT = Self::ActualT>>(
self,
right: Right,
) -> ConjunctionMatcher<Self, Right>
Expand Down Expand Up @@ -190,7 +190,7 @@ pub trait Matcher {
// TODO(b/264518763): Replace the return type with impl Matcher and reduce
// visibility of DisjunctionMatcher once impl in return position in trait
// methods is stable.
fn or<Right: Matcher<ActualT = Self::ActualT>>(
fn or<Right: Matcher<'a, ActualT = Self::ActualT>>(
self,
right: Right,
) -> DisjunctionMatcher<Self, Right>
Expand All @@ -210,9 +210,9 @@ const PRETTY_PRINT_LENGTH_THRESHOLD: usize = 60;
///
/// The parameter `actual_expr` contains the expression which was evaluated to
/// obtain `actual`.
pub(crate) fn create_assertion_failure<T: Debug + ?Sized>(
matcher: &impl Matcher<ActualT = T>,
actual: &T,
pub(crate) fn create_assertion_failure<'a, T: Debug + ?Sized>(
matcher: &impl Matcher<'a, ActualT = T>,
actual: &'a T,
actual_expr: &'static str,
source_location: SourceLocation,
) -> TestAssertionFailure {
Expand Down
4 changes: 2 additions & 2 deletions googletest/src/matcher_support/count_elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
/// unambiguous answer, i.e., the upper bound exists and the lower and upper
/// bounds agree. Otherwise it iterates through `value` and counts the
/// elements.
pub(crate) fn count_elements<ContainerT: ?Sized>(value: &ContainerT) -> usize
pub(crate) fn count_elements<'b, ContainerT: ?Sized>(value: &'b ContainerT) -> usize
where
for<'b> &'b ContainerT: IntoIterator,
&'b ContainerT: IntoIterator,
{
let iterator = value.into_iter();
if let (lower, Some(higher)) = iterator.size_hint() {
Expand Down
14 changes: 7 additions & 7 deletions googletest/src/matchers/all_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,23 +74,23 @@ pub mod internal {
///
/// For internal use only. API stablility is not guaranteed!
#[doc(hidden)]
pub struct AllMatcher<'a, T: Debug + ?Sized, const N: usize> {
components: [Box<dyn Matcher<ActualT = T> + 'a>; N],
pub struct AllMatcher<'a, 'e, T: Debug + ?Sized, const N: usize> {
components: [Box<dyn Matcher<'e, ActualT = T> + 'a>; N],
}

impl<'a, T: Debug + ?Sized, const N: usize> AllMatcher<'a, T, N> {
impl<'a, 'e, T: Debug + ?Sized, const N: usize> AllMatcher<'a, 'e, T, N> {
/// Constructs an [`AllMatcher`] with the given component matchers.
///
/// Intended for use only by the [`all`] macro.
pub fn new(components: [Box<dyn Matcher<ActualT = T> + 'a>; N]) -> Self {
pub fn new(components: [Box<dyn Matcher<'e, ActualT = T> + 'a>; N]) -> Self {
Self { components }
}
}

impl<'a, T: Debug + ?Sized, const N: usize> Matcher for AllMatcher<'a, T, N> {
impl<'a, 'e, T: Debug + ?Sized, const N: usize> Matcher<'e> for AllMatcher<'a, 'e, T, N> {
type ActualT = T;

fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
fn matches(&self, actual: &'e Self::ActualT) -> MatcherResult {
for component in &self.components {
match component.matches(actual) {
MatcherResult::NoMatch => {
Expand All @@ -102,7 +102,7 @@ pub mod internal {
MatcherResult::Match
}

fn explain_match(&self, actual: &Self::ActualT) -> Description {
fn explain_match(&self, actual: &'e Self::ActualT) -> Description {
match N {
0 => anything::<T>().explain_match(actual),
1 => self.components[0].explain_match(actual),
Expand Down
14 changes: 7 additions & 7 deletions googletest/src/matchers/any_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,27 @@ pub mod internal {
///
/// For internal use only. API stablility is not guaranteed!
#[doc(hidden)]
pub struct AnyMatcher<'a, T: Debug + ?Sized, const N: usize> {
components: [Box<dyn Matcher<ActualT = T> + 'a>; N],
pub struct AnyMatcher<'a, 'e, T: Debug + ?Sized, const N: usize> {
components: [Box<dyn Matcher<'e, ActualT = T> + 'a>; N],
}

impl<'a, T: Debug + ?Sized, const N: usize> AnyMatcher<'a, T, N> {
impl<'a, 'e, T: Debug + ?Sized, const N: usize> AnyMatcher<'a, 'e, T, N> {
/// Constructs an [`AnyMatcher`] with the given component matchers.
///
/// Intended for use only by the [`all`] macro.
pub fn new(components: [Box<dyn Matcher<ActualT = T> + 'a>; N]) -> Self {
pub fn new(components: [Box<dyn Matcher<'e, ActualT = T> + 'a>; N]) -> Self {
Self { components }
}
}

impl<'a, T: Debug + ?Sized, const N: usize> Matcher for AnyMatcher<'a, T, N> {
impl<'a, 'e, T: Debug + ?Sized, const N: usize> Matcher<'e> for AnyMatcher<'a, 'e, T, N> {
type ActualT = T;

fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
fn matches(&self, actual: &'e Self::ActualT) -> MatcherResult {
MatcherResult::from(self.components.iter().any(|c| c.matches(actual).is_match()))
}

fn explain_match(&self, actual: &Self::ActualT) -> Description {
fn explain_match(&self, actual: &'e Self::ActualT) -> Description {
match N {
0 => format!("which {}", anything::<T>().describe(MatcherResult::NoMatch)).into(),
1 => self.components[0].explain_match(actual),
Expand Down
6 changes: 3 additions & 3 deletions googletest/src/matchers/anything_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ use std::{fmt::Debug, marker::PhantomData};
/// # }
/// # should_pass().unwrap();
/// ```
pub fn anything<T: Debug + ?Sized>() -> impl Matcher<ActualT = T> {
pub fn anything<T: Debug + ?Sized>() -> Anything<T> {
Anything::<T>(Default::default())
}

struct Anything<T: ?Sized>(PhantomData<T>);
pub struct Anything<T: ?Sized>(PhantomData<T>);

impl<T: Debug + ?Sized> Matcher for Anything<T> {
impl<T: Debug + ?Sized> Matcher<'_> for Anything<T> {
type ActualT = T;

fn matches(&self, _: &T) -> MatcherResult {
Expand Down
14 changes: 10 additions & 4 deletions googletest/src/matchers/char_count_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@ use std::{fmt::Debug, marker::PhantomData};
/// # }
/// # should_pass().unwrap();
/// ```
pub fn char_count<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>>(
pub fn char_count<'a, T: Debug + ?Sized + AsRef<str>, E>(
expected: E,
) -> impl Matcher<ActualT = T> {
) -> impl Matcher<'a, ActualT = T>
where
E: for<'c> Matcher<'c, ActualT = usize>,
{
CharLenMatcher { expected, phantom: Default::default() }
}

Expand All @@ -67,7 +70,10 @@ struct CharLenMatcher<T: ?Sized, E> {
phantom: PhantomData<T>,
}

impl<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>> Matcher for CharLenMatcher<T, E> {
impl<T: Debug + ?Sized + AsRef<str>, E> Matcher<'_> for CharLenMatcher<T, E>
where
E: for<'b> Matcher<'b, ActualT = usize>,
{
type ActualT = T;

fn matches(&self, actual: &T) -> MatcherResult {
Expand Down Expand Up @@ -131,7 +137,7 @@ mod tests {
#[test]
fn char_count_explains_match() -> Result<()> {
struct TestMatcher<T>(PhantomData<T>);
impl<T: Debug> Matcher for TestMatcher<T> {
impl<T: Debug> Matcher<'_> for TestMatcher<T> {
type ActualT = T;

fn matches(&self, _: &T) -> MatcherResult {
Expand Down
6 changes: 3 additions & 3 deletions googletest/src/matchers/conjunction_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ impl<M1, M2> ConjunctionMatcher<M1, M2> {
}
}

impl<M1: Matcher, M2: Matcher<ActualT = M1::ActualT>> Matcher for ConjunctionMatcher<M1, M2>
impl<'a, M1: Matcher<'a>, M2: Matcher<'a, ActualT = M1::ActualT>> Matcher<'a> for ConjunctionMatcher<M1, M2>
where
M1::ActualT: Debug,
{
type ActualT = M1::ActualT;

fn matches(&self, actual: &M1::ActualT) -> MatcherResult {
fn matches(&self, actual: &'a M1::ActualT) -> MatcherResult {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::Match, MatcherResult::Match) => MatcherResult::Match,
_ => MatcherResult::NoMatch,
}
}

fn explain_match(&self, actual: &M1::ActualT) -> Description {
fn explain_match(&self, actual: &'a M1::ActualT) -> Description {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::Match, MatcherResult::Match) => Description::new()
.nested(self.m1.explain_match(actual))
Expand Down
31 changes: 18 additions & 13 deletions googletest/src/matchers/container_eq_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,23 @@ pub struct ContainerEqMatcher<ActualContainerT: ?Sized, ExpectedContainerT> {
phantom: PhantomData<ActualContainerT>,
}

impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT> Matcher
impl<'a, ActualElementT: 'a, ActualContainerT: 'a, ExpectedElementT, ExpectedContainerT> Matcher<'a>
for ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
where
ActualElementT: PartialEq<ExpectedElementT> + Debug + ?Sized,
ActualContainerT: PartialEq<ExpectedContainerT> + Debug + ?Sized,
ExpectedElementT: Debug,
ExpectedContainerT: Debug,
for<'a> &'a ActualContainerT: IntoIterator<Item = &'a ActualElementT>,
for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
&'a ActualContainerT: IntoIterator<Item = &'a ActualElementT>,
for<'b> &'b ExpectedContainerT: IntoIterator<Item = &'b ExpectedElementT>,
{
type ActualT = ActualContainerT;

fn matches(&self, actual: &ActualContainerT) -> MatcherResult {
fn matches(&self, actual: &'a ActualContainerT) -> MatcherResult {
(*actual == self.expected).into()
}

fn explain_match(&self, actual: &ActualContainerT) -> Description {
fn explain_match(&self, actual: &'a ActualContainerT) -> Description {
build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual)).into()
}

Expand All @@ -132,20 +132,25 @@ where
}
}

impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT>
impl<'a, ActualContainerT: ?Sized + 'a, ExpectedContainerT, ExpectedElementT>
ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
where
ActualElementT: PartialEq<ExpectedElementT> + ?Sized,
ActualContainerT: PartialEq<ExpectedContainerT> + ?Sized,
for<'a> &'a ActualContainerT: IntoIterator<Item = &'a ActualElementT>,
for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
&'a ActualContainerT: IntoIterator,
for<'b> &'b ExpectedContainerT: IntoIterator<Item = &'b ExpectedElementT>,
for<'b> <&'a ActualContainerT as IntoIterator>::Item: PartialEq<&'b ExpectedElementT>,
{
fn get_missing_items(&self, actual: &ActualContainerT) -> Vec<&ExpectedElementT> {
fn get_missing_items(
&self,
actual: &'a ActualContainerT,
) -> Vec<&'_ ExpectedElementT> {
self.expected.into_iter().filter(|&i| !actual.into_iter().any(|j| j == i)).collect()
}

fn get_unexpected_items<'a>(&self, actual: &'a ActualContainerT) -> Vec<&'a ActualElementT> {
actual.into_iter().filter(|&i| !self.expected.into_iter().any(|j| i == j)).collect()
fn get_unexpected_items(
&self,
actual: &'a ActualContainerT,
) -> Vec<<&'a ActualContainerT as IntoIterator>::Item> {
actual.into_iter().filter(|i| !self.expected.into_iter().any(|j| i == &j)).collect()
}
}

Expand Down
Loading