Skip to content

Commit

Permalink
coverage: Treat each match arm as a "branch" for branch coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Zalathar committed Jun 26, 2024
1 parent a513c92 commit 5959561
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 60 deletions.
32 changes: 32 additions & 0 deletions compiler/rustc_mir_build/src/build/coverageinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ struct NotInfo {
is_flipped: bool,
}

pub(crate) struct MatchArm {
pub(crate) source_info: SourceInfo,
pub(crate) pre_binding_block: Option<BasicBlock>,
pub(crate) arm_block: BasicBlock,
}

#[derive(Default)]
struct BlockMarkerGen {
num_block_markers: usize,
Expand Down Expand Up @@ -150,6 +156,32 @@ impl BranchInfoBuilder {
}
}

pub(crate) fn add_match_arms(&mut self, cfg: &mut CFG<'_>, arms: &[MatchArm]) {
// Match expressions with 0-1 arms don't have any branches for their arms.
if arms.len() < 2 {
return;
}

// FIXME(#124118) The current implementation of branch coverage for
// match arms can't handle or-patterns.
if arms.iter().any(|arm| arm.pre_binding_block.is_none()) {
return;
}

let branch_arms = arms
.iter()
.map(|&MatchArm { source_info, pre_binding_block, arm_block }| {
let pre_guard_marker =
self.markers.inject_block_marker(cfg, source_info, pre_binding_block.unwrap());
let arm_taken_marker =
self.markers.inject_block_marker(cfg, source_info, arm_block);
BranchArm { span: source_info.span, pre_guard_marker, arm_taken_marker }
})
.collect::<Vec<_>>();

self.branch_arm_lists.push(branch_arms);
}

pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
let Self {
nots: _,
Expand Down
21 changes: 20 additions & 1 deletion compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::scope::DropKind;
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::{coverageinfo, BlockAnd, BlockAndExtension, Builder};
use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
use rustc_data_structures::{fx::FxIndexMap, stack::ensure_sufficient_stack};
use rustc_hir::{BindingMode, ByRef};
Expand Down Expand Up @@ -473,6 +473,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
outer_source_info: SourceInfo,
fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
) -> BlockAnd<()> {
let mut coverage_match_arms = self.coverage_branch_info.is_some().then_some(vec![]);

let arm_end_blocks: Vec<_> = arm_candidates
.into_iter()
.map(|(arm, candidate)| {
Expand Down Expand Up @@ -507,6 +509,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
opt_scrutinee_place,
);

let pre_binding_block = candidate.pre_binding_block;

let arm_block = this.bind_pattern(
outer_source_info,
candidate,
Expand All @@ -516,6 +520,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
false,
);

if let Some(coverage_match_arms) = coverage_match_arms.as_mut() {
coverage_match_arms.push(coverageinfo::MatchArm {
source_info: this.source_info(arm.pattern.span),
pre_binding_block,
arm_block,
})
}

this.fixed_temps_scope = old_dedup_scope;

if let Some(source_scope) = scope {
Expand All @@ -527,6 +539,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
.collect();

if let Some(coverage_match_arms) = coverage_match_arms {
self.coverage_branch_info
.as_mut()
.expect("checked when creating `coverage_match_arms`")
.add_match_arms(&mut self.cfg, &coverage_match_arms);
}

// all the arm blocks will rejoin here
let end_block = self.cfg.start_new_block();

Expand Down
33 changes: 22 additions & 11 deletions tests/coverage/branch/guard-simple.cov-map
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
Function name: guard_simple::never_taken
Raw bytes (56): 0x[01, 01, 04, 01, 05, 11, 09, 0f, 0d, 05, 09, 08, 01, 08, 01, 02, 1e, 20, 05, 02, 02, 0e, 00, 1e, 05, 00, 22, 00, 24, 11, 01, 0e, 00, 1e, 20, 09, 06, 00, 0e, 00, 1e, 09, 00, 22, 00, 24, 0d, 01, 0e, 00, 10, 0b, 02, 01, 00, 02]
Raw bytes (80): 0x[01, 01, 09, 07, 01, 17, 05, 0d, 09, 01, 05, 17, 11, 0d, 09, 11, 09, 23, 0d, 05, 09, 0a, 01, 08, 01, 02, 1e, 20, 01, 02, 02, 09, 00, 0a, 20, 05, 0e, 00, 0e, 00, 1e, 05, 00, 22, 00, 24, 20, 11, 12, 01, 09, 00, 0a, 11, 00, 0e, 00, 1e, 20, 09, 1a, 00, 0e, 00, 1e, 09, 00, 22, 00, 24, 0d, 01, 0e, 00, 10, 1f, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 4
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(4), rhs = Counter(2)
- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3)
- expression 3 operands: lhs = Counter(1), rhs = Counter(2)
Number of file 0 mappings: 8
Number of expressions: 9
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(0)
- expression 1 operands: lhs = Expression(5, Add), rhs = Counter(1)
- expression 2 operands: lhs = Counter(3), rhs = Counter(2)
- expression 3 operands: lhs = Counter(0), rhs = Counter(1)
- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(4)
- expression 5 operands: lhs = Counter(3), rhs = Counter(2)
- expression 6 operands: lhs = Counter(4), rhs = Counter(2)
- expression 7 operands: lhs = Expression(8, Add), rhs = Counter(3)
- expression 8 operands: lhs = Counter(1), rhs = Counter(2)
Number of file 0 mappings: 10
- Code(Counter(0)) at (prev + 8, 1) to (start + 2, 30)
- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 2, 14) to (start + 0, 30)
- Branch { true: Counter(0), false: Expression(0, Sub) } at (prev + 2, 9) to (start + 0, 10)
true = c0
false = (((c3 + c2) + c1) - c0)
- Branch { true: Counter(1), false: Expression(3, Sub) } at (prev + 0, 14) to (start + 0, 30)
true = c1
false = (c0 - c1)
- Code(Counter(1)) at (prev + 0, 34) to (start + 0, 36)
- Code(Counter(4)) at (prev + 1, 14) to (start + 0, 30)
- Branch { true: Counter(2), false: Expression(1, Sub) } at (prev + 0, 14) to (start + 0, 30)
- Branch { true: Counter(4), false: Expression(4, Sub) } at (prev + 1, 9) to (start + 0, 10)
true = c4
false = ((c3 + c2) - c4)
- Code(Counter(4)) at (prev + 0, 14) to (start + 0, 30)
- Branch { true: Counter(2), false: Expression(6, Sub) } at (prev + 0, 14) to (start + 0, 30)
true = c2
false = (c4 - c2)
- Code(Counter(2)) at (prev + 0, 34) to (start + 0, 36)
- Code(Counter(3)) at (prev + 1, 14) to (start + 0, 16)
- Code(Expression(2, Add)) at (prev + 2, 1) to (start + 0, 2)
- Code(Expression(7, Add)) at (prev + 2, 1) to (start + 0, 2)
= ((c1 + c2) + c3)

2 changes: 2 additions & 0 deletions tests/coverage/branch/guard-simple.coverage
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
LL| 1| _ if black_box(false) => {}
^0
------------------
| Branch (LL:9): [True: 1, False: 0]
| Branch (LL:14): [True: 0, False: 1]
------------------
LL| 1| _ if black_box(false) => {}
^0
------------------
| Branch (LL:9): [True: 1, False: 0]
| Branch (LL:14): [True: 0, False: 1]
------------------
LL| 1| _ => {}
Expand Down
46 changes: 31 additions & 15 deletions tests/coverage/branch/guard.cov-map
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
Function name: guard::branch_match_guard
Raw bytes (85): 0x[01, 01, 06, 19, 0d, 05, 09, 0f, 15, 13, 11, 17, 0d, 05, 09, 0d, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 02, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 17, 03, 0e, 02, 0a, 0b, 04, 01, 00, 02]
Raw bytes (120): 0x[01, 01, 0d, 1b, 0d, 33, 11, 05, 09, 03, 19, 19, 0d, 1b, 1d, 33, 11, 05, 09, 05, 09, 2b, 15, 2f, 11, 33, 0d, 05, 09, 10, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 20, 15, 03, 01, 09, 00, 10, 15, 00, 14, 02, 0a, 20, 19, 0e, 03, 09, 00, 10, 0d, 00, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 12, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 20, 1d, 16, 03, 09, 00, 10, 11, 00, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 33, 03, 0e, 02, 0a, 27, 04, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 6
- expression 0 operands: lhs = Counter(6), rhs = Counter(3)
- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(5)
- expression 3 operands: lhs = Expression(4, Add), rhs = Counter(4)
- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(3)
- expression 5 operands: lhs = Counter(1), rhs = Counter(2)
Number of file 0 mappings: 13
Number of expressions: 13
- expression 0 operands: lhs = Expression(6, Add), rhs = Counter(3)
- expression 1 operands: lhs = Expression(12, Add), rhs = Counter(4)
- expression 2 operands: lhs = Counter(1), rhs = Counter(2)
- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(6)
- expression 4 operands: lhs = Counter(6), rhs = Counter(3)
- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(7)
- expression 6 operands: lhs = Expression(12, Add), rhs = Counter(4)
- expression 7 operands: lhs = Counter(1), rhs = Counter(2)
- expression 8 operands: lhs = Counter(1), rhs = Counter(2)
- expression 9 operands: lhs = Expression(10, Add), rhs = Counter(5)
- expression 10 operands: lhs = Expression(11, Add), rhs = Counter(4)
- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(3)
- expression 12 operands: lhs = Counter(1), rhs = Counter(2)
Number of file 0 mappings: 16
- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
- Code(Counter(7)) at (prev + 3, 11) to (start + 0, 12)
- Code(Counter(5)) at (prev + 1, 20) to (start + 2, 10)
- Code(Counter(3)) at (prev + 3, 14) to (start + 0, 15)
- Branch { true: Counter(5), false: Expression(0, Add) } at (prev + 1, 9) to (start + 0, 16)
true = c5
false = (((c1 + c2) + c4) + c3)
- Code(Counter(5)) at (prev + 0, 20) to (start + 2, 10)
- Branch { true: Counter(6), false: Expression(3, Sub) } at (prev + 3, 9) to (start + 0, 16)
true = c6
false = ((((c1 + c2) + c4) + c3) - c6)
- Code(Counter(3)) at (prev + 0, 14) to (start + 0, 15)
- Code(Counter(6)) at (prev + 0, 20) to (start + 0, 25)
- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 30)
- Branch { true: Counter(3), false: Expression(4, Sub) } at (prev + 0, 20) to (start + 0, 30)
true = c3
false = (c6 - c3)
- Code(Counter(3)) at (prev + 0, 29) to (start + 2, 10)
- Code(Counter(4)) at (prev + 3, 14) to (start + 0, 15)
- Branch { true: Counter(7), false: Expression(5, Sub) } at (prev + 3, 9) to (start + 0, 16)
true = c7
false = (((c1 + c2) + c4) - c7)
- Code(Counter(4)) at (prev + 0, 14) to (start + 0, 15)
- Code(Counter(7)) at (prev + 0, 20) to (start + 0, 25)
- Branch { true: Counter(4), false: Counter(2) } at (prev + 0, 20) to (start + 0, 30)
true = c4
false = c2
- Code(Counter(4)) at (prev + 0, 29) to (start + 2, 10)
- Code(Expression(5, Add)) at (prev + 3, 14) to (start + 2, 10)
- Code(Expression(12, Add)) at (prev + 3, 14) to (start + 2, 10)
= (c1 + c2)
- Code(Expression(2, Add)) at (prev + 4, 1) to (start + 0, 2)
- Code(Expression(9, Add)) at (prev + 4, 1) to (start + 0, 2)
= ((((c1 + c2) + c3) + c4) + c5)

5 changes: 5 additions & 0 deletions tests/coverage/branch/guard.coverage
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,22 @@
LL| |
LL| 1| match x {
LL| 1| Some(0) => {
------------------
| Branch (LL:9): [True: 1, False: 3]
------------------
LL| 1| println!("zero");
LL| 1| }
LL| 3| Some(x) if x % 2 == 0 => {
^2
------------------
| Branch (LL:9): [True: 3, False: 0]
| Branch (LL:20): [True: 2, False: 1]
------------------
LL| 2| println!("is nonzero and even");
LL| 2| }
LL| 1| Some(x) if x % 3 == 0 => {
------------------
| Branch (LL:9): [True: 1, False: 0]
| Branch (LL:20): [True: 1, False: 0]
------------------
LL| 1| println!("is nonzero and odd, but divisible by 3");
Expand Down
Loading

0 comments on commit 5959561

Please sign in to comment.