Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# cargo-mutants changelog

## Unreleased

- New: `#[mutants::exclude_re("pattern")]` attribute to exclude specific mutations by regex, without disabling all mutations on the function. The attribute can be placed on functions, `impl` blocks, `trait` blocks, modules, and files. Multiple patterns can be applied. Also supported within `cfg_attr`.

## 27.0.0

Released 2026-03-07.
Expand Down
56 changes: 56 additions & 0 deletions book/src/attrs.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,59 @@ mod test {
}
}
```

## Excluding specific mutations with an attribute

If `#[mutants::skip]` is too broad (it disables _all_ mutations on a function)
you can use `#[mutants::exclude_re("pattern")]` to exclude only mutations
whose name matches a regex, while keeping the rest.

The regex is matched against the full mutant name (the same string shown by
`cargo mutants --list`), using the same syntax as `--exclude-re` on the command
line.

For example, to keep all mutations except the "replace with ()" return-value
mutation:

```rust
#[mutants::exclude_re(r"with \(\)")]
Comment on lines +63 to +67
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I thought so too.

fn do_something(x: i32) -> i32 {
x + 1
}
```

Multiple attributes can be applied to exclude several patterns:

```rust
#[mutants::exclude_re("with 0")]
#[mutants::exclude_re("with 1")]
fn compute(a: i32, b: i32) -> i32 {
a + b
}
```

As with `mutants::skip`, cargo-mutants also looks for `mutants::exclude_re`
within other attributes such as `cfg_attr`, without evaluating the outer
attribute:

```rust
#[cfg_attr(test, mutants::exclude_re("replace .* -> bool"))]
fn is_valid(&self) -> bool {
// ...
true
}
```

### Scope

`#[mutants::exclude_re]` can be placed on:

- **Functions** — applies to all mutations within that function.
- **`impl` blocks** — applies to all methods within the block.
- **`trait` blocks** — applies to all default method implementations.
- **`mod` blocks** — applies to all items within the module.
- **Files** (as an inner attribute `#![mutants::exclude_re("...")]`) — applies to the entire file.

Patterns from outer scopes are inherited: if an `impl` block excludes a pattern,
all methods inside also exclude that pattern, in addition to any patterns on the
methods themselves.
22 changes: 22 additions & 0 deletions mutants_attrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,25 @@ use proc_macro::TokenStream;
pub fn skip(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We ought to also bump the version of mutants_attrs/Cargo.toml.

/// Exclude specific mutations matching a regex pattern.
///
/// Unlike [macro@skip], which skips all mutations on a function, this attribute allows
/// you to exclude only mutations whose name matches the given regex, while keeping
/// other mutations active.
///
/// This can be applied to functions, impl blocks, trait blocks, modules, etc.
///
/// ```
/// #[mutants::exclude_re("delete match arm")]
/// pub fn some_function() -> i32 {
/// // ...
/// # 0
/// }
/// ```
///
/// This is a no-op during compilation, but is seen by cargo-mutants as it processes the source.
#[proc_macro_attribute]
pub fn exclude_re(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}
Loading
Loading