Skip to content

Commit 2263a41

Browse files
author
zhuyunxing
committed
coverage. Implement branch coverage in mcdc for pattern match
1 parent 9cf10bc commit 2263a41

File tree

8 files changed

+283
-16
lines changed

8 files changed

+283
-16
lines changed

compiler/rustc_middle/src/mir/coverage.rs

+11
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ pub struct BranchInfo {
281281
pub num_block_markers: usize,
282282
pub branch_spans: Vec<BranchSpan>,
283283
pub mcdc_branch_spans: Vec<MCDCBranchSpan>,
284+
pub mcdc_match_branch_spans: Vec<MCDCMatchBranchSpan>,
284285
pub mcdc_decision_spans: Vec<MCDCDecisionSpan>,
285286
}
286287

@@ -321,6 +322,16 @@ pub struct MCDCBranchSpan {
321322
pub false_marker: BlockMarkerId,
322323
}
323324

325+
#[derive(Clone, Debug)]
326+
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
327+
pub struct MCDCMatchBranchSpan {
328+
pub span: Span,
329+
pub condition_info: Option<ConditionInfo>,
330+
pub test_markers: Vec<BlockMarkerId>,
331+
pub true_markers: Vec<BlockMarkerId>,
332+
pub substract_markers: Vec<BlockMarkerId>,
333+
}
334+
324335
#[derive(Copy, Clone, Debug)]
325336
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
326337
pub struct DecisionInfo {

compiler/rustc_middle/src/mir/pretty.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -485,12 +485,16 @@ fn write_coverage_branch_info(
485485
)?;
486486
}
487487

488-
for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
489-
mcdc_branch_spans
488+
for coverage::MCDCBranchSpan {
489+
span,
490+
condition_info,
491+
true_marker: true_markers,
492+
false_marker: false_markers,
493+
} in mcdc_branch_spans
490494
{
491495
writeln!(
492496
w,
493-
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
497+
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_markers:?}, false: {false_markers:?} }} => {span:?}",
494498
condition_info.map(|info| info.condition_id)
495499
)?;
496500
}

compiler/rustc_mir_build/src/build/coverageinfo.rs

+92-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use std::assert_matches::assert_matches;
22
use std::collections::hash_map::Entry;
3-
use std::collections::VecDeque;
3+
use std::collections::{BTreeMap, VecDeque};
44

55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_middle::mir::coverage::{
77
BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageKind, MCDCBranchSpan,
8-
MCDCDecisionSpan,
8+
MCDCDecisionSpan, MCDCMatchBranchSpan,
99
};
10-
use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
10+
use rustc_middle::mir::{self, BasicBlock, SourceInfo, StatementKind, UnOp};
1111
use rustc_middle::thir::{ExprId, ExprKind, LogicalOp, Thir};
1212
use rustc_middle::ty::TyCtxt;
1313
use rustc_span::def_id::LocalDefId;
@@ -22,7 +22,8 @@ pub(crate) struct BranchInfoBuilder {
2222

2323
num_block_markers: usize,
2424
branch_spans: Vec<BranchSpan>,
25-
25+
pattern_match_branch_records: BTreeMap<Span, MCDCMatchBranchSpan>,
26+
markers_map: BTreeMap<usize, BasicBlock>,
2627
mcdc_branch_spans: Vec<MCDCBranchSpan>,
2728
mcdc_decision_spans: Vec<MCDCDecisionSpan>,
2829
mcdc_state: Option<MCDCState>,
@@ -47,6 +48,8 @@ impl BranchInfoBuilder {
4748
nots: FxHashMap::default(),
4849
num_block_markers: 0,
4950
branch_spans: vec![],
51+
pattern_match_branch_records: BTreeMap::new(),
52+
markers_map: Default::default(),
5053
mcdc_branch_spans: vec![],
5154
mcdc_decision_spans: vec![],
5255
mcdc_state: MCDCState::new_if_enabled(tcx),
@@ -175,6 +178,8 @@ impl BranchInfoBuilder {
175178
nots: _,
176179
num_block_markers,
177180
branch_spans,
181+
pattern_match_branch_records,
182+
markers_map: _,
178183
mcdc_branch_spans,
179184
mcdc_decision_spans,
180185
mcdc_state: _,
@@ -184,11 +189,24 @@ impl BranchInfoBuilder {
184189
assert!(branch_spans.is_empty());
185190
return None;
186191
}
192+
let mut mcdc_match_branch_spans =
193+
pattern_match_branch_records.into_values().collect::<Vec<_>>();
194+
mcdc_match_branch_spans.sort_by(|a, b| {
195+
if a.span.contains(b.span) { std::cmp::Ordering::Less } else { a.span.cmp(&b.span) }
196+
});
197+
for idx in 0..(mcdc_match_branch_spans.len() - 1) {
198+
if mcdc_match_branch_spans[idx].span.contains(mcdc_match_branch_spans[idx + 1].span) {
199+
let refined_span =
200+
mcdc_match_branch_spans[idx].span.until(mcdc_match_branch_spans[idx + 1].span);
201+
mcdc_match_branch_spans[idx].span = refined_span;
202+
}
203+
}
187204

188205
Some(Box::new(mir::coverage::BranchInfo {
189206
num_block_markers,
190207
branch_spans,
191208
mcdc_branch_spans,
209+
mcdc_match_branch_spans,
192210
mcdc_decision_spans,
193211
}))
194212
}
@@ -385,4 +403,74 @@ impl Builder<'_, '_> {
385403
mcdc_state.record_conditions(logical_op, span);
386404
}
387405
}
406+
407+
pub(crate) fn visit_coverage_mcdc_match_arms(
408+
&mut self,
409+
test_block: BasicBlock,
410+
targets: impl Iterator<Item = (Span, BasicBlock)>,
411+
) {
412+
if self.tcx.sess.instrument_coverage_mcdc() {
413+
return;
414+
}
415+
let mut markers_map = BTreeMap::<usize, BasicBlock>::new();
416+
let mut get_marker_id = |span, block| {
417+
self.cfg
418+
.block_data_mut(block)
419+
.statements
420+
.iter()
421+
.find_map(|statement| match statement.kind {
422+
StatementKind::Coverage(CoverageKind::BlockMarker { id }) => Some(id),
423+
_ => None,
424+
})
425+
.unwrap_or_else(|| {
426+
let source_info = self.source_info(span);
427+
let id = self
428+
.coverage_branch_info
429+
.as_mut()
430+
.expect("Checked at entry of the function")
431+
.next_block_marker_id();
432+
433+
let marker_statement = mir::Statement {
434+
source_info,
435+
kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
436+
};
437+
markers_map.insert(id.as_usize(), block);
438+
self.cfg.push(block, marker_statement);
439+
id
440+
})
441+
};
442+
let test_block_id = get_marker_id(Span::default(), test_block);
443+
444+
let mut targets = targets
445+
.map(|(span, block)| {
446+
let marker_id = get_marker_id(span, block);
447+
(span, marker_id)
448+
})
449+
.collect::<Vec<_>>();
450+
451+
let branch_records = self
452+
.coverage_branch_info
453+
.as_mut()
454+
.map(|branch_info| {
455+
branch_info.markers_map.extend(markers_map);
456+
&mut branch_info.pattern_match_branch_records
457+
})
458+
.expect("Checked at entry of the function");
459+
460+
while let Some((span, matched_block)) = targets.pop() {
461+
// By here we do not know the span of the target yet, so just record the
462+
// basic blocks and insert BlockMarkerId later.
463+
let record = branch_records.entry(span).or_insert_with(|| MCDCMatchBranchSpan {
464+
span,
465+
condition_info: None,
466+
test_markers: vec![],
467+
true_markers: vec![],
468+
substract_markers: vec![],
469+
});
470+
record.test_markers.push(test_block_id);
471+
record.true_markers.push(matched_block);
472+
473+
record.substract_markers.extend(targets.iter().map(|(_, blk)| *blk));
474+
}
475+
}
388476
}

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

+28
Original file line numberDiff line numberDiff line change
@@ -1857,6 +1857,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18571857
// Extract the match-pair from the highest priority candidate and build a test from it.
18581858
let (match_place, test) = self.pick_test(candidates);
18591859

1860+
// Patterns like `Some(A) | Some(B)` need spans for their target branches separately.
1861+
// Keep it in original order so that true arms of the laters won't be counted as false arms of the formers.
1862+
let mut coverage_targets: Vec<_> = candidates
1863+
.iter()
1864+
.filter_map(|candidate| {
1865+
candidate.match_pairs.first().map(|matched_pair| {
1866+
(candidate.extra_data.span, (matched_pair.pattern.span, None))
1867+
})
1868+
})
1869+
.collect();
1870+
1871+
let mut set_coverage_matched_blk = |pattern_span: Span, blk: BasicBlock| {
1872+
*coverage_targets
1873+
.iter_mut()
1874+
.find_map(|(span, (_, blk_opt))| (*span == pattern_span).then_some(blk_opt))
1875+
.expect("target_candidates only are part of candidates") = Some(blk);
1876+
};
1877+
18601878
// For each of the N possible test outcomes, build the vector of candidates that applies if
18611879
// the test has that particular outcome.
18621880
let (remaining_candidates, target_candidates) =
@@ -1883,6 +1901,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18831901
.into_iter()
18841902
.map(|(branch, mut candidates)| {
18851903
let candidate_start = self.cfg.start_new_block();
1904+
set_coverage_matched_blk(
1905+
candidates.first().expect("candidates must be not empty").extra_data.span,
1906+
candidate_start,
1907+
);
18861908
self.match_candidates(
18871909
span,
18881910
scrutinee_span,
@@ -1894,6 +1916,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18941916
})
18951917
.collect();
18961918

1919+
self.visit_coverage_mcdc_match_arms(
1920+
start_block,
1921+
coverage_targets.into_iter().filter_map(|(_, (span, matched_target_block))| {
1922+
matched_target_block.map(|blk| (span, blk))
1923+
}),
1924+
);
18971925
// Perform the test, branching to one of N blocks.
18981926
self.perform_test(
18991927
span,

compiler/rustc_mir_transform/src/coverage/counters.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::fmt::{self, Debug};
1+
use std::{
2+
collections::BTreeMap,
3+
fmt::{self, Debug},
4+
};
25

36
use rustc_data_structures::captures::Captures;
47
use rustc_data_structures::fx::FxHashMap;
@@ -58,6 +61,8 @@ pub(super) struct CoverageCounters {
5861
/// Table of expression data, associating each expression ID with its
5962
/// corresponding operator (+ or -) and its LHS/RHS operands.
6063
expressions: IndexVec<ExpressionId, Expression>,
64+
65+
branch_sum_expressions: BTreeMap<BasicCoverageBlock, Vec<ExpressionId>>,
6166
}
6267

6368
impl CoverageCounters {
@@ -75,6 +80,7 @@ impl CoverageCounters {
7580
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
7681
bcb_edge_counters: FxHashMap::default(),
7782
expressions: IndexVec::new(),
83+
branch_sum_expressions: BTreeMap::new(),
7884
};
7985

8086
MakeBcbCounters::new(&mut this, basic_coverage_blocks)
@@ -88,7 +94,7 @@ impl CoverageCounters {
8894
BcbCounter::Counter { id }
8995
}
9096

91-
fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
97+
pub fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
9298
let expression = Expression { lhs: lhs.as_term(), op, rhs: rhs.as_term() };
9399
let id = self.expressions.push(expression);
94100
BcbCounter::Expression { id }
@@ -97,7 +103,11 @@ impl CoverageCounters {
97103
/// Variant of `make_expression` that makes `lhs` optional and assumes [`Op::Add`].
98104
///
99105
/// This is useful when using [`Iterator::fold`] to build an arbitrary-length sum.
100-
fn make_sum_expression(&mut self, lhs: Option<BcbCounter>, rhs: BcbCounter) -> BcbCounter {
106+
pub(super) fn make_sum_expression(
107+
&mut self,
108+
lhs: Option<BcbCounter>,
109+
rhs: BcbCounter,
110+
) -> BcbCounter {
101111
let Some(lhs) = lhs else { return rhs };
102112
self.make_expression(lhs, Op::Add, rhs)
103113
}
@@ -138,6 +148,10 @@ impl CoverageCounters {
138148
}
139149
}
140150

151+
pub(super) fn set_bcb_branch_sum_expr(&mut self, bcb: BasicCoverageBlock, expr: ExpressionId) {
152+
self.branch_sum_expressions.entry(bcb).or_default().push(expr);
153+
}
154+
141155
pub(super) fn bcb_counter(&self, bcb: BasicCoverageBlock) -> Option<BcbCounter> {
142156
self.bcb_counters[bcb]
143157
}
@@ -164,6 +178,12 @@ impl CoverageCounters {
164178
})
165179
}
166180

181+
pub(super) fn bcb_nodes_with_branch_sum_expressions(
182+
&self,
183+
) -> impl Iterator<Item = (BasicCoverageBlock, &[ExpressionId])> {
184+
self.branch_sum_expressions.iter().map(|(bcb, exprs)| (*bcb, exprs.as_slice()))
185+
}
186+
167187
pub(super) fn into_expressions(self) -> IndexVec<ExpressionId, Expression> {
168188
self.expressions
169189
}

0 commit comments

Comments
 (0)