Skip to content

Commit

Permalink
Mutate proc macro functions (#415)
Browse files Browse the repository at this point in the history
- [x] Docs explaining that proc macro implementations can be mutated,
but this only mutates the definitions and not the usage of macros
- [x] News
- [x] Maybe test that the testdata crate is fully covered?

Fixes #406
  • Loading branch information
sourcefrog authored Oct 1, 2024
2 parents 48fa476 + bacb349 commit f111144
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 5 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ exclude = [
"testdata/override_dependency",
"testdata/package-fails/",
"testdata/patch_dependency",
"testdata/proc_macro",
"testdata/relative_dependency",
"testdata/replace_dependency",
"testdata/small_well_tested",
Expand Down
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: Mutate `proc_macro` targets and functions.

## 24.9.0

- Fixed: Avoid generating empty string elements in `ENCODED_RUSTFLAGS` when `--cap-lints` is set. In some situations these could cause a compiler error complaining about the empty argument.
Expand Down
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- [Strict lints](lints.md)
- [Generating mutants](mutants.md)
- [Error values](error-values.md)
- [Macros](macros.md)
- [Improving performance](performance.md)
- [Parallelism](parallelism.md)
- [Jobserver](jobserver.md)
Expand Down
5 changes: 5 additions & 0 deletions book/src/macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Mutating code using macros

cargo-mutants will mutate the contents of `#[proc_macro]` functions defined in the current crate, and run tests to see if those mutations are caught.

cargo-mutants does not currently mutate calls to macros, or the expansion of a macro, or the definition of declarative `macro_rules` macros. As a result on code that is mostly produced by macro expansion it may not find many mutation opportunities.
7 changes: 6 additions & 1 deletion src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,12 @@ fn direct_package_sources(
}

fn should_mutate_target(target: &cargo_metadata::Target) -> bool {
target.kind.iter().any(|k| k.ends_with("lib") || k == "bin")
for kind in target.kind.iter() {
if kind == "bin" || kind == "proc-macro" || kind.ends_with("lib") {
return true;
}
}
false
}

/// Return the path of the workspace or package directory enclosing a given directory.
Expand Down
12 changes: 12 additions & 0 deletions testdata/proc_macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "cargo-mutants-testdata-proc-macro"
version = "0.0.0"
edition = "2021"
license = "MIT"
publish = false

[lib]
proc-macro = true

[dependencies]
quote = "1.0"
3 changes: 3 additions & 0 deletions testdata/proc_macro/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# `testdata/proc_macro`

An example of mutating a proc macro, and catching those mutations from tests.
13 changes: 13 additions & 0 deletions testdata/proc_macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::iter::once;

use proc_macro::{Literal, TokenStream, TokenTree};

/// Count the number of items in a static array.
#[proc_macro]
pub fn static_len(item: TokenStream) -> TokenStream {
let count = item
.into_iter()
.filter(|tt| !matches!(tt, TokenTree::Punct(p) if p.as_char() == ','))
.count();
once(TokenTree::Literal(Literal::usize_unsuffixed(count))).collect()
}
11 changes: 11 additions & 0 deletions testdata/proc_macro/tests/macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use cargo_mutants_testdata_proc_macro::static_len;

#[test]
fn static_len() {
assert_eq!(static_len!(2, 3, 4, 5), 4);
}

#[test]
fn static_len_empty() {
assert_eq!(static_len!(), 0);
}
14 changes: 14 additions & 0 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ fn cdylib_tree_is_well_tested() {
}));
}

#[test]
fn proc_macro_tree_is_well_tested() {
let tmp_src_dir = copy_of_testdata("proc_macro");
run()
.arg("mutants")
.args(["--no-times", "--no-shuffle", "-v", "-V"])
.current_dir(tmp_src_dir.path())
.assert()
.success()
.stdout(predicate::str::contains(
"2 mutants tested: 1 caught, 1 unviable",
));
}

#[test]
fn well_tested_tree_finds_no_problems() {
let tmp_src_dir = copy_of_testdata("well_tested");
Expand Down
69 changes: 67 additions & 2 deletions tests/snapshots/list__list_mutants_in_all_trees_as_json.snap
Original file line number Diff line number Diff line change
Expand Up @@ -4768,6 +4768,73 @@ expression: buf
]
```

## testdata/proc_macro

```json
[
{
"file": "src/lib.rs",
"function": {
"function_name": "static_len",
"return_type": "-> TokenStream",
"span": {
"end": {
"column": 2,
"line": 13
},
"start": {
"column": 1,
"line": 5
}
}
},
"genre": "FnValue",
"package": "cargo-mutants-testdata-proc-macro",
"replacement": "Default::default()",
"span": {
"end": {
"column": 73,
"line": 12
},
"start": {
"column": 5,
"line": 8
}
}
},
{
"file": "src/lib.rs",
"function": {
"function_name": "static_len",
"return_type": "-> TokenStream",
"span": {
"end": {
"column": 2,
"line": 13
},
"start": {
"column": 1,
"line": 5
}
}
},
"genre": "UnaryOperator",
"package": "cargo-mutants-testdata-proc-macro",
"replacement": "",
"span": {
"end": {
"column": 23,
"line": 10
},
"start": {
"column": 22,
"line": 10
}
}
}
]
```

## testdata/relative_dependency

```json
Expand Down Expand Up @@ -9611,5 +9678,3 @@ expression: buf
}
]
```


9 changes: 7 additions & 2 deletions tests/snapshots/list__list_mutants_in_all_trees_as_text.snap
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,13 @@ src/lib.rs:7:7: replace % with / in is_even
src/lib.rs:7:7: replace % with + in is_even
```
## testdata/proc_macro
```
src/lib.rs:8:5: replace static_len -> TokenStream with Default::default()
src/lib.rs:10:22: delete ! in static_len
```
## testdata/relative_dependency
```
Expand Down Expand Up @@ -523,5 +530,3 @@ main2/src/main.rs:10:5: replace triple_3 -> i32 with 0
main2/src/main.rs:10:5: replace triple_3 -> i32 with 1
main2/src/main.rs:10:5: replace triple_3 -> i32 with -1
```

0 comments on commit f111144

Please sign in to comment.