Skip to content

Commit d0dfcd8

Browse files
committed
coverage: Treat each match arm as a "branch" for branch coverage
1 parent b3632b2 commit d0dfcd8

File tree

6 files changed

+120
-16
lines changed

6 files changed

+120
-16
lines changed

compiler/rustc_mir_build/src/build/coverageinfo.rs

+29
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ struct NotInfo {
3838
is_flipped: bool,
3939
}
4040

41+
pub(crate) struct MatchArm {
42+
pub(crate) source_info: SourceInfo,
43+
pub(crate) has_guard: bool,
44+
pub(crate) pre_binding_block: Option<BasicBlock>,
45+
}
46+
4147
impl BranchInfoBuilder {
4248
/// Creates a new branch info builder, but only if branch coverage instrumentation
4349
/// is enabled and `def_id` represents a function that is eligible for coverage.
@@ -150,6 +156,29 @@ impl BranchInfoBuilder {
150156
]);
151157
}
152158

159+
pub(crate) fn add_match_arms(&mut self, cfg: &mut CFG<'_>, arms: Vec<MatchArm>) {
160+
// Match expressions with 0-1 arms don't have any branches for their arms.
161+
if arms.len() < 2 {
162+
return;
163+
}
164+
165+
// FIXME(#124118) The current implementation of branch coverage for
166+
// match arms can't handle or-patterns or guards.
167+
if arms.iter().any(|arm| arm.has_guard || arm.pre_binding_block.is_none()) {
168+
return;
169+
}
170+
171+
let branch_arms = arms
172+
.iter()
173+
.map(|&MatchArm { source_info, pre_binding_block, .. }| {
174+
let marker = self.inject_block_marker(cfg, source_info, pre_binding_block.unwrap());
175+
BranchArm { span: source_info.span, marker }
176+
})
177+
.collect::<Vec<_>>();
178+
179+
self.branch_arm_lists.push(branch_arms);
180+
}
181+
153182
fn next_block_marker_id(&mut self) -> BlockMarkerId {
154183
let id = BlockMarkerId::from_usize(self.num_block_markers);
155184
self.num_block_markers += 1;

compiler/rustc_mir_build/src/build/matches/mod.rs

+25
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! This also includes code for pattern bindings in `let` statements and
66
//! function parameters.
77
8+
use crate::build::coverageinfo;
89
use crate::build::expr::as_place::PlaceBuilder;
910
use crate::build::scope::DropKind;
1011
use crate::build::ForGuard::{self, OutsideGuard, RefWithinGuard};
@@ -317,6 +318,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
317318
&mut candidates,
318319
);
319320

321+
// Record branch coverage info for this match.
322+
// (Does nothing if branch coverage is not enabled.)
323+
self.visit_coverage_match_expr(&candidates);
324+
320325
self.lower_match_arms(
321326
destination,
322327
scrutinee_place,
@@ -366,6 +371,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
366371
.collect()
367372
}
368373

374+
/// If branch coverage is enabled, inject marker statements into each match
375+
/// arm, and record their IDs in the table of branches.
376+
///
377+
/// Unlike some of the other branch coverage visitor methods, this one needs
378+
/// access to private details of [`Candidate`], so having it here is easier.
379+
fn visit_coverage_match_expr(&mut self, candidates: &[&mut Candidate<'_, 'tcx>]) {
380+
// Bail out if branch coverage is not enabled for this function.
381+
let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
382+
383+
let arms = candidates
384+
.iter()
385+
.map(|c| coverageinfo::MatchArm {
386+
source_info: SourceInfo { span: c.extra_data.span, scope: self.source_scope },
387+
has_guard: c.has_guard,
388+
pre_binding_block: c.pre_binding_block,
389+
})
390+
.collect::<Vec<_>>();
391+
branch_info.add_match_arms(&mut self.cfg, arms);
392+
}
393+
369394
/// Create the decision tree for the match expression, starting from `block`.
370395
///
371396
/// Modifies `candidates` to store the bindings and type ascriptions for

tests/coverage/branch/match-arms.cov-map

+38-14
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,49 @@ Number of file 0 mappings: 12
3636
= ((((((((Zero + c2) + c3) + c4) + c5) + c6) + c7) + c8) + c9)
3737

3838
Function name: match_arms::match_arms
39-
Raw bytes (51): 0x[01, 01, 06, 05, 07, 0b, 11, 09, 0d, 13, 02, 17, 09, 11, 0d, 07, 01, 18, 01, 01, 10, 05, 03, 0b, 00, 10, 11, 01, 11, 00, 21, 0d, 01, 11, 00, 21, 09, 01, 11, 00, 21, 02, 01, 11, 00, 21, 0f, 03, 05, 01, 02]
39+
Raw bytes (102): 0x[01, 01, 15, 0d, 17, 09, 4a, 05, 4f, 53, 11, 09, 0d, 09, 4a, 05, 4f, 53, 11, 09, 0d, 05, 4f, 53, 11, 09, 0d, 05, 4f, 53, 11, 09, 0d, 43, 4a, 47, 09, 11, 0d, 05, 4f, 53, 11, 09, 0d, 0a, 01, 18, 01, 01, 10, 05, 03, 0b, 00, 10, 20, 11, 03, 01, 09, 00, 13, 11, 00, 11, 00, 21, 20, 0d, 17, 01, 09, 00, 13, 0d, 00, 11, 00, 21, 20, 09, 4a, 01, 09, 00, 13, 09, 00, 11, 00, 21, 4a, 01, 11, 00, 21, 3f, 03, 05, 01, 02]
4040
Number of files: 1
4141
- file 0 => global file 1
42-
Number of expressions: 6
43-
- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add)
44-
- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4)
45-
- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
46-
- expression 3 operands: lhs = Expression(4, Add), rhs = Expression(0, Sub)
47-
- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(2)
48-
- expression 5 operands: lhs = Counter(4), rhs = Counter(3)
49-
Number of file 0 mappings: 7
42+
Number of expressions: 21
43+
- expression 0 operands: lhs = Counter(3), rhs = Expression(5, Add)
44+
- expression 1 operands: lhs = Counter(2), rhs = Expression(18, Sub)
45+
- expression 2 operands: lhs = Counter(1), rhs = Expression(19, Add)
46+
- expression 3 operands: lhs = Expression(20, Add), rhs = Counter(4)
47+
- expression 4 operands: lhs = Counter(2), rhs = Counter(3)
48+
- expression 5 operands: lhs = Counter(2), rhs = Expression(18, Sub)
49+
- expression 6 operands: lhs = Counter(1), rhs = Expression(19, Add)
50+
- expression 7 operands: lhs = Expression(20, Add), rhs = Counter(4)
51+
- expression 8 operands: lhs = Counter(2), rhs = Counter(3)
52+
- expression 9 operands: lhs = Counter(1), rhs = Expression(19, Add)
53+
- expression 10 operands: lhs = Expression(20, Add), rhs = Counter(4)
54+
- expression 11 operands: lhs = Counter(2), rhs = Counter(3)
55+
- expression 12 operands: lhs = Counter(1), rhs = Expression(19, Add)
56+
- expression 13 operands: lhs = Expression(20, Add), rhs = Counter(4)
57+
- expression 14 operands: lhs = Counter(2), rhs = Counter(3)
58+
- expression 15 operands: lhs = Expression(16, Add), rhs = Expression(18, Sub)
59+
- expression 16 operands: lhs = Expression(17, Add), rhs = Counter(2)
60+
- expression 17 operands: lhs = Counter(4), rhs = Counter(3)
61+
- expression 18 operands: lhs = Counter(1), rhs = Expression(19, Add)
62+
- expression 19 operands: lhs = Expression(20, Add), rhs = Counter(4)
63+
- expression 20 operands: lhs = Counter(2), rhs = Counter(3)
64+
Number of file 0 mappings: 10
5065
- Code(Counter(0)) at (prev + 24, 1) to (start + 1, 16)
5166
- Code(Counter(1)) at (prev + 3, 11) to (start + 0, 16)
52-
- Code(Counter(4)) at (prev + 1, 17) to (start + 0, 33)
53-
- Code(Counter(3)) at (prev + 1, 17) to (start + 0, 33)
54-
- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 33)
55-
- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 33)
67+
- Branch { true: Counter(4), false: Expression(0, Add) } at (prev + 1, 9) to (start + 0, 19)
68+
true = c4
69+
false = (c3 + (c2 + (c1 - ((c2 + c3) + c4))))
70+
- Code(Counter(4)) at (prev + 0, 17) to (start + 0, 33)
71+
- Branch { true: Counter(3), false: Expression(5, Add) } at (prev + 1, 9) to (start + 0, 19)
72+
true = c3
73+
false = (c2 + (c1 - ((c2 + c3) + c4)))
74+
- Code(Counter(3)) at (prev + 0, 17) to (start + 0, 33)
75+
- Branch { true: Counter(2), false: Expression(18, Sub) } at (prev + 1, 9) to (start + 0, 19)
76+
true = c2
77+
false = (c1 - ((c2 + c3) + c4))
78+
- Code(Counter(2)) at (prev + 0, 17) to (start + 0, 33)
79+
- Code(Expression(18, Sub)) at (prev + 1, 17) to (start + 0, 33)
5680
= (c1 - ((c2 + c3) + c4))
57-
- Code(Expression(3, Add)) at (prev + 3, 5) to (start + 1, 2)
81+
- Code(Expression(15, Add)) at (prev + 3, 5) to (start + 1, 2)
5882
= (((c4 + c3) + c2) + (c1 - ((c2 + c3) + c4)))
5983

6084
Function name: match_arms::or_patterns

tests/coverage/branch/match-arms.coverage

+10-1
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,17 @@
2626
LL| |
2727
LL| 15| match value {
2828
LL| 8| Enum::D(d) => consume(d),
29+
------------------
30+
| Branch (LL:9): [True: 8, False: 7]
31+
------------------
2932
LL| 4| Enum::C(c) => consume(c),
33+
------------------
34+
| Branch (LL:9): [True: 4, False: 3]
35+
------------------
3036
LL| 2| Enum::B(b) => consume(b),
37+
------------------
38+
| Branch (LL:9): [True: 2, False: 1]
39+
------------------
3140
LL| 1| Enum::A(a) => consume(a),
3241
LL| | }
3342
LL| |
@@ -101,5 +110,5 @@
101110
LL| | }
102111
LL| |}
103112
LL| |
104-
LL| |// FIXME(#124118) Actually instrument match arms for branch coverage.
113+
LL| |// FIXME(#124118) Support match expressions with guards or or-patterns.
105114

tests/coverage/branch/match-arms.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,4 @@ fn main() {
8787
}
8888
}
8989

90-
// FIXME(#124118) Actually instrument match arms for branch coverage.
90+
// FIXME(#124118) Support match expressions with guards or or-patterns.

tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff

+17
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,31 @@
2626
debug a => _9;
2727
}
2828

29+
coverage branches {
30+
BlockMarkerId(0) => $DIR/branch_match_arms.rs:16:9: 16:19 (#0)
31+
BlockMarkerId(1) => $DIR/branch_match_arms.rs:17:9: 17:19 (#0)
32+
BlockMarkerId(2) => $DIR/branch_match_arms.rs:18:9: 18:19 (#0)
33+
BlockMarkerId(3) => $DIR/branch_match_arms.rs:19:9: 19:19 (#0)
34+
}
35+
2936
+ coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) };
3037
+ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) };
3138
+ coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) };
3239
+ coverage ExpressionId(3) => Expression { lhs: Counter(3), op: Add, rhs: Counter(2) };
3340
+ coverage ExpressionId(4) => Expression { lhs: Expression(3), op: Add, rhs: Counter(1) };
3441
+ coverage ExpressionId(5) => Expression { lhs: Expression(4), op: Add, rhs: Expression(2) };
42+
+ coverage ExpressionId(6) => Expression { lhs: Counter(1), op: Add, rhs: Expression(2) };
43+
+ coverage ExpressionId(7) => Expression { lhs: Counter(2), op: Add, rhs: Expression(6) };
44+
+ coverage ExpressionId(8) => Expression { lhs: Counter(3), op: Add, rhs: Expression(7) };
3545
+ coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1 - 15:21;
3646
+ coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:16:17 - 16:33;
3747
+ coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17 - 17:33;
3848
+ coverage Code(Counter(1)) => $DIR/branch_match_arms.rs:18:17 - 18:33;
3949
+ coverage Code(Expression(2)) => $DIR/branch_match_arms.rs:19:17 - 19:33;
4050
+ coverage Code(Expression(5)) => $DIR/branch_match_arms.rs:21:1 - 21:2;
51+
+ coverage Branch { true_term: Counter(1), false_term: Expression(2) } => $DIR/branch_match_arms.rs:18:9 - 18:19;
52+
+ coverage Branch { true_term: Counter(2), false_term: Expression(6) } => $DIR/branch_match_arms.rs:17:9 - 17:19;
53+
+ coverage Branch { true_term: Counter(3), false_term: Expression(7) } => $DIR/branch_match_arms.rs:16:9 - 16:19;
4154
+
4255
bb0: {
4356
+ Coverage::CounterIncrement(0);
@@ -55,21 +68,25 @@
5568

5669
bb2: {
5770
+ Coverage::CounterIncrement(3);
71+
Coverage::BlockMarker(0);
5872
falseEdge -> [real: bb6, imaginary: bb3];
5973
}
6074

6175
bb3: {
6276
+ Coverage::CounterIncrement(2);
77+
Coverage::BlockMarker(1);
6378
falseEdge -> [real: bb8, imaginary: bb4];
6479
}
6580

6681
bb4: {
6782
+ Coverage::CounterIncrement(1);
83+
Coverage::BlockMarker(2);
6884
falseEdge -> [real: bb10, imaginary: bb5];
6985
}
7086

7187
bb5: {
7288
+ Coverage::ExpressionUsed(2);
89+
Coverage::BlockMarker(3);
7390
StorageLive(_9);
7491
_9 = ((_1 as A).0: u32);
7592
StorageLive(_10);

0 commit comments

Comments
 (0)