Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4be17d1

Browse files
authoredApr 22, 2024
Rollup merge of rust-lang#124217 - Zalathar:pre-branch, r=oli-obk
coverage: Prepare for improved branch coverage When trying to rebase my new branch coverage work (including rust-lang#124154) on top of the introduction of MC/DC coverage (rust-lang#123409), I found it a lot harder than anticipated. With the benefit of hindsight, the branch coverage code and MC/DC code have become more interdependent than I'm happy with. This PR therefore disentangles them a bit, so that it will be easier for both areas of code to evolve independently without interference. --- This PR also includes a few extra branch coverage tests that I had sitting around from my current branch coverage work. They mostly just demonstrate that certain language constructs listed in rust-lang#124118 currently don't have branch coverage support. `@rustbot` label +A-code-coverage
2 parents 2a7ec56 + 2b6adb0 commit 4be17d1

26 files changed

+993
-94
lines changed
 

‎compiler/rustc_middle/src/mir/coverage.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,9 @@ impl Default for ConditionInfo {
314314
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
315315
pub struct MCDCBranchSpan {
316316
pub span: Span,
317-
pub condition_info: ConditionInfo,
317+
/// If `None`, this actually represents a normal branch span inserted for
318+
/// code that was too complex for MC/DC.
319+
pub condition_info: Option<ConditionInfo>,
318320
pub true_marker: BlockMarkerId,
319321
pub false_marker: BlockMarkerId,
320322
}

‎compiler/rustc_middle/src/mir/pretty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ fn write_coverage_branch_info(
491491
writeln!(
492492
w,
493493
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
494-
condition_info.condition_id
494+
condition_info.map(|info| info.condition_id)
495495
)?;
496496
}
497497

‎compiler/rustc_mir_build/src/build/coverageinfo.rs

+55-48
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ use rustc_middle::mir::coverage::{
77
BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageKind, MCDCBranchSpan,
88
MCDCDecisionSpan,
99
};
10-
use rustc_middle::mir::{self, BasicBlock, UnOp};
10+
use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
1111
use rustc_middle::thir::{ExprId, ExprKind, LogicalOp, Thir};
1212
use rustc_middle::ty::TyCtxt;
1313
use rustc_span::def_id::LocalDefId;
1414
use rustc_span::Span;
1515

16-
use crate::build::Builder;
16+
use crate::build::{Builder, CFG};
1717
use crate::errors::MCDCExceedsConditionNumLimit;
1818

1919
pub(crate) struct BranchInfoBuilder {
@@ -22,6 +22,7 @@ pub(crate) struct BranchInfoBuilder {
2222

2323
num_block_markers: usize,
2424
branch_spans: Vec<BranchSpan>,
25+
2526
mcdc_branch_spans: Vec<MCDCBranchSpan>,
2627
mcdc_decision_spans: Vec<MCDCDecisionSpan>,
2728
mcdc_state: Option<MCDCState>,
@@ -95,13 +96,7 @@ impl BranchInfoBuilder {
9596
}
9697
}
9798

98-
fn record_conditions_operation(&mut self, logical_op: LogicalOp, span: Span) {
99-
if let Some(mcdc_state) = self.mcdc_state.as_mut() {
100-
mcdc_state.record_conditions(logical_op, span);
101-
}
102-
}
103-
104-
fn fetch_condition_info(
99+
fn fetch_mcdc_condition_info(
105100
&mut self,
106101
tcx: TyCtxt<'_>,
107102
true_marker: BlockMarkerId,
@@ -121,14 +116,9 @@ impl BranchInfoBuilder {
121116
_ => {
122117
// Do not generate mcdc mappings and statements for decisions with too many conditions.
123118
let rebase_idx = self.mcdc_branch_spans.len() - decision.conditions_num + 1;
124-
let to_normal_branches = self.mcdc_branch_spans.split_off(rebase_idx);
125-
self.branch_spans.extend(to_normal_branches.into_iter().map(
126-
|MCDCBranchSpan { span, true_marker, false_marker, .. }| BranchSpan {
127-
span,
128-
true_marker,
129-
false_marker,
130-
},
131-
));
119+
for branch in &mut self.mcdc_branch_spans[rebase_idx..] {
120+
branch.condition_info = None;
121+
}
132122

133123
// ConditionInfo of this branch shall also be reset.
134124
condition_info = None;
@@ -144,20 +134,50 @@ impl BranchInfoBuilder {
144134
condition_info
145135
}
146136

137+
fn add_two_way_branch<'tcx>(
138+
&mut self,
139+
cfg: &mut CFG<'tcx>,
140+
source_info: SourceInfo,
141+
true_block: BasicBlock,
142+
false_block: BasicBlock,
143+
) {
144+
let true_marker = self.inject_block_marker(cfg, source_info, true_block);
145+
let false_marker = self.inject_block_marker(cfg, source_info, false_block);
146+
147+
self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker });
148+
}
149+
147150
fn next_block_marker_id(&mut self) -> BlockMarkerId {
148151
let id = BlockMarkerId::from_usize(self.num_block_markers);
149152
self.num_block_markers += 1;
150153
id
151154
}
152155

156+
fn inject_block_marker(
157+
&mut self,
158+
cfg: &mut CFG<'_>,
159+
source_info: SourceInfo,
160+
block: BasicBlock,
161+
) -> BlockMarkerId {
162+
let id = self.next_block_marker_id();
163+
164+
let marker_statement = mir::Statement {
165+
source_info,
166+
kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
167+
};
168+
cfg.push(block, marker_statement);
169+
170+
id
171+
}
172+
153173
pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
154174
let Self {
155175
nots: _,
156176
num_block_markers,
157177
branch_spans,
158178
mcdc_branch_spans,
159179
mcdc_decision_spans,
160-
..
180+
mcdc_state: _,
161181
} = self;
162182

163183
if num_block_markers == 0 {
@@ -325,7 +345,7 @@ impl Builder<'_, '_> {
325345
mut else_block: BasicBlock,
326346
) {
327347
// Bail out if branch coverage is not enabled for this function.
328-
let Some(branch_info) = self.coverage_branch_info.as_ref() else { return };
348+
let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
329349

330350
// If this condition expression is nested within one or more `!` expressions,
331351
// replace it with the enclosing `!` collected by `visit_unary_not`.
@@ -335,47 +355,34 @@ impl Builder<'_, '_> {
335355
std::mem::swap(&mut then_block, &mut else_block);
336356
}
337357
}
338-
let source_info = self.source_info(self.thir[expr_id].span);
339-
340-
// Now that we have `source_info`, we can upgrade to a &mut reference.
341-
let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut");
342-
343-
let mut inject_branch_marker = |block: BasicBlock| {
344-
let id = branch_info.next_block_marker_id();
345-
346-
let marker_statement = mir::Statement {
347-
source_info,
348-
kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
349-
};
350-
self.cfg.push(block, marker_statement);
351358

352-
id
353-
};
354-
355-
let true_marker = inject_branch_marker(then_block);
356-
let false_marker = inject_branch_marker(else_block);
359+
let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
357360

358-
if let Some(condition_info) =
359-
branch_info.fetch_condition_info(self.tcx, true_marker, false_marker)
360-
{
361+
// Separate path for handling branches when MC/DC is enabled.
362+
if branch_info.mcdc_state.is_some() {
363+
let mut inject_block_marker =
364+
|block| branch_info.inject_block_marker(&mut self.cfg, source_info, block);
365+
let true_marker = inject_block_marker(then_block);
366+
let false_marker = inject_block_marker(else_block);
367+
let condition_info =
368+
branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker);
361369
branch_info.mcdc_branch_spans.push(MCDCBranchSpan {
362370
span: source_info.span,
363371
condition_info,
364372
true_marker,
365373
false_marker,
366374
});
367-
} else {
368-
branch_info.branch_spans.push(BranchSpan {
369-
span: source_info.span,
370-
true_marker,
371-
false_marker,
372-
});
375+
return;
373376
}
377+
378+
branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
374379
}
375380

376381
pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) {
377-
if let Some(branch_info) = self.coverage_branch_info.as_mut() {
378-
branch_info.record_conditions_operation(logical_op, span);
382+
if let Some(branch_info) = self.coverage_branch_info.as_mut()
383+
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
384+
{
385+
mcdc_state.record_conditions(logical_op, span);
379386
}
380387
}
381388
}

‎compiler/rustc_mir_transform/src/coverage/mod.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod tests;
99

1010
use self::counters::{CounterIncrementSite, CoverageCounters};
1111
use self::graph::{BasicCoverageBlock, CoverageGraph};
12-
use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
12+
use self::spans::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans};
1313

1414
use crate::MirPass;
1515

@@ -141,21 +141,25 @@ fn create_mappings<'tcx>(
141141

142142
let mut mappings = Vec::new();
143143

144-
mappings.extend(coverage_spans.all_bcb_mappings().filter_map(
144+
mappings.extend(coverage_spans.mappings.iter().filter_map(
145145
|BcbMapping { kind: bcb_mapping_kind, span }| {
146146
let kind = match *bcb_mapping_kind {
147147
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
148-
BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch {
149-
true_term: term_for_bcb(true_bcb),
150-
false_term: term_for_bcb(false_bcb),
151-
},
152-
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => {
153-
MappingKind::MCDCBranch {
148+
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => {
149+
MappingKind::Branch {
154150
true_term: term_for_bcb(true_bcb),
155151
false_term: term_for_bcb(false_bcb),
156-
mcdc_params: condition_info,
157152
}
158153
}
154+
BcbMappingKind::MCDCBranch {
155+
true_bcb,
156+
false_bcb,
157+
condition_info: Some(mcdc_params),
158+
} => MappingKind::MCDCBranch {
159+
true_term: term_for_bcb(true_bcb),
160+
false_term: term_for_bcb(false_bcb),
161+
mcdc_params,
162+
},
159163
BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => {
160164
MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num })
161165
}
@@ -165,6 +169,16 @@ fn create_mappings<'tcx>(
165169
},
166170
));
167171

172+
mappings.extend(coverage_spans.branch_pairs.iter().filter_map(
173+
|&BcbBranchPair { span, true_bcb, false_bcb }| {
174+
let true_term = term_for_bcb(true_bcb);
175+
let false_term = term_for_bcb(false_bcb);
176+
let kind = MappingKind::Branch { true_term, false_term };
177+
let code_region = make_code_region(source_map, file_name, span, body_span)?;
178+
Some(Mapping { kind, code_region })
179+
},
180+
));
181+
168182
mappings
169183
}
170184

@@ -233,7 +247,7 @@ fn inject_mcdc_statements<'tcx>(
233247

234248
// Inject test vector update first because `inject_statement` always insert new statement at head.
235249
for (end_bcbs, bitmap_idx) in
236-
coverage_spans.all_bcb_mappings().filter_map(|mapping| match &mapping.kind {
250+
coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind {
237251
BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => {
238252
Some((end_bcbs, *bitmap_idx))
239253
}
@@ -247,9 +261,9 @@ fn inject_mcdc_statements<'tcx>(
247261
}
248262

249263
for (true_bcb, false_bcb, condition_id) in
250-
coverage_spans.all_bcb_mappings().filter_map(|mapping| match mapping.kind {
264+
coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind {
251265
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => {
252-
Some((true_bcb, false_bcb, condition_info.condition_id))
266+
Some((true_bcb, false_bcb, condition_info?.condition_id))
253267
}
254268
_ => None,
255269
})

‎compiler/rustc_mir_transform/src/coverage/spans.rs

+34-13
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ mod from_mir;
1515
pub(super) enum BcbMappingKind {
1616
/// Associates an ordinary executable code span with its corresponding BCB.
1717
Code(BasicCoverageBlock),
18-
/// Associates a branch span with BCBs for its true and false arms.
19-
Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock },
18+
19+
// Ordinary branch mappings are stored separately, so they don't have a
20+
// variant in this enum.
21+
//
2022
/// Associates a mcdc branch span with condition info besides fields for normal branch.
2123
MCDCBranch {
2224
true_bcb: BasicCoverageBlock,
2325
false_bcb: BasicCoverageBlock,
24-
condition_info: ConditionInfo,
26+
/// If `None`, this actually represents a normal branch mapping inserted
27+
/// for code that was too complex for MC/DC.
28+
condition_info: Option<ConditionInfo>,
2529
},
2630
/// Associates a mcdc decision with its join BCB.
2731
MCDCDecision { end_bcbs: BTreeSet<BasicCoverageBlock>, bitmap_idx: u32, conditions_num: u16 },
@@ -33,9 +37,20 @@ pub(super) struct BcbMapping {
3337
pub(super) span: Span,
3438
}
3539

40+
/// This is separate from [`BcbMappingKind`] to help prepare for larger changes
41+
/// that will be needed for improved branch coverage in the future.
42+
/// (See <https://github.com/rust-lang/rust/pull/124217>.)
43+
#[derive(Debug)]
44+
pub(super) struct BcbBranchPair {
45+
pub(super) span: Span,
46+
pub(super) true_bcb: BasicCoverageBlock,
47+
pub(super) false_bcb: BasicCoverageBlock,
48+
}
49+
3650
pub(super) struct CoverageSpans {
3751
bcb_has_mappings: BitSet<BasicCoverageBlock>,
38-
mappings: Vec<BcbMapping>,
52+
pub(super) mappings: Vec<BcbMapping>,
53+
pub(super) branch_pairs: Vec<BcbBranchPair>,
3954
test_vector_bitmap_bytes: u32,
4055
}
4156

@@ -44,10 +59,6 @@ impl CoverageSpans {
4459
self.bcb_has_mappings.contains(bcb)
4560
}
4661

47-
pub(super) fn all_bcb_mappings(&self) -> impl Iterator<Item = &BcbMapping> {
48-
self.mappings.iter()
49-
}
50-
5162
pub(super) fn test_vector_bitmap_bytes(&self) -> u32 {
5263
self.test_vector_bitmap_bytes
5364
}
@@ -63,6 +74,7 @@ pub(super) fn generate_coverage_spans(
6374
basic_coverage_blocks: &CoverageGraph,
6475
) -> Option<CoverageSpans> {
6576
let mut mappings = vec![];
77+
let mut branch_pairs = vec![];
6678

6779
if hir_info.is_async_fn {
6880
// An async function desugars into a function that returns a future,
@@ -84,14 +96,20 @@ pub(super) fn generate_coverage_spans(
8496
BcbMapping { kind: BcbMappingKind::Code(bcb), span }
8597
}));
8698

87-
mappings.extend(from_mir::extract_branch_mappings(
99+
branch_pairs.extend(from_mir::extract_branch_pairs(
100+
mir_body,
101+
hir_info,
102+
basic_coverage_blocks,
103+
));
104+
105+
mappings.extend(from_mir::extract_mcdc_mappings(
88106
mir_body,
89107
hir_info.body_span,
90108
basic_coverage_blocks,
91109
));
92110
}
93111

94-
if mappings.is_empty() {
112+
if mappings.is_empty() && branch_pairs.is_empty() {
95113
return None;
96114
}
97115

@@ -104,8 +122,7 @@ pub(super) fn generate_coverage_spans(
104122
for BcbMapping { kind, span: _ } in &mappings {
105123
match *kind {
106124
BcbMappingKind::Code(bcb) => insert(bcb),
107-
BcbMappingKind::Branch { true_bcb, false_bcb }
108-
| BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => {
125+
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => {
109126
insert(true_bcb);
110127
insert(false_bcb);
111128
}
@@ -118,8 +135,12 @@ pub(super) fn generate_coverage_spans(
118135
}
119136
}
120137
}
138+
for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs {
139+
insert(true_bcb);
140+
insert(false_bcb);
141+
}
121142

122-
Some(CoverageSpans { bcb_has_mappings, mappings, test_vector_bitmap_bytes })
143+
Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes })
123144
}
124145

125146
#[derive(Debug)]

‎compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs

+57-19
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
1313
use crate::coverage::graph::{
1414
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
1515
};
16-
use crate::coverage::spans::{BcbMapping, BcbMappingKind};
16+
use crate::coverage::spans::{BcbBranchPair, BcbMapping, BcbMappingKind};
1717
use crate::coverage::ExtractedHirInfo;
1818

1919
/// Traverses the MIR body to produce an initial collection of coverage-relevant
@@ -366,15 +366,10 @@ impl SpanFromMir {
366366
}
367367
}
368368

369-
pub(super) fn extract_branch_mappings(
369+
fn resolve_block_markers(
370+
branch_info: &mir::coverage::BranchInfo,
370371
mir_body: &mir::Body<'_>,
371-
body_span: Span,
372-
basic_coverage_blocks: &CoverageGraph,
373-
) -> Vec<BcbMapping> {
374-
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
375-
return vec![];
376-
};
377-
372+
) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
378373
let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
379374
None,
380375
branch_info.num_block_markers,
@@ -389,6 +384,58 @@ pub(super) fn extract_branch_mappings(
389384
}
390385
}
391386

387+
block_markers
388+
}
389+
390+
// FIXME: There is currently a lot of redundancy between
391+
// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so
392+
// that they can each be modified without interfering with the other, but in
393+
// the long term we should try to bring them together again when branch coverage
394+
// and MC/DC coverage support are more mature.
395+
396+
pub(super) fn extract_branch_pairs(
397+
mir_body: &mir::Body<'_>,
398+
hir_info: &ExtractedHirInfo,
399+
basic_coverage_blocks: &CoverageGraph,
400+
) -> Vec<BcbBranchPair> {
401+
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] };
402+
403+
let block_markers = resolve_block_markers(branch_info, mir_body);
404+
405+
branch_info
406+
.branch_spans
407+
.iter()
408+
.filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| {
409+
// For now, ignore any branch span that was introduced by
410+
// expansion. This makes things like assert macros less noisy.
411+
if !raw_span.ctxt().outer_expn_data().is_root() {
412+
return None;
413+
}
414+
let (span, _) =
415+
unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?;
416+
417+
let bcb_from_marker =
418+
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
419+
420+
let true_bcb = bcb_from_marker(true_marker)?;
421+
let false_bcb = bcb_from_marker(false_marker)?;
422+
423+
Some(BcbBranchPair { span, true_bcb, false_bcb })
424+
})
425+
.collect::<Vec<_>>()
426+
}
427+
428+
pub(super) fn extract_mcdc_mappings(
429+
mir_body: &mir::Body<'_>,
430+
body_span: Span,
431+
basic_coverage_blocks: &CoverageGraph,
432+
) -> Vec<BcbMapping> {
433+
let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
434+
return vec![];
435+
};
436+
437+
let block_markers = resolve_block_markers(branch_info, mir_body);
438+
392439
let bcb_from_marker =
393440
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
394441

@@ -406,12 +453,6 @@ pub(super) fn extract_branch_mappings(
406453
Some((span, true_bcb, false_bcb))
407454
};
408455

409-
let branch_filter_map = |&BranchSpan { span: raw_span, true_marker, false_marker }| {
410-
check_branch_bcb(raw_span, true_marker, false_marker).map(|(span, true_bcb, false_bcb)| {
411-
BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span }
412-
})
413-
};
414-
415456
let mcdc_branch_filter_map =
416457
|&MCDCBranchSpan { span: raw_span, true_marker, false_marker, condition_info }| {
417458
check_branch_bcb(raw_span, true_marker, false_marker).map(
@@ -446,10 +487,7 @@ pub(super) fn extract_branch_mappings(
446487
})
447488
};
448489

449-
branch_info
450-
.branch_spans
451-
.iter()
452-
.filter_map(branch_filter_map)
490+
std::iter::empty()
453491
.chain(branch_info.mcdc_branch_spans.iter().filter_map(mcdc_branch_filter_map))
454492
.chain(branch_info.mcdc_decision_spans.iter().filter_map(decision_filter_map))
455493
.collect::<Vec<_>>()

‎tests/coverage/branch/if-let.cov-map

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Function name: if_let::if_let
2+
Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 07, 03, 05, 01, 02]
3+
Number of files: 1
4+
- file 0 => global file 1
5+
Number of expressions: 2
6+
- expression 0 operands: lhs = Counter(1), rhs = Counter(2)
7+
- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
8+
Number of file 0 mappings: 6
9+
- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
10+
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 18)
11+
= (c1 - c2)
12+
- Code(Counter(1)) at (prev + 0, 22) to (start + 0, 27)
13+
- Code(Expression(0, Sub)) at (prev + 0, 28) to (start + 2, 6)
14+
= (c1 - c2)
15+
- Code(Counter(2)) at (prev + 2, 12) to (start + 2, 6)
16+
- Code(Expression(1, Add)) at (prev + 3, 5) to (start + 1, 2)
17+
= (c2 + (c1 - c2))
18+
19+
Function name: if_let::if_let_chain
20+
Raw bytes (52): 0x[01, 01, 04, 01, 05, 05, 09, 0f, 0d, 05, 09, 08, 01, 17, 01, 00, 33, 02, 01, 11, 00, 12, 01, 00, 16, 00, 17, 0d, 01, 15, 00, 16, 02, 00, 1a, 00, 1b, 0d, 01, 05, 03, 06, 0f, 03, 0c, 02, 06, 0b, 03, 05, 01, 02]
21+
Number of files: 1
22+
- file 0 => global file 1
23+
Number of expressions: 4
24+
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
25+
- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
26+
- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3)
27+
- expression 3 operands: lhs = Counter(1), rhs = Counter(2)
28+
Number of file 0 mappings: 8
29+
- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 51)
30+
- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 18)
31+
= (c0 - c1)
32+
- Code(Counter(0)) at (prev + 0, 22) to (start + 0, 23)
33+
- Code(Counter(3)) at (prev + 1, 21) to (start + 0, 22)
34+
- Code(Expression(0, Sub)) at (prev + 0, 26) to (start + 0, 27)
35+
= (c0 - c1)
36+
- Code(Counter(3)) at (prev + 1, 5) to (start + 3, 6)
37+
- Code(Expression(3, Add)) at (prev + 3, 12) to (start + 2, 6)
38+
= (c1 + c2)
39+
- Code(Expression(2, Add)) at (prev + 3, 5) to (start + 1, 2)
40+
= ((c1 + c2) + c3)
41+

‎tests/coverage/branch/if-let.coverage

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
LL| |#![feature(coverage_attribute, let_chains)]
2+
LL| |//@ edition: 2021
3+
LL| |//@ compile-flags: -Zcoverage-options=branch
4+
LL| |//@ llvm-cov-flags: --show-branches=count
5+
LL| |
6+
LL| |macro_rules! no_merge {
7+
LL| | () => {
8+
LL| | for _ in 0..1 {}
9+
LL| | };
10+
LL| |}
11+
LL| |
12+
LL| 3|fn if_let(input: Option<&str>) {
13+
LL| 3| no_merge!();
14+
LL| |
15+
LL| 3| if let Some(x) = input {
16+
^2
17+
LL| 2| say(x);
18+
LL| 2| } else {
19+
LL| 1| say("none");
20+
LL| 1| }
21+
LL| 3| say("done");
22+
LL| 3|}
23+
LL| |
24+
LL| 15|fn if_let_chain(a: Option<&str>, b: Option<&str>) {
25+
LL| 15| if let Some(x) = a
26+
^12
27+
LL| 12| && let Some(y) = b
28+
^8
29+
LL| 8| {
30+
LL| 8| say(x);
31+
LL| 8| say(y);
32+
LL| 8| } else {
33+
LL| 7| say("not both");
34+
LL| 7| }
35+
LL| 15| say("done");
36+
LL| 15|}
37+
LL| |
38+
LL| |#[coverage(off)]
39+
LL| |fn say(message: &str) {
40+
LL| | core::hint::black_box(message);
41+
LL| |}
42+
LL| |
43+
LL| |#[coverage(off)]
44+
LL| |fn main() {
45+
LL| | if_let(Some("x"));
46+
LL| | if_let(Some("x"));
47+
LL| | if_let(None);
48+
LL| |
49+
LL| | for _ in 0..8 {
50+
LL| | if_let_chain(Some("a"), Some("b"));
51+
LL| | }
52+
LL| | for _ in 0..4 {
53+
LL| | if_let_chain(Some("a"), None);
54+
LL| | }
55+
LL| | for _ in 0..2 {
56+
LL| | if_let_chain(None, Some("b"));
57+
LL| | }
58+
LL| | if_let_chain(None, None);
59+
LL| |}
60+
LL| |
61+
LL| |// FIXME(#124118) Actually instrument if-let and let-chains for branch coverage.
62+

‎tests/coverage/branch/if-let.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#![feature(coverage_attribute, let_chains)]
2+
//@ edition: 2021
3+
//@ compile-flags: -Zcoverage-options=branch
4+
//@ llvm-cov-flags: --show-branches=count
5+
6+
macro_rules! no_merge {
7+
() => {
8+
for _ in 0..1 {}
9+
};
10+
}
11+
12+
fn if_let(input: Option<&str>) {
13+
no_merge!();
14+
15+
if let Some(x) = input {
16+
say(x);
17+
} else {
18+
say("none");
19+
}
20+
say("done");
21+
}
22+
23+
fn if_let_chain(a: Option<&str>, b: Option<&str>) {
24+
if let Some(x) = a
25+
&& let Some(y) = b
26+
{
27+
say(x);
28+
say(y);
29+
} else {
30+
say("not both");
31+
}
32+
say("done");
33+
}
34+
35+
#[coverage(off)]
36+
fn say(message: &str) {
37+
core::hint::black_box(message);
38+
}
39+
40+
#[coverage(off)]
41+
fn main() {
42+
if_let(Some("x"));
43+
if_let(Some("x"));
44+
if_let(None);
45+
46+
for _ in 0..8 {
47+
if_let_chain(Some("a"), Some("b"));
48+
}
49+
for _ in 0..4 {
50+
if_let_chain(Some("a"), None);
51+
}
52+
for _ in 0..2 {
53+
if_let_chain(None, Some("b"));
54+
}
55+
if_let_chain(None, None);
56+
}
57+
58+
// FIXME(#124118) Actually instrument if-let and let-chains for branch coverage.
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Function name: let_else::let_else
2+
Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 07, 01, 01, 00, 02]
3+
Number of files: 1
4+
- file 0 => global file 1
5+
Number of expressions: 2
6+
- expression 0 operands: lhs = Counter(1), rhs = Counter(2)
7+
- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
8+
Number of file 0 mappings: 6
9+
- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
10+
- Code(Expression(0, Sub)) at (prev + 3, 14) to (start + 0, 15)
11+
= (c1 - c2)
12+
- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24)
13+
- Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15)
14+
- Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 0, 11)
15+
= (c1 - c2)
16+
- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2)
17+
= (c2 + (c1 - c2))
18+
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
LL| |#![feature(coverage_attribute)]
2+
LL| |//@ edition: 2021
3+
LL| |//@ compile-flags: -Zcoverage-options=branch
4+
LL| |//@ llvm-cov-flags: --show-branches=count
5+
LL| |
6+
LL| |macro_rules! no_merge {
7+
LL| | () => {
8+
LL| | for _ in 0..1 {}
9+
LL| | };
10+
LL| |}
11+
LL| |
12+
LL| 3|fn let_else(value: Option<&str>) {
13+
LL| 3| no_merge!();
14+
LL| |
15+
LL| 3| let Some(x) = value else {
16+
^2
17+
LL| 1| say("none");
18+
LL| 1| return;
19+
LL| | };
20+
LL| |
21+
LL| 2| say(x);
22+
LL| 3|}
23+
LL| |
24+
LL| |#[coverage(off)]
25+
LL| |fn say(message: &str) {
26+
LL| | core::hint::black_box(message);
27+
LL| |}
28+
LL| |
29+
LL| |#[coverage(off)]
30+
LL| |fn main() {
31+
LL| | let_else(Some("x"));
32+
LL| | let_else(Some("x"));
33+
LL| | let_else(None);
34+
LL| |}
35+
LL| |
36+
LL| |// FIXME(#124118) Actually instrument let-else for branch coverage.
37+

‎tests/coverage/branch/let-else.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![feature(coverage_attribute)]
2+
//@ edition: 2021
3+
//@ compile-flags: -Zcoverage-options=branch
4+
//@ llvm-cov-flags: --show-branches=count
5+
6+
macro_rules! no_merge {
7+
() => {
8+
for _ in 0..1 {}
9+
};
10+
}
11+
12+
fn let_else(value: Option<&str>) {
13+
no_merge!();
14+
15+
let Some(x) = value else {
16+
say("none");
17+
return;
18+
};
19+
20+
say(x);
21+
}
22+
23+
#[coverage(off)]
24+
fn say(message: &str) {
25+
core::hint::black_box(message);
26+
}
27+
28+
#[coverage(off)]
29+
fn main() {
30+
let_else(Some("x"));
31+
let_else(Some("x"));
32+
let_else(None);
33+
}
34+
35+
// FIXME(#124118) Actually instrument let-else for branch coverage.
+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
Function name: match_arms::guards
2+
Raw bytes (88): 0x[01, 01, 08, 07, 15, 0b, 11, 0f, 0d, 00, 09, 17, 25, 1b, 21, 1f, 1d, 03, 19, 0c, 01, 30, 01, 01, 10, 29, 03, 0b, 00, 10, 19, 01, 11, 00, 29, 20, 19, 09, 00, 17, 00, 1b, 1d, 01, 11, 00, 29, 20, 1d, 0d, 00, 17, 00, 1b, 21, 01, 11, 00, 29, 20, 21, 11, 00, 17, 00, 1b, 25, 01, 11, 00, 29, 20, 25, 15, 00, 17, 00, 1b, 03, 01, 0e, 00, 18, 13, 03, 05, 01, 02]
3+
Number of files: 1
4+
- file 0 => global file 1
5+
Number of expressions: 8
6+
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(5)
7+
- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4)
8+
- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3)
9+
- expression 3 operands: lhs = Zero, rhs = Counter(2)
10+
- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(9)
11+
- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(8)
12+
- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(7)
13+
- expression 7 operands: lhs = Expression(0, Add), rhs = Counter(6)
14+
Number of file 0 mappings: 12
15+
- Code(Counter(0)) at (prev + 48, 1) to (start + 1, 16)
16+
- Code(Counter(10)) at (prev + 3, 11) to (start + 0, 16)
17+
- Code(Counter(6)) at (prev + 1, 17) to (start + 0, 41)
18+
- Branch { true: Counter(6), false: Counter(2) } at (prev + 0, 23) to (start + 0, 27)
19+
true = c6
20+
false = c2
21+
- Code(Counter(7)) at (prev + 1, 17) to (start + 0, 41)
22+
- Branch { true: Counter(7), false: Counter(3) } at (prev + 0, 23) to (start + 0, 27)
23+
true = c7
24+
false = c3
25+
- Code(Counter(8)) at (prev + 1, 17) to (start + 0, 41)
26+
- Branch { true: Counter(8), false: Counter(4) } at (prev + 0, 23) to (start + 0, 27)
27+
true = c8
28+
false = c4
29+
- Code(Counter(9)) at (prev + 1, 17) to (start + 0, 41)
30+
- Branch { true: Counter(9), false: Counter(5) } at (prev + 0, 23) to (start + 0, 27)
31+
true = c9
32+
false = c5
33+
- Code(Expression(0, Add)) at (prev + 1, 14) to (start + 0, 24)
34+
= ((((Zero + c2) + c3) + c4) + c5)
35+
- Code(Expression(4, Add)) at (prev + 3, 5) to (start + 1, 2)
36+
= ((((((((Zero + c2) + c3) + c4) + c5) + c6) + c7) + c8) + c9)
37+
38+
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]
40+
Number of files: 1
41+
- 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
50+
- Code(Counter(0)) at (prev + 24, 1) to (start + 1, 16)
51+
- 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)
56+
= (c1 - ((c2 + c3) + c4))
57+
- Code(Expression(3, Add)) at (prev + 3, 5) to (start + 1, 2)
58+
= (((c4 + c3) + c2) + (c1 - ((c2 + c3) + c4)))
59+
60+
Function name: match_arms::or_patterns
61+
Raw bytes (75): 0x[01, 01, 0d, 11, 0d, 05, 2f, 33, 11, 09, 0d, 09, 2a, 05, 2f, 33, 11, 09, 0d, 03, 27, 09, 2a, 05, 2f, 33, 11, 09, 0d, 09, 01, 25, 01, 01, 10, 05, 03, 0b, 00, 10, 11, 01, 11, 00, 12, 0d, 00, 1e, 00, 1f, 03, 00, 24, 00, 2e, 09, 01, 11, 00, 12, 2a, 00, 1e, 00, 1f, 27, 00, 24, 00, 2e, 23, 03, 05, 01, 02]
62+
Number of files: 1
63+
- file 0 => global file 1
64+
Number of expressions: 13
65+
- expression 0 operands: lhs = Counter(4), rhs = Counter(3)
66+
- expression 1 operands: lhs = Counter(1), rhs = Expression(11, Add)
67+
- expression 2 operands: lhs = Expression(12, Add), rhs = Counter(4)
68+
- expression 3 operands: lhs = Counter(2), rhs = Counter(3)
69+
- expression 4 operands: lhs = Counter(2), rhs = Expression(10, Sub)
70+
- expression 5 operands: lhs = Counter(1), rhs = Expression(11, Add)
71+
- expression 6 operands: lhs = Expression(12, Add), rhs = Counter(4)
72+
- expression 7 operands: lhs = Counter(2), rhs = Counter(3)
73+
- expression 8 operands: lhs = Expression(0, Add), rhs = Expression(9, Add)
74+
- expression 9 operands: lhs = Counter(2), rhs = Expression(10, Sub)
75+
- expression 10 operands: lhs = Counter(1), rhs = Expression(11, Add)
76+
- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(4)
77+
- expression 12 operands: lhs = Counter(2), rhs = Counter(3)
78+
Number of file 0 mappings: 9
79+
- Code(Counter(0)) at (prev + 37, 1) to (start + 1, 16)
80+
- Code(Counter(1)) at (prev + 3, 11) to (start + 0, 16)
81+
- Code(Counter(4)) at (prev + 1, 17) to (start + 0, 18)
82+
- Code(Counter(3)) at (prev + 0, 30) to (start + 0, 31)
83+
- Code(Expression(0, Add)) at (prev + 0, 36) to (start + 0, 46)
84+
= (c4 + c3)
85+
- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 18)
86+
- Code(Expression(10, Sub)) at (prev + 0, 30) to (start + 0, 31)
87+
= (c1 - ((c2 + c3) + c4))
88+
- Code(Expression(9, Add)) at (prev + 0, 36) to (start + 0, 46)
89+
= (c2 + (c1 - ((c2 + c3) + c4)))
90+
- Code(Expression(8, Add)) at (prev + 3, 5) to (start + 1, 2)
91+
= ((c4 + c3) + (c2 + (c1 - ((c2 + c3) + c4))))
92+
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
LL| |#![feature(coverage_attribute)]
2+
LL| |//@ edition: 2021
3+
LL| |//@ compile-flags: -Zcoverage-options=branch
4+
LL| |//@ llvm-cov-flags: --show-branches=count
5+
LL| |
6+
LL| |// Tests for branch coverage of various kinds of match arms.
7+
LL| |
8+
LL| |// Helper macro to prevent start-of-function spans from being merged into
9+
LL| |// spans on the lines we care about.
10+
LL| |macro_rules! no_merge {
11+
LL| | () => {
12+
LL| | for _ in 0..1 {}
13+
LL| | };
14+
LL| |}
15+
LL| |
16+
LL| |#[derive(Clone, Copy, Debug)]
17+
LL| |enum Enum {
18+
LL| | A(u32),
19+
LL| | B(u32),
20+
LL| | C(u32),
21+
LL| | D(u32),
22+
LL| |}
23+
LL| |
24+
LL| 15|fn match_arms(value: Enum) {
25+
LL| 15| no_merge!();
26+
LL| |
27+
LL| 15| match value {
28+
LL| 8| Enum::D(d) => consume(d),
29+
LL| 4| Enum::C(c) => consume(c),
30+
LL| 2| Enum::B(b) => consume(b),
31+
LL| 1| Enum::A(a) => consume(a),
32+
LL| | }
33+
LL| |
34+
LL| 15| consume(0);
35+
LL| 15|}
36+
LL| |
37+
LL| 15|fn or_patterns(value: Enum) {
38+
LL| 15| no_merge!();
39+
LL| |
40+
LL| 15| match value {
41+
LL| 12| Enum::D(x) | Enum::C(x) => consume(x),
42+
^8 ^4
43+
LL| 3| Enum::B(y) | Enum::A(y) => consume(y),
44+
^2 ^1
45+
LL| | }
46+
LL| |
47+
LL| 15| consume(0);
48+
LL| 15|}
49+
LL| |
50+
LL| 45|fn guards(value: Enum, cond: bool) {
51+
LL| 45| no_merge!();
52+
LL| |
53+
LL| 3| match value {
54+
LL| 8| Enum::D(d) if cond => consume(d),
55+
------------------
56+
| Branch (LL:23): [True: 8, False: 16]
57+
------------------
58+
LL| 4| Enum::C(c) if cond => consume(c),
59+
------------------
60+
| Branch (LL:23): [True: 4, False: 8]
61+
------------------
62+
LL| 2| Enum::B(b) if cond => consume(b),
63+
------------------
64+
| Branch (LL:23): [True: 2, False: 4]
65+
------------------
66+
LL| 1| Enum::A(a) if cond => consume(a),
67+
------------------
68+
| Branch (LL:23): [True: 1, False: 2]
69+
------------------
70+
LL| 30| _ => consume(0),
71+
LL| | }
72+
LL| |
73+
LL| 45| consume(0);
74+
LL| 45|}
75+
LL| |
76+
LL| |#[coverage(off)]
77+
LL| |fn consume<T>(x: T) {
78+
LL| | core::hint::black_box(x);
79+
LL| |}
80+
LL| |
81+
LL| |#[coverage(off)]
82+
LL| |fn main() {
83+
LL| | #[coverage(off)]
84+
LL| | fn call_everything(e: Enum) {
85+
LL| | match_arms(e);
86+
LL| | or_patterns(e);
87+
LL| | for cond in [false, false, true] {
88+
LL| | guards(e, cond);
89+
LL| | }
90+
LL| | }
91+
LL| |
92+
LL| | call_everything(Enum::A(0));
93+
LL| | for b in 0..2 {
94+
LL| | call_everything(Enum::B(b));
95+
LL| | }
96+
LL| | for c in 0..4 {
97+
LL| | call_everything(Enum::C(c));
98+
LL| | }
99+
LL| | for d in 0..8 {
100+
LL| | call_everything(Enum::D(d));
101+
LL| | }
102+
LL| |}
103+
LL| |
104+
LL| |// FIXME(#124118) Actually instrument match arms for branch coverage.
105+

‎tests/coverage/branch/match-arms.rs

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#![feature(coverage_attribute)]
2+
//@ edition: 2021
3+
//@ compile-flags: -Zcoverage-options=branch
4+
//@ llvm-cov-flags: --show-branches=count
5+
6+
// Tests for branch coverage of various kinds of match arms.
7+
8+
// Helper macro to prevent start-of-function spans from being merged into
9+
// spans on the lines we care about.
10+
macro_rules! no_merge {
11+
() => {
12+
for _ in 0..1 {}
13+
};
14+
}
15+
16+
#[derive(Clone, Copy, Debug)]
17+
enum Enum {
18+
A(u32),
19+
B(u32),
20+
C(u32),
21+
D(u32),
22+
}
23+
24+
fn match_arms(value: Enum) {
25+
no_merge!();
26+
27+
match value {
28+
Enum::D(d) => consume(d),
29+
Enum::C(c) => consume(c),
30+
Enum::B(b) => consume(b),
31+
Enum::A(a) => consume(a),
32+
}
33+
34+
consume(0);
35+
}
36+
37+
fn or_patterns(value: Enum) {
38+
no_merge!();
39+
40+
match value {
41+
Enum::D(x) | Enum::C(x) => consume(x),
42+
Enum::B(y) | Enum::A(y) => consume(y),
43+
}
44+
45+
consume(0);
46+
}
47+
48+
fn guards(value: Enum, cond: bool) {
49+
no_merge!();
50+
51+
match value {
52+
Enum::D(d) if cond => consume(d),
53+
Enum::C(c) if cond => consume(c),
54+
Enum::B(b) if cond => consume(b),
55+
Enum::A(a) if cond => consume(a),
56+
_ => consume(0),
57+
}
58+
59+
consume(0);
60+
}
61+
62+
#[coverage(off)]
63+
fn consume<T>(x: T) {
64+
core::hint::black_box(x);
65+
}
66+
67+
#[coverage(off)]
68+
fn main() {
69+
#[coverage(off)]
70+
fn call_everything(e: Enum) {
71+
match_arms(e);
72+
or_patterns(e);
73+
for cond in [false, false, true] {
74+
guards(e, cond);
75+
}
76+
}
77+
78+
call_everything(Enum::A(0));
79+
for b in 0..2 {
80+
call_everything(Enum::B(b));
81+
}
82+
for c in 0..4 {
83+
call_everything(Enum::C(c));
84+
}
85+
for d in 0..8 {
86+
call_everything(Enum::D(d));
87+
}
88+
}
89+
90+
// FIXME(#124118) Actually instrument match arms for branch coverage.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Function name: match_trivial::_uninhabited (unused)
2+
Raw bytes (9): 0x[01, 01, 00, 01, 00, 16, 01, 01, 10]
3+
Number of files: 1
4+
- file 0 => global file 1
5+
Number of expressions: 0
6+
Number of file 0 mappings: 1
7+
- Code(Zero) at (prev + 22, 1) to (start + 1, 16)
8+
9+
Function name: match_trivial::trivial
10+
Raw bytes (14): 0x[01, 01, 00, 02, 01, 1e, 01, 01, 10, 05, 03, 0b, 05, 02]
11+
Number of files: 1
12+
- file 0 => global file 1
13+
Number of expressions: 0
14+
Number of file 0 mappings: 2
15+
- Code(Counter(0)) at (prev + 30, 1) to (start + 1, 16)
16+
- Code(Counter(1)) at (prev + 3, 11) to (start + 5, 2)
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
LL| |#![feature(coverage_attribute)]
2+
LL| |//@ edition: 2021
3+
LL| |//@ compile-flags: -Zcoverage-options=branch
4+
LL| |//@ llvm-cov-flags: --show-branches=count
5+
LL| |
6+
LL| |// When instrumenting match expressions for branch coverage, make sure we don't
7+
LL| |// cause an ICE or produce weird coverage output for matches with <2 arms.
8+
LL| |
9+
LL| |// Helper macro to prevent start-of-function spans from being merged into
10+
LL| |// spans on the lines we care about.
11+
LL| |macro_rules! no_merge {
12+
LL| | () => {
13+
LL| | for _ in 0..1 {}
14+
LL| | };
15+
LL| |}
16+
LL| |
17+
LL| |enum Uninhabited {}
18+
LL| |enum Trivial {
19+
LL| | Value,
20+
LL| |}
21+
LL| |
22+
LL| 0|fn _uninhabited(x: Uninhabited) {
23+
LL| 0| no_merge!();
24+
LL| |
25+
LL| | match x {}
26+
LL| |
27+
LL| | consume("done");
28+
LL| |}
29+
LL| |
30+
LL| 1|fn trivial(x: Trivial) {
31+
LL| 1| no_merge!();
32+
LL| |
33+
LL| 1| match x {
34+
LL| 1| Trivial::Value => consume("trivial"),
35+
LL| 1| }
36+
LL| 1|
37+
LL| 1| consume("done");
38+
LL| 1|}
39+
LL| |
40+
LL| |#[coverage(off)]
41+
LL| |fn consume<T>(x: T) {
42+
LL| | core::hint::black_box(x);
43+
LL| |}
44+
LL| |
45+
LL| |#[coverage(off)]
46+
LL| |fn main() {
47+
LL| | trivial(Trivial::Value);
48+
LL| |}
49+
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#![feature(coverage_attribute)]
2+
//@ edition: 2021
3+
//@ compile-flags: -Zcoverage-options=branch
4+
//@ llvm-cov-flags: --show-branches=count
5+
6+
// When instrumenting match expressions for branch coverage, make sure we don't
7+
// cause an ICE or produce weird coverage output for matches with <2 arms.
8+
9+
// Helper macro to prevent start-of-function spans from being merged into
10+
// spans on the lines we care about.
11+
macro_rules! no_merge {
12+
() => {
13+
for _ in 0..1 {}
14+
};
15+
}
16+
17+
enum Uninhabited {}
18+
enum Trivial {
19+
Value,
20+
}
21+
22+
fn _uninhabited(x: Uninhabited) {
23+
no_merge!();
24+
25+
match x {}
26+
27+
consume("done");
28+
}
29+
30+
fn trivial(x: Trivial) {
31+
no_merge!();
32+
33+
match x {
34+
Trivial::Value => consume("trivial"),
35+
}
36+
37+
consume("done");
38+
}
39+
40+
#[coverage(off)]
41+
fn consume<T>(x: T) {
42+
core::hint::black_box(x);
43+
}
44+
45+
#[coverage(off)]
46+
fn main() {
47+
trivial(Trivial::Value);
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
- // MIR for `main` before InstrumentCoverage
2+
+ // MIR for `main` after InstrumentCoverage
3+
4+
fn main() -> () {
5+
let mut _0: ();
6+
let mut _1: Enum;
7+
let mut _2: isize;
8+
let _3: u32;
9+
let mut _4: u32;
10+
let _5: u32;
11+
let mut _6: u32;
12+
let _7: u32;
13+
let mut _8: u32;
14+
let _9: u32;
15+
let mut _10: u32;
16+
scope 1 {
17+
debug d => _3;
18+
}
19+
scope 2 {
20+
debug c => _5;
21+
}
22+
scope 3 {
23+
debug b => _7;
24+
}
25+
scope 4 {
26+
debug a => _9;
27+
}
28+
29+
+ coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) };
30+
+ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) };
31+
+ coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) };
32+
+ coverage ExpressionId(3) => Expression { lhs: Counter(3), op: Add, rhs: Counter(2) };
33+
+ coverage ExpressionId(4) => Expression { lhs: Expression(3), op: Add, rhs: Counter(1) };
34+
+ coverage ExpressionId(5) => Expression { lhs: Expression(4), op: Add, rhs: Expression(2) };
35+
+ coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1 - 15:21;
36+
+ coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:16:17 - 16:33;
37+
+ coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17 - 17:33;
38+
+ coverage Code(Counter(1)) => $DIR/branch_match_arms.rs:18:17 - 18:33;
39+
+ coverage Code(Expression(2)) => $DIR/branch_match_arms.rs:19:17 - 19:33;
40+
+ coverage Code(Expression(5)) => $DIR/branch_match_arms.rs:21:1 - 21:2;
41+
+
42+
bb0: {
43+
+ Coverage::CounterIncrement(0);
44+
StorageLive(_1);
45+
_1 = Enum::A(const 0_u32);
46+
PlaceMention(_1);
47+
_2 = discriminant(_1);
48+
switchInt(move _2) -> [0: bb5, 1: bb4, 2: bb3, 3: bb2, otherwise: bb1];
49+
}
50+
51+
bb1: {
52+
FakeRead(ForMatchedPlace(None), _1);
53+
unreachable;
54+
}
55+
56+
bb2: {
57+
+ Coverage::CounterIncrement(3);
58+
falseEdge -> [real: bb6, imaginary: bb3];
59+
}
60+
61+
bb3: {
62+
+ Coverage::CounterIncrement(2);
63+
falseEdge -> [real: bb8, imaginary: bb4];
64+
}
65+
66+
bb4: {
67+
+ Coverage::CounterIncrement(1);
68+
falseEdge -> [real: bb10, imaginary: bb5];
69+
}
70+
71+
bb5: {
72+
+ Coverage::ExpressionUsed(2);
73+
StorageLive(_9);
74+
_9 = ((_1 as A).0: u32);
75+
StorageLive(_10);
76+
_10 = _9;
77+
_0 = consume(move _10) -> [return: bb12, unwind: bb14];
78+
}
79+
80+
bb6: {
81+
StorageLive(_3);
82+
_3 = ((_1 as D).0: u32);
83+
StorageLive(_4);
84+
_4 = _3;
85+
_0 = consume(move _4) -> [return: bb7, unwind: bb14];
86+
}
87+
88+
bb7: {
89+
StorageDead(_4);
90+
StorageDead(_3);
91+
goto -> bb13;
92+
}
93+
94+
bb8: {
95+
StorageLive(_5);
96+
_5 = ((_1 as C).0: u32);
97+
StorageLive(_6);
98+
_6 = _5;
99+
_0 = consume(move _6) -> [return: bb9, unwind: bb14];
100+
}
101+
102+
bb9: {
103+
StorageDead(_6);
104+
StorageDead(_5);
105+
goto -> bb13;
106+
}
107+
108+
bb10: {
109+
StorageLive(_7);
110+
_7 = ((_1 as B).0: u32);
111+
StorageLive(_8);
112+
_8 = _7;
113+
_0 = consume(move _8) -> [return: bb11, unwind: bb14];
114+
}
115+
116+
bb11: {
117+
StorageDead(_8);
118+
StorageDead(_7);
119+
goto -> bb13;
120+
}
121+
122+
bb12: {
123+
StorageDead(_10);
124+
StorageDead(_9);
125+
goto -> bb13;
126+
}
127+
128+
bb13: {
129+
+ Coverage::ExpressionUsed(5);
130+
StorageDead(_1);
131+
return;
132+
}
133+
134+
bb14 (cleanup): {
135+
resume;
136+
}
137+
}
138+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![feature(coverage_attribute)]
2+
//@ test-mir-pass: InstrumentCoverage
3+
//@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime -Zcoverage-options=branch
4+
// skip-filecheck
5+
6+
enum Enum {
7+
A(u32),
8+
B(u32),
9+
C(u32),
10+
D(u32),
11+
}
12+
13+
// EMIT_MIR branch_match_arms.main.InstrumentCoverage.diff
14+
fn main() {
15+
match Enum::A(0) {
16+
Enum::D(d) => consume(d),
17+
Enum::C(c) => consume(c),
18+
Enum::B(b) => consume(b),
19+
Enum::A(a) => consume(a),
20+
}
21+
}
22+
23+
#[inline(never)]
24+
#[coverage(off)]
25+
fn consume(x: u32) {
26+
core::hint::black_box(x);
27+
}

0 commit comments

Comments
 (0)
Please sign in to comment.