Skip to content

Commit

Permalink
Make matches_pattern support _ at the top level in braced structs.
Browse files Browse the repository at this point in the history
This change is not backward-compatible since:
* Some things that previously fell back to `match` pattern matching now use `match_pattern!`'s specialized matching, which requires the matched object to implement `Debug`.

Toward #447

PiperOrigin-RevId: 703619892
  • Loading branch information
marcianx authored and copybara-github committed Dec 9, 2024
1 parent 2ba098e commit d446232
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 14 deletions.
12 changes: 12 additions & 0 deletions googletest/src/matchers/matches_pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,18 @@ pub mod internal {
}

mod compile_fail_tests {
/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
/// struct Foo { a: u32 }
/// impl Foo {
/// fn b() {}
/// }
/// let actual = Foo { a: 1 };
/// verify_that!(actual, matches_pattern!(Foo { a: eq(&1), b(): _ }));
/// ```
fn _underscore_unsupported_for_methods() {}

/// ```compile_fail
/// use ::googletest::prelude::*;
/// #[derive(Debug)]
Expand Down
17 changes: 17 additions & 0 deletions googletest/tests/matches_pattern_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,23 @@ fn matches_struct_containing_non_copy_field_binding_mode() -> Result<()> {
verify_that!(actual, matches_pattern!(AStruct { a_string: eq("123") }))
}

#[test]
fn matches_struct_with_interleaved_underscore() -> Result<()> {
#[derive(Debug)]
struct NonCopy;
#[allow(dead_code)]
#[derive(Debug)]
struct AStruct {
a: u32,
b: NonCopy,
c: u32,
}
let actual = AStruct { a: 1, b: NonCopy, c: 3 };

verify_that!(actual, matches_pattern!(&AStruct { a: eq(1), b: _, c: eq(3) }))?;
verify_that!(actual, matches_pattern!(AStruct { a: eq(&1), b: _, c: eq(&3) }))
}

#[test]
fn has_correct_assertion_failure_message_for_single_field() -> Result<()> {
#[derive(Debug)]
Expand Down
43 changes: 29 additions & 14 deletions googletest_macro/src/matches_pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,21 +204,32 @@ impl Parse for FieldOrMethod {
}

/// Either field or method call matcher. E.g.:
/// * `field: starts_with("something")`
/// * `field: starts_with("something")` or `field: _`
/// * `property(arg1, arg2): starts_with("something")
struct FieldOrMethodPattern {
ref_token: Option<Token![ref]>,
field_or_method: FieldOrMethod,
matcher: Expr,
/// When `None`, it represents `_` which matches anything, meaning we should
/// ignore it.
matcher: Option<Expr>,
}

impl Parse for FieldOrMethodPattern {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(FieldOrMethodPattern {
field_or_method: input.parse()?,
ref_token: input.parse()?,
matcher: input.parse()?,
})
let field_or_method: FieldOrMethod = input.parse()?;
let underscore = input.parse::<Option<Token![_]>>()?;
match underscore {
Some(underscore) if matches!(field_or_method, FieldOrMethod::Method(_)) => compile_err(
underscore.spans[0],
"Don't match a method call against `_`. Just omit it instead.",
),
Some(_) => Ok(FieldOrMethodPattern { field_or_method, ref_token: None, matcher: None }),
None => Ok(FieldOrMethodPattern {
field_or_method,
ref_token: input.parse()?,
matcher: Some(input.parse()?),
}),
}
}
}

Expand All @@ -231,13 +242,17 @@ fn parse_braced_pattern_args(
let mut field_names = vec![];
let field_patterns: Vec<TokenStream> = patterns
.into_iter()
.map(|FieldOrMethodPattern { ref_token, field_or_method, matcher }| match field_or_method {
FieldOrMethod::Field(ident) => {
field_names.push(ident.clone());
quote! { field!(#struct_name . #ident, #ref_token #matcher) }
}
FieldOrMethod::Method(call) => {
quote! { property!(#struct_name . #call, #ref_token #matcher) }
.filter_map(|FieldOrMethodPattern { ref_token, field_or_method, matcher }| {
match field_or_method {
FieldOrMethod::Field(ident) => {
field_names.push(ident.clone());
matcher.map(|matcher| {
quote! { field!(#struct_name . #ident, #ref_token #matcher) }
})
}
FieldOrMethod::Method(call) => {
Some(quote! { property!(#struct_name . #call, #ref_token #matcher) })
}
}
})
.collect();
Expand Down

0 comments on commit d446232

Please sign in to comment.