Skip to content

Commit ff91edb

Browse files
marcianxcopybara-github
authored andcommitted
Make matches_pattern support _ at the top level in braced structs.
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
1 parent 2ba098e commit ff91edb

File tree

3 files changed

+58
-14
lines changed

3 files changed

+58
-14
lines changed

googletest/src/matchers/matches_pattern.rs

+12
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,18 @@ pub mod internal {
372372
}
373373

374374
mod compile_fail_tests {
375+
/// ```compile_fail
376+
/// use ::googletest::prelude::*;
377+
/// #[derive(Debug)]
378+
/// struct Foo { a: u32 }
379+
/// impl Foo {
380+
/// fn b() {}
381+
/// }
382+
/// let actual = Foo { a: 1 };
383+
/// verify_that!(actual, matches_pattern!(Foo { a: eq(&1), b(): _ }));
384+
/// ```
385+
fn _underscore_unsupported_for_methods() {}
386+
375387
/// ```compile_fail
376388
/// use ::googletest::prelude::*;
377389
/// #[derive(Debug)]

googletest/tests/matches_pattern_test.rs

+17
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,23 @@ fn matches_struct_containing_non_copy_field_binding_mode() -> Result<()> {
150150
verify_that!(actual, matches_pattern!(AStruct { a_string: eq("123") }))
151151
}
152152

153+
#[test]
154+
fn matches_struct_with_interleaved_underscore() -> Result<()> {
155+
#[derive(Debug)]
156+
struct NonCopy;
157+
#[allow(dead_code)]
158+
#[derive(Debug)]
159+
struct AStruct {
160+
a: u32,
161+
b: NonCopy,
162+
c: u32,
163+
}
164+
let actual = AStruct { a: 1, b: NonCopy, c: 3 };
165+
166+
verify_that!(actual, matches_pattern!(&AStruct { a: eq(1), b: _, c: eq(3) }))?;
167+
verify_that!(actual, matches_pattern!(AStruct { a: eq(&1), b: _, c: eq(&3) }))
168+
}
169+
153170
#[test]
154171
fn has_correct_assertion_failure_message_for_single_field() -> Result<()> {
155172
#[derive(Debug)]

googletest_macro/src/matches_pattern.rs

+29-14
Original file line numberDiff line numberDiff line change
@@ -204,21 +204,32 @@ impl Parse for FieldOrMethod {
204204
}
205205

206206
/// Either field or method call matcher. E.g.:
207-
/// * `field: starts_with("something")`
207+
/// * `field: starts_with("something")` or `field: _`
208208
/// * `property(arg1, arg2): starts_with("something")
209209
struct FieldOrMethodPattern {
210210
ref_token: Option<Token![ref]>,
211211
field_or_method: FieldOrMethod,
212-
matcher: Expr,
212+
/// When `None`, it represents `_` which matches anything. then we should
213+
/// ignore it.
214+
matcher: Option<Expr>,
213215
}
214216

215217
impl Parse for FieldOrMethodPattern {
216218
fn parse(input: ParseStream) -> syn::Result<Self> {
217-
Ok(FieldOrMethodPattern {
218-
field_or_method: input.parse()?,
219-
ref_token: input.parse()?,
220-
matcher: input.parse()?,
221-
})
219+
let field_or_method: FieldOrMethod = input.parse()?;
220+
let underscore = input.parse::<Option<Token![_]>>()?;
221+
match underscore {
222+
Some(underscore) if matches!(field_or_method, FieldOrMethod::Method(_)) => compile_err(
223+
underscore.spans[0],
224+
"Don't match a method call against `_`. Just omit it instead.",
225+
),
226+
Some(_) => Ok(FieldOrMethodPattern { field_or_method, ref_token: None, matcher: None }),
227+
None => Ok(FieldOrMethodPattern {
228+
field_or_method,
229+
ref_token: input.parse()?,
230+
matcher: Some(input.parse()?),
231+
}),
232+
}
222233
}
223234
}
224235

@@ -231,13 +242,17 @@ fn parse_braced_pattern_args(
231242
let mut field_names = vec![];
232243
let field_patterns: Vec<TokenStream> = patterns
233244
.into_iter()
234-
.map(|FieldOrMethodPattern { ref_token, field_or_method, matcher }| match field_or_method {
235-
FieldOrMethod::Field(ident) => {
236-
field_names.push(ident.clone());
237-
quote! { field!(#struct_name . #ident, #ref_token #matcher) }
238-
}
239-
FieldOrMethod::Method(call) => {
240-
quote! { property!(#struct_name . #call, #ref_token #matcher) }
245+
.filter_map(|FieldOrMethodPattern { ref_token, field_or_method, matcher }| {
246+
match field_or_method {
247+
FieldOrMethod::Field(ident) => {
248+
field_names.push(ident.clone());
249+
matcher.map(|matcher| {
250+
quote! { field!(#struct_name . #ident, #ref_token #matcher) }
251+
})
252+
}
253+
FieldOrMethod::Method(call) => {
254+
Some(quote! { property!(#struct_name . #call, #ref_token #matcher) })
255+
}
241256
}
242257
})
243258
.collect();

0 commit comments

Comments
 (0)