Skip to content

Commit 9b31451

Browse files
committed
News about name in json
2 parents 2fcb00b + 1e4f5ae commit 9b31451

10 files changed

+182
-28
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cargo-mutants"
3-
version = "26.0.0"
3+
version = "26.0.1"
44
edition = "2021"
55
authors = ["Martin Pool"]
66
license = "MIT"

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# cargo-mutants changelog
22

3+
## Unreleased
4+
5+
- New: Added a `name` field in the JSON representation of mutants. Thanks to @0xLoopTheory.
6+
37
## 26.0.0 2025-12-07
48

59
- Changed: The default is now *not* to shuffle mutants: they run in the deterministic order they are generated in the source tree. This should give somewhat better locality of reference due to consecutively testing changes in each package or module. The previous behavior can be restored with `--shuffle`.

src/mutant.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ pub enum MutationTarget {
4949
/// A mutation applied to source code.
5050
#[derive(Clone, Eq, PartialEq, Debug)]
5151
pub struct Mutant {
52+
/// A precomputed human-readable name for this mutant, including the file,
53+
/// location, and change description.
54+
///
55+
/// This is used in CLI output, filtering, and JSON.
56+
pub name: String,
57+
5258
/// Which file is being mutated.
5359
pub source_file: SourceFile,
5460

@@ -103,6 +109,34 @@ pub struct Function {
103109
}
104110

105111
impl Mutant {
112+
/// Construct a mutant discovered while walking source code.
113+
///
114+
/// This initializes all fields and precomputes the human-readable `name`
115+
/// (including file path, line/column, and change description) for use in
116+
/// CLI output, filtering, and JSON.
117+
pub(crate) fn new_discovered(
118+
source_file: SourceFile,
119+
function: Option<Arc<Function>>,
120+
span: Span,
121+
short_replaced: Option<String>,
122+
replacement: String,
123+
genre: Genre,
124+
target: Option<MutationTarget>,
125+
) -> Self {
126+
let mut mutant = Mutant {
127+
name: String::new(),
128+
source_file,
129+
function,
130+
span,
131+
short_replaced,
132+
replacement,
133+
genre,
134+
target,
135+
};
136+
mutant.name = mutant.name(true);
137+
mutant
138+
}
139+
106140
/// Return text of the whole file with the mutation applied.
107141
pub fn mutated_code(&self) -> String {
108142
self.span.replace(
@@ -293,6 +327,7 @@ impl Serialize for Mutant {
293327
{
294328
// custom serialize to omit inessential info
295329
let mut ss = serializer.serialize_struct("Mutant", 7)?;
330+
ss.serialize_field("name", &self.name)?;
296331
ss.serialize_field("package", &self.source_file.package.name)?;
297332
ss.serialize_field("file", &self.source_file.tree_relative_slashes())?;
298333
ss.serialize_field("function", &self.function.as_ref().map(Arc::as_ref))?;

src/visit.rs

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,9 @@ pub struct Discovered {
4444
impl Discovered {
4545
pub(crate) fn remove_previously_caught(&mut self, previously_caught: &[String]) {
4646
self.mutants.retain(|m| {
47-
let name = m.name(true);
48-
let c = previously_caught.contains(&name);
47+
let c = previously_caught.contains(&m.name);
4948
if c {
50-
trace!(?name, "skip previously caught mutant");
49+
trace!(name = %m.name, "skip previously caught mutant");
5150
}
5251
!c
5352
});
@@ -75,6 +74,7 @@ pub fn walk_tree(
7574
mutants.append(&mut package_mutants);
7675
files.append(&mut package_files);
7776
}
77+
7878
progress.finish();
7979
Ok(Discovered { mutants, files })
8080
}
@@ -318,15 +318,16 @@ impl DiscoveryVisitor<'_> {
318318

319319
/// Record that we generated some mutants.
320320
fn collect_mutant(&mut self, span: Span, replacement: &TokenStream, genre: Genre) {
321-
self.mutants.push(Mutant {
322-
source_file: self.source_file.clone(),
323-
function: self.fn_stack.last().cloned(),
321+
let mutant = Mutant::new_discovered(
322+
self.source_file.clone(),
323+
self.fn_stack.last().cloned(),
324324
span,
325-
short_replaced: None,
326-
replacement: replacement.to_pretty_string(),
325+
None,
326+
replacement.to_pretty_string(),
327327
genre,
328-
target: None,
329-
});
328+
None,
329+
);
330+
self.mutants.push(mutant);
330331
}
331332

332333
fn collect_fn_mutants(&mut self, sig: &Signature, block: &Block) {
@@ -656,15 +657,15 @@ impl<'ast> Visit<'ast> for DiscoveryVisitor<'_> {
656657
continue;
657658
}
658659
let short_replaced = Some(arm.pat.to_pretty_string());
659-
let mutant = Mutant {
660-
source_file: self.source_file.clone(),
661-
function: self.fn_stack.last().cloned(),
662-
span: arm.span().into(),
660+
let mutant = Mutant::new_discovered(
661+
self.source_file.clone(),
662+
self.fn_stack.last().cloned(),
663+
arm.span().into(),
663664
short_replaced,
664-
replacement: String::new(),
665-
genre: Genre::MatchArm,
666-
target: None,
667-
};
665+
String::new(),
666+
Genre::MatchArm,
667+
None,
668+
);
668669
self.mutants.push(mutant);
669670
}
670671
} else {
@@ -722,18 +723,18 @@ impl<'ast> Visit<'ast> for DiscoveryVisitor<'_> {
722723
// No comma, just the field
723724
field.span().into()
724725
};
725-
let mutant = Mutant {
726-
source_file: self.source_file.clone(),
727-
function: self.fn_stack.last().cloned(),
726+
let mutant = Mutant::new_discovered(
727+
self.source_file.clone(),
728+
self.fn_stack.last().cloned(),
728729
span,
729-
short_replaced: None,
730-
replacement: String::new(),
731-
genre: Genre::StructField,
732-
target: Some(MutationTarget::StructLiteralField {
730+
None,
731+
String::new(),
732+
Genre::StructField,
733+
Some(MutationTarget::StructLiteralField {
733734
field_name: field_name_str,
734735
struct_name: struct_name.clone(),
735736
}),
736-
};
737+
);
737738
self.mutants.push(mutant);
738739
}
739740
}

tests/snapshots/main__mutants.json.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ expression: mutants_json
44
---
55
[
66
{
7+
"name": "src/bin/factorial.rs:2:5: replace main with ()",
78
"package": "cargo-mutants-testdata-factorial",
89
"file": "src/bin/factorial.rs",
910
"function": {
@@ -34,6 +35,7 @@ expression: mutants_json
3435
"genre": "FnValue"
3536
},
3637
{
38+
"name": "src/bin/factorial.rs:8:5: replace factorial -> u32 with 0",
3739
"package": "cargo-mutants-testdata-factorial",
3840
"file": "src/bin/factorial.rs",
3941
"function": {
@@ -64,6 +66,7 @@ expression: mutants_json
6466
"genre": "FnValue"
6567
},
6668
{
69+
"name": "src/bin/factorial.rs:8:5: replace factorial -> u32 with 1",
6770
"package": "cargo-mutants-testdata-factorial",
6871
"file": "src/bin/factorial.rs",
6972
"function": {
@@ -94,6 +97,7 @@ expression: mutants_json
9497
"genre": "FnValue"
9598
},
9699
{
100+
"name": "src/bin/factorial.rs:10:11: replace *= with += in factorial",
97101
"package": "cargo-mutants-testdata-factorial",
98102
"file": "src/bin/factorial.rs",
99103
"function": {
@@ -124,6 +128,7 @@ expression: mutants_json
124128
"genre": "BinaryOperator"
125129
},
126130
{
131+
"name": "src/bin/factorial.rs:10:11: replace *= with /= in factorial",
127132
"package": "cargo-mutants-testdata-factorial",
128133
"file": "src/bin/factorial.rs",
129134
"function": {

tests/util/snapshots/main__util__list_mutants_in_cfg_attr_test_skip_json.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
2020
}
2121
},
2222
"genre": "FnValue",
23+
"name": "src/lib.rs:18:5: replace double -> usize with 0",
2324
"package": "cargo-mutants-testdata-cfg-attr-test-skip",
2425
"replacement": "0",
2526
"span": {
@@ -50,6 +51,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
5051
}
5152
},
5253
"genre": "FnValue",
54+
"name": "src/lib.rs:18:5: replace double -> usize with 1",
5355
"package": "cargo-mutants-testdata-cfg-attr-test-skip",
5456
"replacement": "1",
5557
"span": {
@@ -80,6 +82,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
8082
}
8183
},
8284
"genre": "BinaryOperator",
85+
"name": "src/lib.rs:18:7: replace * with + in double",
8386
"package": "cargo-mutants-testdata-cfg-attr-test-skip",
8487
"replacement": "+",
8588
"span": {
@@ -110,6 +113,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
110113
}
111114
},
112115
"genre": "BinaryOperator",
116+
"name": "src/lib.rs:18:7: replace * with / in double",
113117
"package": "cargo-mutants-testdata-cfg-attr-test-skip",
114118
"replacement": "/",
115119
"span": {

tests/util/snapshots/main__util__list_mutants_in_factorial_json.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
2020
}
2121
},
2222
"genre": "FnValue",
23+
"name": "src/bin/factorial.rs:2:5: replace main with ()",
2324
"package": "cargo-mutants-testdata-factorial",
2425
"replacement": "()",
2526
"span": {
@@ -50,6 +51,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
5051
}
5152
},
5253
"genre": "FnValue",
54+
"name": "src/bin/factorial.rs:8:5: replace factorial -> u32 with 0",
5355
"package": "cargo-mutants-testdata-factorial",
5456
"replacement": "0",
5557
"span": {
@@ -80,6 +82,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
8082
}
8183
},
8284
"genre": "FnValue",
85+
"name": "src/bin/factorial.rs:8:5: replace factorial -> u32 with 1",
8386
"package": "cargo-mutants-testdata-factorial",
8487
"replacement": "1",
8588
"span": {
@@ -110,6 +113,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
110113
}
111114
},
112115
"genre": "BinaryOperator",
116+
"name": "src/bin/factorial.rs:10:11: replace *= with += in factorial",
113117
"package": "cargo-mutants-testdata-factorial",
114118
"replacement": "+=",
115119
"span": {
@@ -140,6 +144,7 @@ expression: "String::from_utf8_lossy(&output.stdout)"
140144
}
141145
},
142146
"genre": "BinaryOperator",
147+
"name": "src/bin/factorial.rs:10:11: replace *= with /= in factorial",
143148
"package": "cargo-mutants-testdata-factorial",
144149
"replacement": "/=",
145150
"span": {

0 commit comments

Comments
 (0)