diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index e2a5f97a84744..ee9eeb62990d2 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -1,17 +1,18 @@ -mod mcdc; use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; 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_span::def_id::LocalDefId; use crate::build::coverageinfo::mcdc::MCDCInfoBuilder; use crate::build::{Builder, CFG}; +mod mcdc; + pub(crate) struct BranchInfoBuilder { /// Maps condition expressions to their enclosing `!`, for better instrumentation. nots: FxHashMap, @@ -155,7 +156,7 @@ impl BranchInfoBuilder { } } -impl Builder<'_, '_> { +impl<'tcx> Builder<'_, 'tcx> { /// If branch coverage is enabled, inject marker statements into `then_block` /// and `else_block`, and record their IDs in the table of branch spans. 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); } + + /// 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); + } } diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index bce1526775963..c8c2263f2ed3e 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1968,6 +1968,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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() } @@ -2452,6 +2455,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None, 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)); matching.unit() }); diff --git a/tests/coverage/branch/if-let.cov-map b/tests/coverage/branch/if-let.cov-map index c12df8d98018d..0c7d986933e7d 100644 --- a/tests/coverage/branch/if-let.cov-map +++ b/tests/coverage/branch/if-let.cov-map @@ -1,13 +1,16 @@ 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 - file 0 => global file 1 Number of expressions: 2 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - 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(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) - Code(Counter(1)) at (prev + 0, 22) to (start + 0, 27) - 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)) 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 - file 0 => global file 1 Number of expressions: 4 @@ -25,12 +28,18 @@ Number of expressions: 4 - expression 1 operands: lhs = Counter(1), 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 file 0 mappings: 10 - 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) - 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) = (c0 - c1) - Code(Counter(3)) at (prev + 1, 5) to (start + 3, 6) diff --git a/tests/coverage/branch/if-let.coverage b/tests/coverage/branch/if-let.coverage index f30c5d34eca17..9a3f0113f7515 100644 --- a/tests/coverage/branch/if-let.coverage +++ b/tests/coverage/branch/if-let.coverage @@ -14,6 +14,9 @@ LL| | LL| 3| if let Some(x) = input { ^2 + ------------------ + | Branch (LL:12): [True: 2, False: 1] + ------------------ LL| 2| say(x); LL| 2| } else { LL| 1| say("none"); @@ -24,8 +27,14 @@ LL| 15|fn if_let_chain(a: Option<&str>, b: Option<&str>) { LL| 15| if let Some(x) = a ^12 + ------------------ + | Branch (LL:12): [True: 12, False: 3] + ------------------ LL| 12| && let Some(y) = b ^8 + ------------------ + | Branch (LL:16): [True: 8, False: 4] + ------------------ LL| 8| { LL| 8| say(x); LL| 8| say(y); diff --git a/tests/coverage/branch/let-else.cov-map b/tests/coverage/branch/let-else.cov-map index ad987bd6bb1e9..c7f7adddbc295 100644 --- a/tests/coverage/branch/let-else.cov-map +++ b/tests/coverage/branch/let-else.cov-map @@ -1,13 +1,16 @@ 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 - file 0 => global file 1 Number of expressions: 2 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - 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(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) - Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24) - Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15) diff --git a/tests/coverage/branch/let-else.coverage b/tests/coverage/branch/let-else.coverage index 83730e1dfbafe..22ad8f2b0e138 100644 --- a/tests/coverage/branch/let-else.coverage +++ b/tests/coverage/branch/let-else.coverage @@ -14,6 +14,9 @@ LL| | LL| 3| let Some(x) = value else { ^2 + ------------------ + | Branch (LL:9): [True: 2, False: 1] + ------------------ LL| 1| say("none"); LL| 1| return; LL| | };