Skip to content

Commit

Permalink
Skip new and default
Browse files Browse the repository at this point in the history
Helps with sourcefrog#25
  • Loading branch information
sourcefrog committed Mar 28, 2022
1 parent 7df9231 commit a171af0
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
mutants.out
mutants.out.old
.cargo/config.toml
wiki
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# cargo-mutants changelog

## Unreleased

- Improved: Don't attempt to mutate functions called `new` or implementations of
`Default`. cargo-mutants can not yet generate good mutations for these so they
are generally false positives.

## 0.2.4

Released 2022-03-26
Expand Down
25 changes: 22 additions & 3 deletions src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl DiscoveryVisitor {
) {
self.in_namespace(&ident.to_string(), |v| {
let function_name = Arc::new(v.namespace_stack.join("::"));
let return_type_str = Arc::new(format!("{}", return_type.to_token_stream()));
let return_type_str = Arc::new(return_type_to_string(return_type));
for op in ops_for_return_type(return_type) {
v.mutants.push(Mutant::new(
v.source_file.clone(),
Expand Down Expand Up @@ -87,22 +87,36 @@ impl<'ast> Visit<'ast> for DiscoveryVisitor {
if attrs_excluded(&i.attrs) {
return;
}
let type_name = type_name_string(&i.self_ty);
// The trait being implemented.
if let Some((_, trait_path, _)) = &i.trait_ {
let trait_name = &trait_path.segments.last().unwrap().ident;
if trait_name == "Default" {
// We don't know (yet) how to generate an interestingly-broken
// Default::default.
return;
}
}
// Make an approximately-right namespace.
let name = type_name_string(&i.self_ty);
self.in_namespace(&name, |v| syn::visit::visit_item_impl(v, i));
// TODO: For `impl X for Y` get both X and Y onto the namespace
// stack so that we can show a more descriptive name.
self.in_namespace(&type_name, |v| syn::visit::visit_item_impl(v, i));
}

/// Visit `fn foo()` within an `impl`.
fn visit_impl_item_method(&mut self, i: &'ast syn::ImplItemMethod) {
if attrs_excluded(&i.attrs) {
return;
} else if i.sig.ident == "new" {
return; // don't look inside constructors because there's often no good alternative
}
self.collect_fn_mutants(&i.sig.ident, &i.sig.output, &i.block.brace_token.span);
self.in_namespace(&i.sig.ident.to_string(), |v| {
syn::visit::visit_impl_item_method(v, i)
});
}

/// Visit `mod foo { ... }`.
fn visit_item_mod(&mut self, node: &'ast syn::ItemMod) {
if !attrs_excluded(&node.attrs) {
self.in_namespace(&node.ident.to_string(), |v| {
Expand Down Expand Up @@ -154,6 +168,11 @@ fn type_name_string(ty: &syn::Type) -> String {
}
}

fn return_type_to_string(return_type: &syn::ReturnType) -> String {
// TODO: Remove unnecessary spaces.
format!("{}", return_type.to_token_stream())
}

fn path_is_result(path: &syn::Path) -> bool {
path.segments
.last()
Expand Down
15 changes: 13 additions & 2 deletions testdata/tree/well_tested/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ impl Foo {
}
}

#[mutants::skip]
impl Default for Foo {
fn default() -> Self {
Foo { i: 1 }
Foo::new()
}
}

Expand All @@ -28,3 +27,15 @@ fn double() {
foo.double();
assert_eq!(foo.i, 128);
}

#[test]
fn default() {
let foo = Foo::default();
assert_eq!(foo.i, 32);
}

#[test]
fn new_foo() {
let foo = Foo::new();
assert_eq!(foo.i, 32);
}
9 changes: 1 addition & 8 deletions tests/snapshots/cli__list_mutants_json_well_tested.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
source: tests/cli.rs
assertion_line: 41
expression: "String::from_utf8_lossy(&output.stdout)"

---
[
{
Expand All @@ -11,13 +11,6 @@ expression: "String::from_utf8_lossy(&output.stdout)"
"return_type": "-> & 'static str",
"replacement": "Default::default()"
},
{
"file": "src/methods.rs",
"line": 6,
"function": "Foo::new",
"return_type": "-> Foo",
"replacement": "Default::default()"
},
{
"file": "src/methods.rs",
"line": 10,
Expand Down
3 changes: 1 addition & 2 deletions tests/snapshots/cli__list_mutants_well_tested.snap
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
---
source: tests/cli.rs
assertion_line: 41
expression: "String::from_utf8_lossy(&output.stdout)"

---
src/inside_mod.rs:3: replace outer::inner::name -> & 'static str with Default::default()
src/methods.rs:6: replace Foo::new -> Foo with Default::default()
src/methods.rs:10: replace Foo::double with ()
src/nested_function.rs:1: replace has_nested -> u32 with Default::default()
src/nested_function.rs:2: replace has_nested::inner -> u32 with Default::default()
Expand Down
4 changes: 2 additions & 2 deletions tests/snapshots/cli__well_tested_tree_check_only.snap
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
source: tests/cli.rs
assertion_line: 240
expression: stdout
---
Freshen source tree ... ok
Copy source and build products to scratch directory ... done
Unmutated baseline ... ok
Found 13 mutants to test
Found 12 mutants to test
src/inside_mod.rs:3: replace outer::inner::name -> & 'static str with Default::default() ... check ok
src/methods.rs:6: replace Foo::new -> Foo with Default::default() ... check ok
src/methods.rs:10: replace Foo::double with () ... check ok
src/nested_function.rs:1: replace has_nested -> u32 with Default::default() ... check ok
src/nested_function.rs:2: replace has_nested::inner -> u32 with Default::default() ... check ok
Expand Down
4 changes: 2 additions & 2 deletions tests/snapshots/cli__well_tested_tree_finds_no_problems.snap
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
source: tests/cli.rs
assertion_line: 221
expression: stdout
---
Freshen source tree ... ok
Copy source and build products to scratch directory ... done
Unmutated baseline ... ok
Found 13 mutants to test
Found 12 mutants to test
src/inside_mod.rs:3: replace outer::inner::name -> & 'static str with Default::default() ... caught
src/methods.rs:6: replace Foo::new -> Foo with Default::default() ... caught
src/methods.rs:10: replace Foo::double with () ... caught
src/nested_function.rs:1: replace has_nested -> u32 with Default::default() ... caught
src/nested_function.rs:2: replace has_nested::inner -> u32 with Default::default() ... caught
Expand Down
3 changes: 2 additions & 1 deletion tests/snapshots/cli__well_tested_tree_quiet.snap
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
source: tests/cli.rs
assertion_line: 199
expression: stdout
---
Freshen source tree ... ok
Copy source and build products to scratch directory ... done
Unmutated baseline ... ok
Found 13 mutants to test
Found 12 mutants to test

0 comments on commit a171af0

Please sign in to comment.