Skip to content

Mutate match arms#485

Merged
sourcefrog merged 3 commits intosourcefrog:mainfrom
zaneduffield:mutate-match
Feb 9, 2025
Merged

Mutate match arms#485
sourcefrog merged 3 commits intosourcefrog:mainfrom
zaneduffield:mutate-match

Conversation

@zaneduffield
Copy link
Contributor

Implements #484.

I created just one new genre for the two new kinds of mutations here, but maybe it should be two new genres?

Another thing I wasn't sure about was whether and how to change Mutant::styled_parts to more simply render the new mutations. If the match arm or guard being mutated is long then the output could become quite hard to read.

With this new mutation genre, there are some uncovered mutations in the cargo-mutants source tree that I've added a commit to address. A couple of things about that are worth noting

  1. I removed a match arm in src/fnvalue.rs that I think was redundant. It's possible that it deserves to be there as an optimisation though, which would make it hard to cover.
  2. I wasn't able to add a test to cover the mutation on a match arm relating to Cargo `default-members.
    Ok(default_packages) if default_packages.is_empty() => {
    debug!("manifest has no explicit default packages");
    PackageSelection::All
    }

    I think this might be redundant as well. The docs say that the default value for this property is non-empty, which would mean that for it to be empty would require it to actually be empty:

    When unspecified, the root package will be used. In the case of a virtual workspace, all members will be used (as if --workspace were specified on the command-line).

@sourcefrog sourcefrog self-assigned this Jan 21, 2025
Copy link
Owner

@sourcefrog sourcefrog left a comment

Choose a reason for hiding this comment

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

Implements #484.

Thanks, this looks good! We should also document the new patterns in https://github.com/sourcefrog/cargo-mutants/blob/main/book/src/mutants.md

I created just one new genre for the two new kinds of mutations here, but maybe it should be two new genres?

I think two distinct new genres is better.

Another thing I wasn't sure about was whether and how to change Mutant::styled_parts to more simply render the new mutations. If the match arm or guard being mutated is long then the output could become quite hard to read.

Probably the rendering code should know to abbreviate long replacements, and just say "replace match arm with xxx". In a sense it has this special coded for functions now, since it doesn't quote the whole of the function body that's being replaced... But, let's not do that in this PR.

With this new mutation genre, there are some uncovered mutations in the cargo-mutants source tree that I've added a commit to address. A couple of things about that are worth noting

  1. I removed a match arm in src/fnvalue.rs that I think was redundant. It's possible that it deserves to be there as an optimisation though, which would make it hard to cover.

That's great that it found this! I think letting it hit the general non-empty case is fine: the iterator will be empty and that should finish quickly. I think the code you removed is just a relic of a time when it didn't know how to expand tuples, only to replace unit types.

  1. I wasn't able to add a test to cover the mutation on a match arm relating to Cargo `default-members.

    Ok(default_packages) if default_packages.is_empty() => {
    debug!("manifest has no explicit default packages");
    PackageSelection::All
    }

    I think this might be redundant as well. The docs say that the default value for this property is non-empty, which would mean that for it to be empty would require it to actually be empty:

    When unspecified, the root package will be used. In the case of a virtual workspace, all members will be used (as if --workspace were specified on the command-line).

Yes I guess we could just delete that arm.

}

impl Foo {
#[mutants::skip]
Copy link
Owner

Choose a reason for hiding this comment

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

I think this addition to the testdata is harmless but we don't exactly need it... it's just proving again that mutants::skip works...?

Or was there a coverage gap without it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I started adding this when I thought it was possible to add #[mutants::skip] to a statement. I'll delete it.

}
}

// Don't use the value of the match expression, because we don't expect it to be mutated.
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
// Don't use the value of the match expression, because we don't expect it to be mutated.
// This match expression has no default so won't be mutated.
// This doesn't count as a missed mutant because the function
// return value doesn't depend on the match expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

deleted

fn in_workspace_only_default_members_are_mutated_if_present() {
let tmp = copy_of_testdata("workspace_default_members");
run()
.args(["mutants", "--no-shuffle", "-d"])
Copy link
Owner

Choose a reason for hiding this comment

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

Maybe this should just run with --list which will be a bit faster: we've probably sufficiently tested elsewhere that running tests from packages works.

fn in_workspace_only_default_members_are_mutated_if_present() {
let tmp = copy_of_testdata("workspace_default_members");
run()
.args(["mutants", "--no-shuffle", "-d"])
Copy link
Owner

Choose a reason for hiding this comment

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

Maybe this should just run with --list which will be a bit faster: we've probably sufficiently tested elsewhere that running tests from packages works.

@sourcefrog
Copy link
Owner

I see some mutants come out like this:

src/process.rs:187:17: delete ' ' | '\\' | '\'' | '"' => {
                    r.push('\\');
                    r.push(c);
                } in quote_argv

src/fnvalue.rs:186:9: delete Type::Reference(syn::TypeReference {
            mutability: Some(_),
            elem,
            ..
        }) => match &**elem {
            Type::Slice(TypeSlice { elem, .. }) => iter::once(quote! { Vec::leak(Vec::new()) })
                .chain(
                    type_replacements(elem, error_exprs).map(|r| quote! { Vec::leak(vec![ #r ]) }),
                )
                .collect_vec(),
            _ => {
                // Make &mut with static lifetime by leaking them on the heap.
                type_replacements(elem, error_exprs)
                    .map(|rep| {
                        quote! { Box::leak(Box::new(#rep)) }
                    })
                    .collect_vec()
            }
        }, in type_replacements

and others are even longer.

Maybe we should just change this to "delete match arm" or "replace match guard with true".

@zaneduffield
Copy link
Contributor Author

Thanks, this looks good! We should also document the new patterns in https://github.com/sourcefrog/cargo-mutants/blob/main/book/src/mutants.md

I've addressed all the comments but this. I won't have any time to work on this until next week, though.

@zaneduffield
Copy link
Contributor Author

I updated the mutants.md file.

Copy link
Owner

@sourcefrog sourcefrog left a comment

Choose a reason for hiding this comment

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

Thanks, looks good, I just thought of one other thing.

@sourcefrog sourcefrog merged commit 64e75eb into sourcefrog:main Feb 9, 2025
25 checks passed
@zaneduffield zaneduffield deleted the mutate-match branch February 9, 2025 03:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants