@@ -2,11 +2,13 @@ use std::assert_matches::assert_matches;
22use std:: collections:: hash_map:: Entry ;
33
44use rustc_data_structures:: fx:: FxHashMap ;
5- use rustc_middle:: mir:: coverage:: { BlockMarkerId , BranchSpan , CoverageKind } ;
5+ use rustc_index:: IndexVec ;
6+ use rustc_middle:: mir:: coverage:: { BlockMarkerId , BranchSpan , CoverageKind , DecisionMarkerId } ;
67use rustc_middle:: mir:: { self , BasicBlock , UnOp } ;
78use rustc_middle:: thir:: { ExprId , ExprKind , Thir } ;
89use rustc_middle:: ty:: TyCtxt ;
910use rustc_span:: def_id:: LocalDefId ;
11+ use rustc_span:: Span ;
1012
1113use crate :: build:: Builder ;
1214
@@ -16,6 +18,7 @@ pub(crate) struct BranchInfoBuilder {
1618
1719 num_block_markers : usize ,
1820 branch_spans : Vec < BranchSpan > ,
21+ decisions : IndexVec < DecisionMarkerId , Span > ,
1922}
2023
2124#[ derive( Clone , Copy ) ]
@@ -32,8 +35,15 @@ impl BranchInfoBuilder {
3235 /// Creates a new branch info builder, but only if branch coverage instrumentation
3336 /// is enabled and `def_id` represents a function that is eligible for coverage.
3437 pub ( crate ) fn new_if_enabled ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> Option < Self > {
35- if tcx. sess . instrument_coverage_branch ( ) && tcx. is_eligible_for_coverage ( def_id) {
36- Some ( Self { nots : FxHashMap :: default ( ) , num_block_markers : 0 , branch_spans : vec ! [ ] } )
38+ if ( tcx. sess . instrument_coverage_branch ( ) || tcx. sess . instrument_coverage_mcdc ( ) )
39+ && tcx. is_eligible_for_coverage ( def_id)
40+ {
41+ Some ( Self {
42+ nots : FxHashMap :: default ( ) ,
43+ num_block_markers : 0 ,
44+ branch_spans : vec ! [ ] ,
45+ decisions : IndexVec :: new ( ) ,
46+ } )
3747 } else {
3848 None
3949 }
@@ -86,14 +96,14 @@ impl BranchInfoBuilder {
8696 }
8797
8898 pub ( crate ) fn into_done ( self ) -> Option < Box < mir:: coverage:: BranchInfo > > {
89- let Self { nots : _, num_block_markers, branch_spans } = self ;
99+ let Self { nots : _, num_block_markers, branch_spans, decisions } = self ;
90100
91101 if num_block_markers == 0 {
92102 assert ! ( branch_spans. is_empty( ) ) ;
93103 return None ;
94104 }
95105
96- Some ( Box :: new ( mir:: coverage:: BranchInfo { num_block_markers, branch_spans } ) )
106+ Some ( Box :: new ( mir:: coverage:: BranchInfo { num_block_markers, branch_spans, decisions } ) )
97107 }
98108}
99109
@@ -105,6 +115,7 @@ impl Builder<'_, '_> {
105115 mut expr_id : ExprId ,
106116 mut then_block : BasicBlock ,
107117 mut else_block : BasicBlock ,
118+ decision_id : Option < DecisionMarkerId > , // If MCDC is enabled
108119 ) {
109120 // Bail out if branch coverage is not enabled for this function.
110121 let Some ( branch_info) = self . coverage_branch_info . as_ref ( ) else { return } ;
@@ -125,9 +136,15 @@ impl Builder<'_, '_> {
125136 let mut inject_branch_marker = |block : BasicBlock | {
126137 let id = branch_info. next_block_marker_id ( ) ;
127138
139+ let cov_kind = if let Some ( decision_id) = decision_id {
140+ CoverageKind :: MCDCBlockMarker { id, decision_id }
141+ } else {
142+ CoverageKind :: BlockMarker { id }
143+ } ;
144+
128145 let marker_statement = mir:: Statement {
129146 source_info,
130- kind : mir:: StatementKind :: Coverage ( CoverageKind :: BlockMarker { id } ) ,
147+ kind : mir:: StatementKind :: Coverage ( cov_kind ) ,
131148 } ;
132149 self . cfg . push ( block, marker_statement) ;
133150
@@ -139,8 +156,41 @@ impl Builder<'_, '_> {
139156
140157 branch_info. branch_spans . push ( BranchSpan {
141158 span : source_info. span ,
159+ // FIXME: Handle case when MCDC is disabled better than just putting 0.
160+ decision_id : decision_id. unwrap_or ( DecisionMarkerId :: from_u32 ( 0 ) ) ,
142161 true_marker,
143162 false_marker,
144163 } ) ;
145164 }
165+
166+ /// If MCDC coverage is enabled, inject a marker in all the decisions
167+ /// (boolean expressions)
168+ pub ( crate ) fn visit_coverage_decision (
169+ & mut self ,
170+ expr_id : ExprId ,
171+ block : BasicBlock ,
172+ ) -> Option < DecisionMarkerId > {
173+ // Early return if MCDC coverage is not enabled.
174+ if !self . tcx . sess . instrument_coverage_mcdc ( ) {
175+ return None ;
176+ }
177+ let Some ( branch_info) = self . coverage_branch_info . as_mut ( ) else {
178+ return None ;
179+ } ;
180+
181+ let span = self . thir [ expr_id] . span ;
182+ let decision_id = branch_info. decisions . push ( span) ;
183+
184+ // Inject a decision marker
185+ let source_info = self . source_info ( span) ;
186+ let marker_statement = mir:: Statement {
187+ source_info,
188+ kind : mir:: StatementKind :: Coverage (
189+ CoverageKind :: MCDCDecisionMarker { id : decision_id } ,
190+ ) ,
191+ } ;
192+ self . cfg . push ( block, marker_statement) ;
193+
194+ Some ( decision_id)
195+ }
146196}
0 commit comments