Skip to content

Commit c7b3545

Browse files
Googlercopybara-github
Googler
authored andcommitted
Fix the parsing logic of matches_pattern! to support _ at the top level in braced enums.
In the current parsing logic, the `_` field in pattern `AsEnum::Foo { a_field : _ }` is parsed as None, and as a result no FieldMatcher is generated, and the internal `is()` matcher only evaluates the innerMatcher(the field_matcher). With the current change, when no field_matcher is present, the matches pattern macro verifies the variant of the Enum with `pattern_only()` matcher. Toward #626 PiperOrigin-RevId: 755381147
1 parent 3a977ca commit c7b3545

File tree

2 files changed

+165
-42
lines changed

2 files changed

+165
-42
lines changed

googletest/tests/matches_pattern_test.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,110 @@ fn matches_enum_struct_non_exhaustive() -> Result<()> {
573573
verify_that!(actual, matches_pattern!(&AnEnum::Variant1 { a_field: eq(123), .. }))
574574
}
575575

576+
#[test]
577+
fn matches_enum_struct_with_all_non_exhaustive_fields() -> Result<()> {
578+
#[allow(dead_code)]
579+
#[derive(Debug)]
580+
enum AnEnum {
581+
Variant1 { a: u32, b: u32 },
582+
Variant2 { c: u32, d: u32 },
583+
}
584+
let actual: AnEnum = AnEnum::Variant1 { a: 123, b: 234 };
585+
verify_that!(actual, matches_pattern!(&AnEnum::Variant1 { .. }))
586+
}
587+
588+
#[test]
589+
fn has_failure_when_wrong_enum_struct_variant_is_matched_with_all_non_exhaustive_fields(
590+
) -> Result<()> {
591+
#[allow(dead_code)]
592+
#[derive(Debug)]
593+
enum AnEnum {
594+
Variant1 { a: u32, b: u32 },
595+
Variant2 { c: u32, d: u32 },
596+
}
597+
let actual: AnEnum = AnEnum::Variant1 { a: 123, b: 234 };
598+
599+
let result = verify_that!(actual, matches_pattern!(&AnEnum::Variant2 { .. }));
600+
601+
const EXPECTED: &str = indoc!(
602+
"
603+
Expected: is & AnEnum :: Variant2 { .. }
604+
Actual: Variant1 { a: 123, b: 234 },
605+
which is not & AnEnum :: Variant2 { .. }
606+
"
607+
);
608+
verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
609+
}
610+
611+
#[test]
612+
fn matches_enum_struct_with_all_wildcard_fields() -> Result<()> {
613+
#[allow(dead_code)]
614+
#[derive(Debug)]
615+
enum AnEnum {
616+
Variant1 { a: u32, b: u32 },
617+
Variant2 { c: u32, d: u32 },
618+
}
619+
let actual: AnEnum = AnEnum::Variant1 { a: 123, b: 234 };
620+
verify_that!(actual, matches_pattern!(&AnEnum::Variant1 { a: _, b: _ }))
621+
}
622+
623+
#[test]
624+
fn has_failure_when_wrong_enum_struct_variant_is_matched_with_all_wildcard_fields() -> Result<()> {
625+
#[allow(dead_code)]
626+
#[derive(Debug)]
627+
enum AnEnum {
628+
Variant1 { a: u32, b: u32 },
629+
Variant2 { c: u32, d: u32 },
630+
}
631+
let actual: AnEnum = AnEnum::Variant1 { a: 123, b: 234 };
632+
633+
let result = verify_that!(actual, matches_pattern!(&AnEnum::Variant2 { c: _, d: _ }));
634+
635+
const EXPECTED: &str = indoc!(
636+
"
637+
Expected: is & AnEnum :: Variant2 { c : _, d : _, }
638+
Actual: Variant1 { a: 123, b: 234 },
639+
which is not & AnEnum :: Variant2 { c : _, d : _, }
640+
"
641+
);
642+
verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
643+
}
644+
645+
#[test]
646+
fn matches_enum_struct_non_exhaustive_with_wildcard_fields() -> Result<()> {
647+
#[allow(dead_code)]
648+
#[derive(Debug)]
649+
enum AnEnum {
650+
Variant1 { a: u32, b: u32 },
651+
Variant2 { c: u32, d: u32 },
652+
}
653+
let actual: AnEnum = AnEnum::Variant1 { a: 123, b: 234 };
654+
verify_that!(actual, matches_pattern!(&AnEnum::Variant1 { a: _, .. }))
655+
}
656+
657+
#[test]
658+
fn has_failure_when_wrong_enum_struct_variant_is_matched_non_exhaustive_with_wildcard_fields(
659+
) -> Result<()> {
660+
#[allow(dead_code)]
661+
#[derive(Debug)]
662+
enum AnEnum {
663+
Variant1 { a: u32, b: u32 },
664+
Variant2 { c: u32, d: u32 },
665+
}
666+
let actual: AnEnum = AnEnum::Variant1 { a: 123, b: 234 };
667+
668+
let result = verify_that!(actual, matches_pattern!(&AnEnum::Variant2 { c: _, .. }));
669+
670+
const EXPECTED: &str = indoc!(
671+
"
672+
Expected: is & AnEnum :: Variant2 { c : _, .. }
673+
Actual: Variant1 { a: 123, b: 234 },
674+
which is not & AnEnum :: Variant2 { c : _, .. }
675+
"
676+
);
677+
verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
678+
}
679+
576680
#[test]
577681
fn matches_enum_struct_exhaustive_with_multiple_variants() -> Result<()> {
578682
#[allow(dead_code)]

googletest_macro/src/matches_pattern.rs

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -308,50 +308,69 @@ fn parse_braced_pattern_args(
308308
})
309309
.collect();
310310

311-
let matcher = quote! {
312-
googletest::matchers::__internal_unstable_do_not_depend_on_these::is(
313-
stringify!(#struct_name),
314-
all!(#(#field_patterns),* )
315-
)
316-
};
317-
318-
// Do a match to ensure:
319-
// - Fields are exhaustively listed unless the pattern ended with `..` and has
320-
// any fields in the pattern.
321-
// - `UNDEFINED_SYMBOL { .. }` fails to compile.
322-
//
323-
// The requisite that some fields are in the pattern is there because
324-
// `matches_pattern!` also uses the brace notation for tuple structs when
325-
// asserting on method calls on tuple structs. i.e.
326-
//
327-
// ```
328-
// struct Struct(u32);
329-
// ...
330-
// matches_pattern!(foo, Struct { bar(): eq(1) })
331-
// ```
332-
// and we can't emit an exhaustiveness check based on the `matches_pattern!`.
333-
if field_names.is_empty() && dot_dot.is_none() &&
334-
// If there are no fields, then this check means that there are method patterns, and we can
335-
// no longer be confident that this is a braced struct rather than a tuple struct.
336-
!field_patterns.is_empty()
337-
{
338-
Ok(matcher)
339-
} else {
311+
if field_patterns.is_empty() {
312+
// It is possible that the logic above didn't generate any field matchers
313+
// (e.g., for patterns like `AsEnum::Foo { a_field : _ }`).
314+
// In this case we verify that the enum has the correct case, but don't
315+
// verify the payload.
316+
let full_pattern = quote! { #struct_name { #(#field_names: _,)* #dot_dot}};
317+
340318
Ok(quote! {
341-
googletest::matchers::__internal_unstable_do_not_depend_on_these::compile_assert_and_match(
342-
|actual| {
343-
// Exhaustively check that all field names are specified.
344-
match actual {
345-
#struct_name { #(#field_names: _,)* #dot_dot } => {},
346-
// The pattern below is unreachable if the type is a struct (as opposed to
347-
// an enum). Since the macro can't know which it is, we always include it
348-
// and just tell the compiler not to complain.
349-
#[allow(unreachable_patterns)]
350-
_ => {},
351-
}
352-
},
353-
#matcher)
319+
googletest::matchers::__internal_unstable_do_not_depend_on_these::pattern_only(
320+
|actual| { matches!(actual, #full_pattern) },
321+
concat!("is ", stringify!(#full_pattern)),
322+
concat!("is not ", stringify!(#full_pattern))
323+
)
354324
})
325+
} else {
326+
// We have created at least one field matcher. Each field matcher will verify
327+
// not only its part of the payload, but also that the enum has the
328+
// correct case.
329+
let matcher = quote! {
330+
googletest::matchers::__internal_unstable_do_not_depend_on_these::is(
331+
stringify!(#struct_name),
332+
all!(#(#field_patterns),* )
333+
)
334+
};
335+
336+
// Do a match to ensure:
337+
// - Fields are exhaustively listed unless the pattern ended with `..` and has
338+
// any fields in the pattern.
339+
// - `UNDEFINED_SYMBOL { .. }` fails to compile.
340+
//
341+
// The requisite that some fields are in the pattern is there because
342+
// `matches_pattern!` also uses the brace notation for tuple structs when
343+
// asserting on method calls on tuple structs. i.e.
344+
//
345+
// ```
346+
// struct Struct(u32);
347+
// ...
348+
// matches_pattern!(foo, Struct { bar(): eq(1) })
349+
// ```
350+
// and we can't emit an exhaustiveness check based on the `matches_pattern!`.
351+
if field_names.is_empty() && dot_dot.is_none() &&
352+
// If there are no fields, then this check means that there are method patterns, and we can
353+
// no longer be confident that this is a braced struct rather than a tuple struct.
354+
!field_patterns.is_empty()
355+
{
356+
Ok(matcher)
357+
} else {
358+
Ok(quote! {
359+
googletest::matchers::__internal_unstable_do_not_depend_on_these::compile_assert_and_match(
360+
|actual| {
361+
// Exhaustively check that all field names are specified.
362+
match actual {
363+
#struct_name { #(#field_names: _,)* #dot_dot } => {},
364+
// The pattern below is unreachable if the type is a struct (as opposed to
365+
// an enum). Since the macro can't know which it is, we always include it
366+
// and just tell the compiler not to complain.
367+
#[allow(unreachable_patterns)]
368+
_ => {},
369+
}
370+
},
371+
#matcher)
372+
})
373+
}
355374
}
356375
}
357376

0 commit comments

Comments
 (0)