Skip to content

Commit d9cc669

Browse files
authored
Remove ^= as mutation for |= and &= operators (#556)
## Problem When mutating bitwise assignment operators, cargo-mutants was suggesting replacing `|=` with `^=` (and `&=` with `^=`). However, this mutation is uninformative for the common pattern of accumulating bits into a bitmap that starts from zero: ```rust let base_ix = self.text.len(); self.chars |= slice.chars << base_ix; self.chars_utf16 |= slice.chars_utf16 << base_ix; ``` In this code (from Zed's `crates/rope/src/chunk.rs`), when accumulating new set bits into a bitmap that's initially zeros, `|=` and `^=` produce identical results. The mutation doesn't actually test anything different. ## Solution Changed the mutation patterns for bitwise assignment operators: - `&=` now only mutates to `|=` (removed `^=`) - `|=` now only mutates to `&=` (removed `^=`) - `^=` still mutates to both `|=` and `&=` (unchanged) This makes the mutations more meaningful by ensuring they actually change the behavior in typical usage patterns. ## Changes - Updated mutation logic in `src/visit.rs` - Updated documentation in `book/src/mutants.md` with explicit entries for bitwise assignment operators - Added changelog entry to `NEWS.md` - Updated test expectations in `mutate_assign_ops` test
2 parents 89f6546 + ffec8db commit d9cc669

File tree

3 files changed

+12
-11
lines changed

3 files changed

+12
-11
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
- New: `start_time` and `end_time` fields in `outcomes.json`.
1010

11+
- Changed: The bitwise assignment operators `&=` and `|=` are no longer mutated to `^=`. In code that accumulates bits into a bitmap starting from zero (e.g., `bitmap |= new_bits`), `|=` and `^=` produce the same result, making such mutations uninformative.
12+
1113
## 25.3.1 2025-08-10
1214

1315
- Fixed: cargo-mutants' own tests were failing on nightly due to a change in the format of messages emitted by tests.

book/src/mutants.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,17 @@ like `a == 0`.
8383
| `&` | `\|`,`^` |
8484
| `\|` | `&`, `^` |
8585
| `^` | `&`, `\|` |
86-
| `+=` and similar assignments | assignment corresponding to the line above |
86+
| `&=` | `\|=` |
87+
| `\|=` | `&=` |
88+
| `^=` | `\|=`, `&=` |
89+
| `+=`, `-=`, `*=`, `/=`, `%=`, `<<=`, `>>=` | assignment corresponding to the operator above |
8790

8891
Equality operators are not currently replaced with comparisons like `<` or `<=`
8992
because they are
9093
too prone to generate false positives, for example when unsigned integers are compared to 0.
9194

95+
The bitwise assignment operators `&=` and `|=` are not mutated to `^=` because in code that accumulates bits (e.g., `bitmap |= new_bits`), `|=` and `^=` produce the same result when starting from zero, making such mutations uninformative.
96+
9297
## Unary operators
9398

9499
Unary operators are deleted in expressions like `-a` and `!a`.

src/visit.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -591,9 +591,9 @@ impl<'ast> Visit<'ast> for DiscoveryVisitor<'_> {
591591
BinOp::Shr(_) => vec![quote! {<<}],
592592
BinOp::ShrAssign(_) => vec![quote! {<<=}],
593593
BinOp::BitAnd(_) => vec![quote! {|}, quote! {^}],
594-
BinOp::BitAndAssign(_) => vec![quote! {|=}, quote! {^=}],
594+
BinOp::BitAndAssign(_) => vec![quote! {|=}],
595595
BinOp::BitOr(_) => vec![quote! {&}, quote! {^}],
596-
BinOp::BitOrAssign(_) => vec![quote! {&=}, quote! {^=}],
596+
BinOp::BitOrAssign(_) => vec![quote! {&=}],
597597
BinOp::BitXor(_) => vec![quote! {|}, quote! {&}],
598598
BinOp::BitXorAssign(_) => vec![quote! {|=}, quote! {&=}],
599599
_ => {
@@ -1389,14 +1389,8 @@ mod test {
13891389
mutate_expr("a /= b"),
13901390
&["replace /= with %=", "replace /= with *="]
13911391
);
1392-
assert_eq!(
1393-
mutate_expr("a &= b"),
1394-
&["replace &= with |=", "replace &= with ^="]
1395-
);
1396-
assert_eq!(
1397-
mutate_expr("a |= b"),
1398-
&["replace |= with &=", "replace |= with ^="]
1399-
);
1392+
assert_eq!(mutate_expr("a &= b"), &["replace &= with |="]);
1393+
assert_eq!(mutate_expr("a |= b"), &["replace |= with &="]);
14001394
assert_eq!(
14011395
mutate_expr("a ^= b"),
14021396
&["replace ^= with |=", "replace ^= with &="]

0 commit comments

Comments
 (0)