Measure test modules in coverage, excluding only failure lines
Excluding whole #[cfg(test)] modules was overly broad: coverage is
also what proves the tests themselves run. Test code is measured
again, with two line patterns excluded that a passing suite can
never execute — panic! lines (test-failure diagnostics; the clippy
panic lint keeps panic! out of library code) and a lone ')?;'
closing a multi-line fallible call, whose only LLVM region is the
never-taken error branch. The remaining once-uncovered test lines
now genuinely run: predicate matches with dead '_ => false' arms
became matches! one-liners, the LCS window destructure became a
let-else, and the trees_with recursion base case is asserted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01565B66CMh3HTjm3124SLpB
change
commit 2684dcb437061bdd424e009b9c052438c5759026
author Claude <noreply@anthropic.com>
date
parent 26c63457
diff --git a/bin/coverage b/bin/coverage
index fd5e027..0955b3d 100755
--- a/bin/coverage
+++ b/bin/coverage
@@ -5,10 +5,14 @@
 # normal build artifacts never mix — mixing causes phantom uncovered
 # lines. Override the gate with COVERAGE_THRESHOLD (defaults to 100).
 #
-# `#[cfg(test)]` modules are excluded from measurement (they sit at
-# the tail of each file, so the unterminated excl-start runs to EOF):
-# their panic! arms and `?` branches never run in a passing suite and
-# would force a lowered gate for lines that are not library code.
+# Test modules are measured too — that is what proves the tests
+# themselves run. Two line patterns are excluded because a passing
+# suite can never execute them: `panic!` lines (test-failure
+# diagnostics; the clippy panic lint keeps panic! out of library
+# code), and a lone `)?;` closing a multi-line fallible call (LLVM
+# attributes only the `?` error-branch region to that line, so it
+# reads uncovered exactly when the call never fails — the same
+# region on a single-line call is invisible to line coverage).
 #
 # Scaffolded into a project as bin/coverage; the `coverage` justfile
 # recipe runs it. See the rust skill's coverage.md and scaffolding.md.
@@ -37,8 +41,8 @@ main() {
         --ignore-not-existing \
         --keep-only 'src/**' \
         --ignore 'src/bin/**' \
-        --excl-line 'cov-excl-line|unreachable!' \
-        --excl-start 'cov-excl-start|#\[cfg\(test\)\]' \
+        --excl-line 'cov-excl-line|unreachable!|panic!|^\s*\)\?;$' \
+        --excl-start 'cov-excl-start' \
         --excl-stop 'cov-excl-stop')
 
     # Per-file breakdown: walk the covdir tree, printing leaf files only.
diff --git a/src/check.rs b/src/check.rs
index 1fb5432..6c310fa 100644
--- a/src/check.rs
+++ b/src/check.rs
@@ -287,9 +287,9 @@ mod tests {
         );
         // The 9 itself is among the witnesses.
         let m = parse_json("[1, 9]")?;
-        let witnesses_nine = report.violations.iter().any(|v| match v {
-            Violation::ExtraInsertion { span, .. } => m.source_slice(span.clone()) == Some("9"),
-            _ => false,
+        let witnesses_nine = report.violations.iter().any(|v| {
+            matches!(v, Violation::ExtraInsertion { span, .. }
+                if m.source_slice(span.clone()) == Some("9"))
         });
         assert!(witnesses_nine);
         Ok(())
@@ -381,9 +381,9 @@ mod tests {
         )));
         // The witness is A's new label.
         let a = parse_json("[2]")?;
-        assert!(report.violations.iter().any(|v| match v {
-            Violation::MissedRelabel { span, .. } => a.source_slice(span.clone()) == Some("2"),
-            _ => false,
+        assert!(report.violations.iter().any(|v| {
+            matches!(v, Violation::MissedRelabel { span, .. }
+                if a.source_slice(span.clone()) == Some("2"))
         }));
         // The merge that lands the rename passes.
         let report = check_json("[1]", "[2]", "[1]", "[2]")?;
@@ -434,9 +434,9 @@ mod tests {
         let report = check_json("[1]", "[1]", "[1]", "[9]")?;
         assert!(!report.violations.is_empty());
         let m = parse_json("[9]")?;
-        assert!(report.violations.iter().any(|v| match v {
-            Violation::ExtraRelabel { span, .. } => m.source_slice(span.clone()) == Some("9"),
-            _ => false,
+        assert!(report.violations.iter().any(|v| {
+            matches!(v, Violation::ExtraRelabel { span, .. }
+                if m.source_slice(span.clone()) == Some("9"))
         }));
         Ok(())
     }
@@ -560,15 +560,10 @@ mod tests {
             r#"{"a": 1, "b": 2, "d": 4}"#,
         )?;
         assert!(!report.violations.is_empty());
-        assert!(report.violations.iter().any(|v| match v {
-            Violation::ExtraInsertion { span, .. } => {
-                let m = parse_json(r#"{"a": 1, "b": 2, "d": 4}"#);
-                m.is_ok_and(|m| {
-                    m.source_slice(span.clone())
-                        .is_some_and(|s| s.contains("4"))
-                })
-            }
-            _ => false,
+        let m = parse_json(r#"{"a": 1, "b": 2, "d": 4}"#)?;
+        assert!(report.violations.iter().any(|v| {
+            matches!(v, Violation::ExtraInsertion { span, .. }
+                if m.source_slice(span.clone()).is_some_and(|s| s.contains("4")))
         }));
         Ok(())
     }
@@ -579,12 +574,10 @@ mod tests {
         // element (and its comma) both branches kept flags the comma
         // too — the exemption must not reach it.
         let report = check_json("[1, 2]", "[1, 2]", "[1, 2]", "[1]")?;
-        let flags_comma = report.violations.iter().any(|v| match v {
-            Violation::ExtraDeletion { span, .. } => {
-                let o = parse_json("[1, 2]");
-                o.is_ok_and(|o| o.source_slice(span.clone()) == Some(","))
-            }
-            _ => false,
+        let o = parse_json("[1, 2]")?;
+        let flags_comma = report.violations.iter().any(|v| {
+            matches!(v, Violation::ExtraDeletion { span, .. }
+                if o.source_slice(span.clone()) == Some(","))
         });
         assert!(flags_comma, "{:?}", report.violations);
         Ok(())
diff --git a/src/diff.rs b/src/diff.rs
index 1ee5f91..a91a76b 100644
--- a/src/diff.rs
+++ b/src/diff.rs
@@ -1141,9 +1141,9 @@ mod tests {
         let m = diff(&o, &a);
         let script = edits(&o, &a, &m);
         assert_eq!(script.len(), 1);
-        let relabels_comment = script.iter().any(|e| match e {
-            Edit::Relabel(s, d) => o.label(*s) == Some("// old") && a.label(*d) == Some("// new"),
-            _ => false,
+        let relabels_comment = script.iter().any(|e| {
+            matches!(e, Edit::Relabel(s, d)
+                if o.label(*s) == Some("// old") && a.label(*d) == Some("// new"))
         });
         assert!(relabels_comment, "{script:?}");
         Ok(())
@@ -1169,10 +1169,9 @@ mod tests {
         // The number 2 and its separating comma arrive as inserts;
         // nothing is deleted or relabeled.
         assert!(script.iter().all(|e| matches!(e, Edit::Insert(_))));
-        let inserted_number = script.iter().any(|e| match e {
-            Edit::Insert(n) => a.label(*n) == Some("2"),
-            _ => false,
-        });
+        let inserted_number = script
+            .iter()
+            .any(|e| matches!(e, Edit::Insert(n) if a.label(*n) == Some("2")));
         assert!(inserted_number);
 
         let reversed = edits(&a, &o, &diff(&a, &o));
@@ -1316,9 +1315,10 @@ mod tests {
                     assert_eq!(xs.get(*i), ys.get(*j), "LCS({xs:?}, {ys:?})");
                 }
                 for window in pairs.windows(2) {
-                    if let [(i1, j1), (i2, j2)] = window {
-                        assert!(i1 < i2 && j1 < j2, "LCS({xs:?}, {ys:?}) crosses: {pairs:?}");
-                    }
+                    let [(i1, j1), (i2, j2)] = window else {
+                        unreachable!("windows(2) yields pairs");
+                    };
+                    assert!(i1 < i2 && j1 < j2, "LCS({xs:?}, {ys:?}) crosses: {pairs:?}");
                 }
             }
         }
diff --git a/src/merge.rs b/src/merge.rs
index 6e79cb4..767ba4b 100644
--- a/src/merge.rs
+++ b/src/merge.rs
@@ -553,9 +553,7 @@ mod tests {
             MergeOutcome::Merged(m) => {
                 assert_eq!(m.shape(&o, &a, &b), Shape::of(&expected));
             }
-            MergeOutcome::Conflicts(conflicts) => {
-                panic!("expected a clean merge, got conflicts: {conflicts:?}");
-            }
+            MergeOutcome::Conflicts(conflicts) => panic!("unexpected conflicts: {conflicts:?}"),
         }
         let report = check::check(&o, &a, &b, &expected);
         assert!(report.is_correct(), "{:?}", report.violations);
@@ -675,9 +673,7 @@ mod tests {
         let a = parse(a, lang)?;
         let b = parse(b, lang)?;
         match merge(&o, &a, &b)? {
-            MergeOutcome::Merged(m) => {
-                panic!("expected conflicts, got a merge: {:?}", m.shape(&o, &a, &b));
-            }
+            MergeOutcome::Merged(m) => panic!("unexpected merge: {:?}", m.shape(&o, &a, &b)),
             MergeOutcome::Conflicts(conflicts) => {
                 assert!(
                     conflicts.iter().any(|c| c.rule == rule),
diff --git a/src/synth.rs b/src/synth.rs
index c68b1c2..30160ee 100644
--- a/src/synth.rs
+++ b/src/synth.rs
@@ -205,9 +205,7 @@ mod tests {
         let b = parse(b, lang)?;
         match merge(&o, &a, &b)? {
             MergeOutcome::Merged(m) => Ok(synthesize(&m, &o, &a, &b)),
-            MergeOutcome::Conflicts(conflicts) => {
-                panic!("expected a clean merge, got conflicts: {conflicts:?}");
-            }
+            MergeOutcome::Conflicts(conflicts) => panic!("unexpected conflicts: {conflicts:?}"),
         }
     }
 
diff --git a/src/zs.rs b/src/zs.rs
index 37b4dfe..5f525e5 100644
--- a/src/zs.rs
+++ b/src/zs.rs
@@ -316,6 +316,7 @@ mod tests {
         // must be a bijection. Kills backtrack mutants that hand-picked
         // cases let through.
         let labels = [0u8, 1u8];
+        assert!(trees_with(0, &labels).is_empty());
         let mut all: Vec<(ZsTree<(usize, u8)>, usize)> = Vec::new();
         for nodes in 1..=3usize {
             for tree in trees_with(nodes, &labels) {