1- use std:: collections:: { BTreeMap , BTreeSet , VecDeque } ;
1+ use std:: collections:: { BTreeMap , VecDeque } ;
22
33use rustc_middle:: mir:: coverage:: {
44 BlockMarkerId , ConditionId , ConditionInfo , MCDCBranchSpan , MCDCDecisionSpan ,
@@ -198,7 +198,6 @@ impl PatternDecisionCtx {
198198 let entry_span = targets. first ( ) . expect ( "targets mut be non empty" ) . 0 ;
199199
200200 let mut false_next_arm = None ;
201-
202201 // Tranverse conditions in reverse order.
203202 while let Some ( ( span, matched_block) ) = targets. pop ( ) {
204203 // LLVM whether the decision dominates the condition by checking if span of the decision cover that of the condition,
@@ -246,16 +245,22 @@ impl PatternDecisionCtx {
246245 // to the unique test block of `IpAddr::V4(_) | IpAddr::V6(_)`. Thus the test block cannot find its direct
247246 // predecessors, which are not matched blocks of any patterns, at `visit_matching_arms`
248247 // (in fact they were not connected at that moment).
249- fn link_condition_gaps ( & mut self , cfg : & mut CFG < ' _ > ) {
248+ fn link_condition_gaps ( & mut self , entry_block : BasicBlock , cfg : & mut CFG < ' _ > ) {
250249 if self . start_tests . len ( ) < 2 {
251250 return ;
252251 }
253- let mut linked_starts = BTreeSet :: new ( ) ;
252+
254253 for ( & end, span) in & self . end_tests {
255254 let mut successor = Some ( end) ;
256255 while let Some ( block) = successor. take ( ) {
257- let next_blocks =
258- cfg. block_data ( block) . terminator ( ) . successors ( ) . collect :: < Vec < _ > > ( ) ;
256+ let Some ( next_blocks) = cfg
257+ . block_data ( block)
258+ . terminator
259+ . as_ref ( )
260+ . map ( |term| term. successors ( ) . collect :: < Vec < _ > > ( ) )
261+ else {
262+ break ;
263+ } ;
259264 match next_blocks. as_slice ( ) {
260265 & [ unique_successor] => {
261266 if let Some ( & next_span) = self . start_tests . get ( & unique_successor) {
@@ -264,7 +269,6 @@ impl PatternDecisionCtx {
264269 . get_mut ( span)
265270 . expect ( "end tests must be recorded" ) ;
266271 end_record. true_next_pattern = Some ( next_span) ;
267- linked_starts. insert ( unique_successor) ;
268272 } else {
269273 successor = Some ( unique_successor) ;
270274 }
@@ -274,19 +278,29 @@ impl PatternDecisionCtx {
274278 }
275279 }
276280
277- self . start_tests . retain ( |block, _| !linked_starts. contains ( block) ) ;
278-
281+ // There might be unreached arms in match guard. E.g
282+ // ```rust
283+ // match pattern {
284+ // (A | B, C | D) => {},
285+ // (B, D) => {},
286+ // _ => {}
287+ // }
288+ // ```
289+ // Clearly the arm `(B, D)` is covered by the first arm so it should be ureached.
290+ // Such arms can cause unresolved start tests here, just ignore them.
291+ self . start_tests . retain ( |block, _| * block == entry_block) ;
292+ // In case no block in `start_tests` is `entry_block`.
279293 assert_eq ! ( self . start_tests. len( ) , 1 , "still some gaps exist in mcdc pattern decision" ) ;
280294 }
281295
282296 fn finish (
283297 mut self ,
284298 cfg : & mut CFG < ' _ > ,
285- matching_block : BasicBlock ,
286- failure_block : BasicBlock ,
299+ entry_block : BasicBlock ,
300+ end_blocks : Option < ( BasicBlock , BasicBlock ) > ,
287301 mut inject_block_marker : impl FnMut ( & mut CFG < ' _ > , Span , BasicBlock ) -> BlockMarkerId ,
288- ) -> ( MCDCDecisionSpan , Vec < MCDCBranchSpan > ) {
289- self . link_condition_gaps ( cfg) ;
302+ ) -> ( Option < MCDCDecisionSpan > , Vec < MCDCBranchSpan > ) {
303+ self . link_condition_gaps ( entry_block , cfg) ;
290304
291305 let Self { decision_span, decision_depth, mut matching_arms, start_tests, end_tests : _ } =
292306 self ;
@@ -295,13 +309,6 @@ impl PatternDecisionCtx {
295309 blocks. into_iter ( ) . map ( |& block| inject_block_marker ( cfg, span, block) ) . collect ( )
296310 } ;
297311
298- let decision = MCDCDecisionSpan {
299- span : decision_span,
300- decision_depth,
301- conditions_num : matching_arms. len ( ) ,
302- end_markers : into_block_markers ( decision_span, & [ matching_block, failure_block] ) ,
303- } ;
304-
305312 let mut branch_spans = vec ! [ ] ;
306313
307314 // LLVM implementation requires the entry condition should be assigned id 1.
@@ -357,7 +364,7 @@ impl PatternDecisionCtx {
357364 true_markers : into_block_markers ( span, & matched_blocks) ,
358365 } ) ;
359366 }
360- assert_eq ! ( branch_spans . len ( ) , decision . conditions_num , "conditions num is not correct" ) ;
367+
361368 branch_spans. sort_by ( |lhs, rhs| {
362369 if lhs. span . contains ( rhs. span ) {
363370 std:: cmp:: Ordering :: Less
@@ -381,6 +388,15 @@ impl PatternDecisionCtx {
381388 branch_spans[ idx] . span = span;
382389 }
383390
391+ // No `end_blocks` means it's irrefutable pattern matching. In such cases do not generate mcdc info because the decision
392+ // never fails. Instead we generate normal branch coverage for it.
393+ let decision = end_blocks. map ( |( matching, failure) | MCDCDecisionSpan {
394+ span : decision_span,
395+ decision_depth,
396+ conditions_num : branch_spans. len ( ) ,
397+ end_markers : into_block_markers ( decision_span, & [ matching, failure] ) ,
398+ } ) ;
399+
384400 ( decision, branch_spans)
385401 }
386402}
@@ -481,8 +497,8 @@ impl MCDCInfoBuilder {
481497 & mut self ,
482498 cfg : & mut CFG < ' _ > ,
483499 tcx : TyCtxt < ' _ > ,
484- matching_block : BasicBlock ,
485- failure_block : BasicBlock ,
500+ entry_block : BasicBlock ,
501+ end_blocks : Option < ( BasicBlock , BasicBlock ) > ,
486502 inject_block_marker : impl FnMut ( & mut CFG < ' _ > , Span , BasicBlock ) -> BlockMarkerId ,
487503 ) {
488504 if !self
@@ -497,15 +513,24 @@ impl MCDCInfoBuilder {
497513 } ;
498514
499515 let ( decision, conditions) =
500- decision_ctx. finish ( cfg, matching_block, failure_block, inject_block_marker) ;
501- self . append_mcdc_info ( tcx, decision, conditions) ;
516+ decision_ctx. finish ( cfg, entry_block, end_blocks, inject_block_marker) ;
517+ if let Some ( decision) = decision {
518+ self . append_mcdc_info ( tcx, decision, conditions) ;
519+ } else {
520+ self . append_normal_branches ( conditions) ;
521+ }
522+ }
523+
524+ fn append_normal_branches ( & mut self , mut conditions : Vec < MCDCBranchSpan > ) {
525+ conditions. iter_mut ( ) . for_each ( |branch| branch. condition_info = None ) ;
526+ self . branch_spans . extend ( conditions) ;
502527 }
503528
504529 fn append_mcdc_info (
505530 & mut self ,
506531 tcx : TyCtxt < ' _ > ,
507532 decision : MCDCDecisionSpan ,
508- mut conditions : Vec < MCDCBranchSpan > ,
533+ conditions : Vec < MCDCBranchSpan > ,
509534 ) {
510535 // take_condition() returns Some for decision_result when the decision stack
511536 // is empty, i.e. when all the conditions of the decision were instrumented,
@@ -521,9 +546,7 @@ impl MCDCInfoBuilder {
521546 // MCDC is equivalent to normal branch coverage if number of conditions is less than 1, so ignore these decisions.
522547 // See comment of `MAX_CONDITIONS_NUM_IN_DECISION` for why decisions with oversized conditions are ignored.
523548 _ => {
524- // Generate normal branch coverage mappings in such cases.
525- conditions. iter_mut ( ) . for_each ( |branch| branch. condition_info = None ) ;
526- self . branch_spans . extend ( conditions) ;
549+ self . append_normal_branches ( conditions) ;
527550 if decision. conditions_num > MAX_CONDITIONS_NUM_IN_DECISION {
528551 tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
529552 span : decision. span ,
@@ -590,8 +613,8 @@ impl Builder<'_, '_> {
590613
591614 pub ( crate ) fn mcdc_finish_pattern_matching_decision (
592615 & mut self ,
593- matching_block : BasicBlock ,
594- failure_block : BasicBlock ,
616+ entry_block : BasicBlock ,
617+ end_blocks : Option < ( BasicBlock , BasicBlock ) > ,
595618 ) {
596619 if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
597620 && let Some ( mcdc_info) = branch_info. mcdc_info . as_mut ( )
@@ -606,8 +629,8 @@ impl Builder<'_, '_> {
606629 mcdc_info. finish_pattern_decision (
607630 & mut self . cfg ,
608631 self . tcx ,
609- matching_block ,
610- failure_block ,
632+ entry_block ,
633+ end_blocks ,
611634 inject_block_marker,
612635 ) ;
613636 }
0 commit comments