Repository: sourcefrog/cargo-mutants (rust)

Issue:
Clean warning and success if the `--in-diff` diff file is empty?
I think in https://github.com/sourcefrog/conserve/actions/runs/8676081693/job/23790302177 the diff was empty and mutants failed, which is probably not what you want.

Pull request description:
Don't error on an empty --in-diff file
Fixes #331

Patch:
diff --git a/NEWS.md b/NEWS.md
index 196d91f..d02f5d4 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -6,6 +6,8 @@
 
 - Changed: No build timeouts by default. Previously, cargo-mutants set a default build timeout based on the baseline build, but experience showed that this would sometimes make builds flaky, because build times can be quite variable. If mutants cause builds to hang, then you can still set a timeout using `--build-timeout` or `--build-timeout-multiplier`.
 
+- Fixed: Don't error if the `--in-diff` file is empty.
+
 - Changed: cargo-mutants no longer passes `--cap-lints=allow` to rustc. This was previously done so that mutants would not unnecessarily be unviable due to triggering compiler warnings in trees configured to deny some lints, but it had the undesirable effect of disabling rustc's detection of long running const evaluations. If your tree treats some lints as errors then the previous behavior can be restored with `--cap-lints=true` (or the equivalent config option), or you can use `cfg_attr` and a feature flag  to accept those warnings when testing under cargo-mutants.
 
 ## 24.5.0
diff --git a/book/src/lints.md b/book/src/lints.md
index 739f187..97a8901 100644
--- a/book/src/lints.md
+++ b/book/src/lints.md
@@ -6,7 +6,7 @@ For example, mutants that delete code are likely to cause some parameters to be
 
 There are a few possible solutions:
 
-1.  Define a feature flag for mutation testing, and use `cfg_attr` to enable strict warnings only when not testing mutants.
+1. Define a feature flag for mutation testing, and use `cfg_attr` to enable strict warnings only when not testing mutants.
 2. Use the `cargo mutants --cap-lints=true` command line option, or the `cap_lints = true` config option.
 
 `--cap_lints=true` also disables rustc's detection of long-running const expression evaluation, so may cause some builds to fail. If that happens in your tree, you can set a [build timeout](timeouts.md).
diff --git a/src/in_diff.rs b/src/in_diff.rs
index b7be4e9..755d32d 100644
--- a/src/in_diff.rs
+++ b/src/in_diff.rs
@@ -10,7 +10,7 @@ use camino::Utf8Path;
 use indoc::formatdoc;
 use itertools::Itertools;
 use patch::{Line, Patch};
-use tracing::{trace, warn};
+use tracing::{info, trace, warn};
 
 use crate::mutate::Mutant;
 use crate::source::SourceFile;
@@ -19,6 +19,10 @@ use crate::Result;
 /// Return only mutants to functions whose source was touched by this diff.
 pub fn diff_filter(mutants: Vec<Mutant>, diff_text: &str) -> Result<Vec<Mutant>> {
     // Flatten the error to a string because otherwise it references the diff, and can't be returned.
+    if diff_text.trim().is_empty() {
+        info!("diff file is empty; no mutants will match");
+        return Ok(Vec::new());
+    }
     let patches =
         Patch::from_multiple(diff_text).map_err(|err| anyhow!("Failed to parse diff: {err}"))?;
     check_diff_new_text_matches(&patches, &mutants)?;


Test patch:
diff --git a/tests/in_diff.rs b/tests/in_diff.rs
index 6f0af9c..81a0eb5 100644
--- a/tests/in_diff.rs
+++ b/tests/in_diff.rs
@@ -4,6 +4,7 @@ use std::fs::read_to_string;
 use std::io::Write;
 
 use indoc::indoc;
+use predicates::prelude::predicate;
 use similar::TextDiff;
 use tempfile::NamedTempFile;
 
@@ -68,6 +69,22 @@ fn list_mutants_changed_in_diff1() {
     );
 }
 
+#[test]
+fn empty_diff_is_not_an_error_and_matches_nothing() {
+    let diff_file = NamedTempFile::new().unwrap();
+    let tmp = copy_of_testdata("diff1");
+    run()
+        .args(["mutants", "--no-shuffle", "-d"])
+        .arg(tmp.path())
+        .arg("--in-diff")
+        .arg(diff_file.path())
+        .arg("--list")
+        .assert()
+        .success()
+        .stdout("")
+        .stderr(predicate::str::contains("diff file is empty"));
+}
+
 /// If the text in the diff doesn't look like the tree then error out.
 #[test]
 fn mismatched_diff_causes_error() {