Skip to content

Commit 3d757cc

Browse files
committed
Mutate guards on match arms by replacement with true/false
1 parent 06eb438 commit 3d757cc

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

src/mutant.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub enum Genre {
2828
UnaryOperator,
2929
/// Delete match arm.
3030
MatchArm,
31+
/// Replace the expression of a match arm guard with a fixed value.
32+
MatchArmGuard,
3133
}
3234

3335
/// A mutation applied to source code.
@@ -148,6 +150,10 @@ impl Mutant {
148150
v.push(s(" with "));
149151
v.push(s(self.replacement_text()).yellow());
150152
}
153+
Genre::MatchArmGuard => {
154+
v.push(s("replace match guard with "));
155+
v.push(s(self.replacement_text()).yellow());
156+
}
151157
Genre::MatchArm => {
152158
v.push(s("delete match arm"));
153159
}

src/visit.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,15 +613,33 @@ impl<'ast> Visit<'ast> for DiscoveryVisitor<'_> {
613613
if has_catchall {
614614
i.arms
615615
.iter()
616-
// Don't mutate the wild arm, because that will likely be unviable.
617-
.filter(|arm| !matches!(arm.pat, syn::Pat::Wild(_)))
616+
// Don't mutate the wild arm, because that will likely be unviable, and also
617+
// skip it if a guard is present, because the replacement of the guard with 'false'
618+
// below is logically equivalent to removing the arm.
619+
.filter(|arm| !matches!(arm.pat, syn::Pat::Wild(_)) && arm.guard.is_none())
618620
.for_each(|arm| {
619621
self.collect_mutant(arm.span().into(), &quote! {}, Genre::MatchArm);
620622
});
621623
} else {
622624
trace!("match has no `_` pattern");
623625
}
624626

627+
i.arms
628+
.iter()
629+
.flat_map(|arm| &arm.guard)
630+
.for_each(|(_if, guard_expr)| {
631+
self.collect_mutant(
632+
guard_expr.span().into(),
633+
&quote! { true },
634+
Genre::MatchArmGuard,
635+
);
636+
self.collect_mutant(
637+
guard_expr.span().into(),
638+
&quote! { false },
639+
Genre::MatchArmGuard,
640+
);
641+
});
642+
625643
syn::visit::visit_expr_match(self, i);
626644
}
627645
}
@@ -1169,4 +1187,36 @@ mod test {
11691187
empty
11701188
);
11711189
}
1190+
1191+
#[test]
1192+
fn mutate_match_guard() {
1193+
let options = Options::default();
1194+
let mutants = mutate_source_str(
1195+
indoc! {"
1196+
fn main() {
1197+
match x {
1198+
X::A if foo() => {},
1199+
X::A => {},
1200+
X::B => {},
1201+
X::C if bar() => {},
1202+
}
1203+
}
1204+
"},
1205+
&options,
1206+
)
1207+
.unwrap();
1208+
assert_eq!(
1209+
mutants
1210+
.iter()
1211+
.filter(|m| m.genre == Genre::MatchArmGuard)
1212+
.map(|m| m.name(true))
1213+
.collect_vec(),
1214+
[
1215+
"src/main.rs:3:17: replace match guard with true",
1216+
"src/main.rs:3:17: replace match guard with false",
1217+
"src/main.rs:6:17: replace match guard with true",
1218+
"src/main.rs:6:17: replace match guard with false",
1219+
]
1220+
);
1221+
}
11721222
}

0 commit comments

Comments
 (0)