11use std:: assert_matches:: assert_matches;
22use std:: collections:: hash_map:: Entry ;
3- use std:: collections:: VecDeque ;
3+ use std:: collections:: { BTreeMap , VecDeque } ;
44
55use rustc_data_structures:: fx:: FxHashMap ;
66use rustc_middle:: mir:: coverage:: {
7- BlockMarkerId , BranchSpan , ConditionId , ConditionInfo , CoverageKind , MCDCBranchSpan ,
8- MCDCDecisionSpan ,
7+ BlockMarkerId , BranchSpan , ConditionId , ConditionInfo , CoverageKind , MCDCBranchInfo ,
8+ MCDCBranchSpan , MCDCDecisionSpan , MCDCMatchArmSpan ,
99} ;
1010use rustc_middle:: mir:: { self , BasicBlock , SourceInfo , UnOp } ;
1111use rustc_middle:: thir:: { ExprId , ExprKind , LogicalOp , Thir } ;
@@ -23,9 +23,7 @@ pub(crate) struct BranchInfoBuilder {
2323 num_block_markers : usize ,
2424 branch_spans : Vec < BranchSpan > ,
2525
26- mcdc_branch_spans : Vec < MCDCBranchSpan > ,
27- mcdc_decision_spans : Vec < MCDCDecisionSpan > ,
28- mcdc_state : Option < MCDCState > ,
26+ mcdc_builder : Option < MCDCBuilder > ,
2927}
3028
3129#[ derive( Clone , Copy ) ]
@@ -47,9 +45,7 @@ impl BranchInfoBuilder {
4745 nots : FxHashMap :: default ( ) ,
4846 num_block_markers : 0 ,
4947 branch_spans : vec ! [ ] ,
50- mcdc_branch_spans : vec ! [ ] ,
51- mcdc_decision_spans : vec ! [ ] ,
52- mcdc_state : MCDCState :: new_if_enabled ( tcx) ,
48+ mcdc_builder : MCDCBuilder :: new_if_enabled ( tcx) ,
5349 } )
5450 } else {
5551 None
@@ -102,21 +98,21 @@ impl BranchInfoBuilder {
10298 true_marker : BlockMarkerId ,
10399 false_marker : BlockMarkerId ,
104100 ) -> Option < ConditionInfo > {
105- let mcdc_state = self . mcdc_state . as_mut ( ) ?;
101+ let mcdc_builder = self . mcdc_builder . as_mut ( ) ?;
106102 let ( mut condition_info, decision_result) =
107- mcdc_state . take_condition ( true_marker, false_marker) ;
103+ mcdc_builder . take_condition ( true_marker, false_marker) ;
108104 if let Some ( decision) = decision_result {
109105 match decision. conditions_num {
110106 0 => {
111107 unreachable ! ( "Decision with no condition is not expected" ) ;
112108 }
113109 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
114- self . mcdc_decision_spans . push ( decision) ;
110+ mcdc_builder . decision_spans . push ( decision) ;
115111 }
116112 _ => {
117113 // Do not generate mcdc mappings and statements for decisions with too many conditions.
118- let rebase_idx = self . mcdc_branch_spans . len ( ) - decision. conditions_num + 1 ;
119- for branch in & mut self . mcdc_branch_spans [ rebase_idx..] {
114+ let rebase_idx = mcdc_builder . branch_spans . len ( ) - decision. conditions_num + 1 ;
115+ for branch in & mut mcdc_builder . branch_spans [ rebase_idx..] {
120116 branch. condition_info = None ;
121117 }
122118
@@ -171,14 +167,7 @@ impl BranchInfoBuilder {
171167 }
172168
173169 pub ( crate ) fn into_done ( self ) -> Option < Box < mir:: coverage:: BranchInfo > > {
174- let Self {
175- nots : _,
176- num_block_markers,
177- branch_spans,
178- mcdc_branch_spans,
179- mcdc_decision_spans,
180- mcdc_state : _,
181- } = self ;
170+ let Self { nots : _, num_block_markers, branch_spans, mcdc_builder } = self ;
182171
183172 if num_block_markers == 0 {
184173 assert ! ( branch_spans. is_empty( ) ) ;
@@ -188,8 +177,7 @@ impl BranchInfoBuilder {
188177 Some ( Box :: new ( mir:: coverage:: BranchInfo {
189178 num_block_markers,
190179 branch_spans,
191- mcdc_branch_spans,
192- mcdc_decision_spans,
180+ mcdc_info : mcdc_builder. map ( MCDCBuilder :: into_done) ,
193181 } ) )
194182 }
195183}
@@ -199,17 +187,25 @@ impl BranchInfoBuilder {
199187/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
200188const MAX_CONDITIONS_NUM_IN_DECISION : usize = 6 ;
201189
202- struct MCDCState {
190+ struct MCDCBuilder {
203191 /// To construct condition evaluation tree.
204192 decision_stack : VecDeque < ConditionInfo > ,
205193 processing_decision : Option < MCDCDecisionSpan > ,
194+
195+ match_arm_records : BTreeMap < Span , MCDCMatchArmSpan > ,
196+ branch_spans : Vec < MCDCBranchSpan > ,
197+ decision_spans : Vec < MCDCDecisionSpan > ,
206198}
207199
208- impl MCDCState {
200+ impl MCDCBuilder {
209201 fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
210- tcx. sess
211- . instrument_coverage_mcdc ( )
212- . then ( || Self { decision_stack : VecDeque :: new ( ) , processing_decision : None } )
202+ tcx. sess . instrument_coverage_mcdc ( ) . then ( || Self {
203+ decision_stack : VecDeque :: new ( ) ,
204+ processing_decision : None ,
205+ match_arm_records : BTreeMap :: new ( ) ,
206+ branch_spans : vec ! [ ] ,
207+ decision_spans : vec ! [ ] ,
208+ } )
213209 }
214210
215211 // At first we assign ConditionIds for each sub expression.
@@ -333,6 +329,23 @@ impl MCDCState {
333329 ( Some ( condition_info) , None )
334330 }
335331 }
332+
333+ fn into_done ( self ) -> MCDCBranchInfo {
334+ let Self { match_arm_records, branch_spans, decision_spans, .. } = self ;
335+
336+ let mut match_arm_spans = match_arm_records. into_values ( ) . collect :: < Vec < _ > > ( ) ;
337+ match_arm_spans. sort_by ( |a, b| {
338+ if a. span . contains ( b. span ) { std:: cmp:: Ordering :: Less } else { a. span . cmp ( & b. span ) }
339+ } ) ;
340+ for idx in 0 ..match_arm_spans. len ( ) . saturating_sub ( 1 ) {
341+ if match_arm_spans[ idx] . span . contains ( match_arm_spans[ idx + 1 ] . span ) {
342+ let refined_span = match_arm_spans[ idx] . span . until ( match_arm_spans[ idx + 1 ] . span ) ;
343+ match_arm_spans[ idx] . span = refined_span;
344+ }
345+ }
346+
347+ MCDCBranchInfo { branch_spans, match_arm_spans, decision_spans }
348+ }
336349}
337350
338351impl Builder < ' _ , ' _ > {
@@ -359,19 +372,21 @@ impl Builder<'_, '_> {
359372 let source_info = SourceInfo { span : self . thir [ expr_id] . span , scope : self . source_scope } ;
360373
361374 // Separate path for handling branches when MC/DC is enabled.
362- if branch_info. mcdc_state . is_some ( ) {
375+ if branch_info. mcdc_builder . is_some ( ) {
363376 let mut inject_block_marker =
364377 |block| branch_info. inject_block_marker ( & mut self . cfg , source_info, block) ;
365378 let true_marker = inject_block_marker ( then_block) ;
366379 let false_marker = inject_block_marker ( else_block) ;
367380 let condition_info =
368381 branch_info. fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker) ;
369- branch_info. mcdc_branch_spans . push ( MCDCBranchSpan {
370- span : source_info. span ,
371- condition_info,
372- true_marker,
373- false_marker,
374- } ) ;
382+ branch_info. mcdc_builder . as_mut ( ) . expect ( "checked at condition" ) . branch_spans . push (
383+ MCDCBranchSpan {
384+ span : source_info. span ,
385+ condition_info,
386+ true_marker,
387+ false_marker,
388+ } ,
389+ ) ;
375390 return ;
376391 }
377392
@@ -380,9 +395,54 @@ impl Builder<'_, '_> {
380395
381396 pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
382397 if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
383- && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
398+ && let Some ( mcdc_state) = branch_info. mcdc_builder . as_mut ( )
384399 {
385400 mcdc_state. record_conditions ( logical_op, span) ;
386401 }
387402 }
403+
404+ pub ( crate ) fn visit_coverage_mcdc_match_arms (
405+ & mut self ,
406+ ( test_span, test_block) : ( Span , BasicBlock ) ,
407+ targets : impl Iterator < Item = ( Span , BasicBlock ) > ,
408+ ) {
409+ let Some ( branch_info) = self . coverage_branch_info . as_mut ( ) else { return } ;
410+ // Only enabled in mcdc
411+ if branch_info. mcdc_builder . is_none ( ) {
412+ return ;
413+ }
414+
415+ // May inject redundant block markers, it's ok because we will transform them into basic coverage block in coverage pass.
416+ let mut gen_marker_id = |span, block| {
417+ branch_info. inject_block_marker (
418+ & mut self . cfg ,
419+ SourceInfo { span, scope : self . source_scope } ,
420+ block,
421+ )
422+ } ;
423+
424+ let test_block_id = gen_marker_id ( test_span, test_block) ;
425+
426+ let mut targets =
427+ targets. map ( |( span, block) | ( span, gen_marker_id ( span, block) ) ) . collect :: < Vec < _ > > ( ) ;
428+
429+ let branch_records =
430+ & mut branch_info. mcdc_builder . as_mut ( ) . expect ( "checked at entry" ) . match_arm_records ;
431+
432+ while let Some ( ( span, matched_block) ) = targets. pop ( ) {
433+ // By here we do not know the span of the target yet, so just record the
434+ // basic blocks and insert BlockMarkerId later.
435+ let record = branch_records. entry ( span) . or_insert_with ( || MCDCMatchArmSpan {
436+ span,
437+ condition_info : None ,
438+ test_markers : vec ! [ ] ,
439+ true_markers : vec ! [ ] ,
440+ prior_binding_markers : vec ! [ ] ,
441+ } ) ;
442+ record. test_markers . push ( test_block_id) ;
443+ record. true_markers . push ( matched_block) ;
444+
445+ record. prior_binding_markers . extend ( targets. iter ( ) . map ( |( _, blk) | * blk) ) ;
446+ }
447+ }
388448}
0 commit comments