Auto merge of #124223 - Zalathar:conditional-let, r=compiler-errors

coverage: Branch coverage support for let-else and if-let

This PR adds branch coverage instrumentation for let-else and if-let, including let-chains.

This lifts two of the limitations listed at #124118.
This commit is contained in:
bors
2024-05-07 22:28:51 +00:00
6 changed files with 64 additions and 13 deletions

View File

@@ -1,17 +1,18 @@
mod mcdc;
use std::assert_matches::assert_matches; use std::assert_matches::assert_matches;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp}; use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
use rustc_middle::thir::{ExprId, ExprKind, Thir}; use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use crate::build::coverageinfo::mcdc::MCDCInfoBuilder; use crate::build::coverageinfo::mcdc::MCDCInfoBuilder;
use crate::build::{Builder, CFG}; use crate::build::{Builder, CFG};
mod mcdc;
pub(crate) struct BranchInfoBuilder { pub(crate) struct BranchInfoBuilder {
/// Maps condition expressions to their enclosing `!`, for better instrumentation. /// Maps condition expressions to their enclosing `!`, for better instrumentation.
nots: FxHashMap<ExprId, NotInfo>, nots: FxHashMap<ExprId, NotInfo>,
@@ -155,7 +156,7 @@ impl BranchInfoBuilder {
} }
} }
impl Builder<'_, '_> { impl<'tcx> Builder<'_, 'tcx> {
/// If branch coverage is enabled, inject marker statements into `then_block` /// If branch coverage is enabled, inject marker statements into `then_block`
/// and `else_block`, and record their IDs in the table of branch spans. /// and `else_block`, and record their IDs in the table of branch spans.
pub(crate) fn visit_coverage_branch_condition( pub(crate) fn visit_coverage_branch_condition(
@@ -195,4 +196,23 @@ impl Builder<'_, '_> {
branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block); branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
} }
/// If branch coverage is enabled, inject marker statements into `true_block`
/// and `false_block`, and record their IDs in the table of branches.
///
/// Used to instrument let-else and if-let (including let-chains) for branch coverage.
pub(crate) fn visit_coverage_conditional_let(
&mut self,
pattern: &Pat<'tcx>, // Pattern that has been matched when the true path is taken
true_block: BasicBlock,
false_block: BasicBlock,
) {
// Bail out if branch coverage is not enabled for this function.
let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
// FIXME(#124144) This may need special handling when MC/DC is enabled.
let source_info = SourceInfo { span: pattern.span, scope: self.source_scope };
branch_info.add_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
}
} }

View File

@@ -2000,6 +2000,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
false, false,
); );
// If branch coverage is enabled, record this branch.
self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_post_guard_block);
post_guard_block.unit() post_guard_block.unit()
} }
@@ -2492,6 +2495,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None, None,
true, true,
); );
// If branch coverage is enabled, record this branch.
this.visit_coverage_conditional_let(pattern, matching, failure);
this.break_for_else(failure, this.source_info(initializer_span)); this.break_for_else(failure, this.source_info(initializer_span));
matching.unit() matching.unit()
}); });

View File

@@ -1,13 +1,16 @@
Function name: if_let::if_let Function name: if_let::if_let
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] Raw bytes (45): 0x[01, 01, 02, 05, 09, 09, 02, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 0c, 00, 13, 02, 00, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 07, 03, 05, 01, 02]
Number of files: 1 Number of files: 1
- file 0 => global file 1 - file 0 => global file 1
Number of expressions: 2 Number of expressions: 2
- expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 0 operands: lhs = Counter(1), rhs = Counter(2)
- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) - expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
Number of file 0 mappings: 6 Number of file 0 mappings: 7
- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 18) - Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 3, 12) to (start + 0, 19)
true = (c1 - c2)
false = c2
- Code(Expression(0, Sub)) at (prev + 0, 17) to (start + 0, 18)
= (c1 - c2) = (c1 - c2)
- Code(Counter(1)) at (prev + 0, 22) to (start + 0, 27) - Code(Counter(1)) at (prev + 0, 22) to (start + 0, 27)
- Code(Expression(0, Sub)) at (prev + 0, 28) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 0, 28) to (start + 2, 6)
@@ -17,7 +20,7 @@ Number of file 0 mappings: 6
= (c2 + (c1 - c2)) = (c2 + (c1 - c2))
Function name: if_let::if_let_chain Function name: if_let::if_let_chain
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] Raw bytes (66): 0x[01, 01, 04, 01, 05, 05, 09, 0f, 0d, 05, 09, 0a, 01, 17, 01, 00, 33, 20, 02, 05, 01, 0c, 00, 13, 02, 00, 11, 00, 12, 01, 00, 16, 00, 17, 20, 0d, 09, 01, 10, 00, 17, 0d, 00, 15, 00, 16, 02, 00, 1a, 00, 1b, 0d, 01, 05, 03, 06, 0f, 03, 0c, 02, 06, 0b, 03, 05, 01, 02]
Number of files: 1 Number of files: 1
- file 0 => global file 1 - file 0 => global file 1
Number of expressions: 4 Number of expressions: 4
@@ -25,12 +28,18 @@ Number of expressions: 4
- expression 1 operands: lhs = Counter(1), rhs = Counter(2) - expression 1 operands: lhs = Counter(1), rhs = Counter(2)
- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3) - expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3)
- expression 3 operands: lhs = Counter(1), rhs = Counter(2) - expression 3 operands: lhs = Counter(1), rhs = Counter(2)
Number of file 0 mappings: 8 Number of file 0 mappings: 10
- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 51) - Code(Counter(0)) at (prev + 23, 1) to (start + 0, 51)
- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 18) - Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 1, 12) to (start + 0, 19)
true = (c0 - c1)
false = c1
- Code(Expression(0, Sub)) at (prev + 0, 17) to (start + 0, 18)
= (c0 - c1) = (c0 - c1)
- Code(Counter(0)) at (prev + 0, 22) to (start + 0, 23) - Code(Counter(0)) at (prev + 0, 22) to (start + 0, 23)
- Code(Counter(3)) at (prev + 1, 21) to (start + 0, 22) - Branch { true: Counter(3), false: Counter(2) } at (prev + 1, 16) to (start + 0, 23)
true = c3
false = c2
- Code(Counter(3)) at (prev + 0, 21) to (start + 0, 22)
- Code(Expression(0, Sub)) at (prev + 0, 26) to (start + 0, 27) - Code(Expression(0, Sub)) at (prev + 0, 26) to (start + 0, 27)
= (c0 - c1) = (c0 - c1)
- Code(Counter(3)) at (prev + 1, 5) to (start + 3, 6) - Code(Counter(3)) at (prev + 1, 5) to (start + 3, 6)

View File

@@ -14,6 +14,9 @@
LL| | LL| |
LL| 3| if let Some(x) = input { LL| 3| if let Some(x) = input {
^2 ^2
------------------
| Branch (LL:12): [True: 2, False: 1]
------------------
LL| 2| say(x); LL| 2| say(x);
LL| 2| } else { LL| 2| } else {
LL| 1| say("none"); LL| 1| say("none");
@@ -24,8 +27,14 @@
LL| 15|fn if_let_chain(a: Option<&str>, b: Option<&str>) { LL| 15|fn if_let_chain(a: Option<&str>, b: Option<&str>) {
LL| 15| if let Some(x) = a LL| 15| if let Some(x) = a
^12 ^12
------------------
| Branch (LL:12): [True: 12, False: 3]
------------------
LL| 12| && let Some(y) = b LL| 12| && let Some(y) = b
^8 ^8
------------------
| Branch (LL:16): [True: 8, False: 4]
------------------
LL| 8| { LL| 8| {
LL| 8| say(x); LL| 8| say(x);
LL| 8| say(y); LL| 8| say(y);

View File

@@ -1,13 +1,16 @@
Function name: let_else::let_else Function name: let_else::let_else
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] Raw bytes (45): 0x[01, 01, 02, 05, 09, 09, 02, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 09, 00, 10, 02, 00, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 07, 01, 01, 00, 02]
Number of files: 1 Number of files: 1
- file 0 => global file 1 - file 0 => global file 1
Number of expressions: 2 Number of expressions: 2
- expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 0 operands: lhs = Counter(1), rhs = Counter(2)
- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) - expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
Number of file 0 mappings: 6 Number of file 0 mappings: 7
- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
- Code(Expression(0, Sub)) at (prev + 3, 14) to (start + 0, 15) - Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 3, 9) to (start + 0, 16)
true = (c1 - c2)
false = c2
- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 15)
= (c1 - c2) = (c1 - c2)
- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24) - Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24)
- Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15) - Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15)

View File

@@ -14,6 +14,9 @@
LL| | LL| |
LL| 3| let Some(x) = value else { LL| 3| let Some(x) = value else {
^2 ^2
------------------
| Branch (LL:9): [True: 2, False: 1]
------------------
LL| 1| say("none"); LL| 1| say("none");
LL| 1| return; LL| 1| return;
LL| | }; LL| | };