1
- use std:: collections:: { BTreeMap , BTreeSet , VecDeque } ;
1
+ use std:: collections:: { BTreeMap , VecDeque } ;
2
2
3
3
use rustc_middle:: mir:: coverage:: {
4
4
BlockMarkerId , ConditionId , ConditionInfo , MCDCBranchSpan , MCDCDecisionSpan ,
@@ -198,7 +198,6 @@ impl PatternDecisionCtx {
198
198
let entry_span = targets. first ( ) . expect ( "targets mut be non empty" ) . 0 ;
199
199
200
200
let mut false_next_arm = None ;
201
-
202
201
// Tranverse conditions in reverse order.
203
202
while let Some ( ( span, matched_block) ) = targets. pop ( ) {
204
203
// 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 {
246
245
// to the unique test block of `IpAddr::V4(_) | IpAddr::V6(_)`. Thus the test block cannot find its direct
247
246
// predecessors, which are not matched blocks of any patterns, at `visit_matching_arms`
248
247
// (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 < ' _ > ) {
250
249
if self . start_tests . len ( ) < 2 {
251
250
return ;
252
251
}
253
- let mut linked_starts = BTreeSet :: new ( ) ;
252
+
254
253
for ( & end, span) in & self . end_tests {
255
254
let mut successor = Some ( end) ;
256
255
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
+ } ;
259
264
match next_blocks. as_slice ( ) {
260
265
& [ unique_successor] => {
261
266
if let Some ( & next_span) = self . start_tests . get ( & unique_successor) {
@@ -264,7 +269,6 @@ impl PatternDecisionCtx {
264
269
. get_mut ( span)
265
270
. expect ( "end tests must be recorded" ) ;
266
271
end_record. true_next_pattern = Some ( next_span) ;
267
- linked_starts. insert ( unique_successor) ;
268
272
} else {
269
273
successor = Some ( unique_successor) ;
270
274
}
@@ -274,19 +278,29 @@ impl PatternDecisionCtx {
274
278
}
275
279
}
276
280
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`.
279
293
assert_eq ! ( self . start_tests. len( ) , 1 , "still some gaps exist in mcdc pattern decision" ) ;
280
294
}
281
295
282
296
fn finish (
283
297
mut self ,
284
298
cfg : & mut CFG < ' _ > ,
285
- matching_block : BasicBlock ,
286
- failure_block : BasicBlock ,
299
+ entry_block : BasicBlock ,
300
+ end_blocks : Option < ( BasicBlock , BasicBlock ) > ,
287
301
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) ;
290
304
291
305
let Self { decision_span, decision_depth, mut matching_arms, start_tests, end_tests : _ } =
292
306
self ;
@@ -295,13 +309,6 @@ impl PatternDecisionCtx {
295
309
blocks. into_iter ( ) . map ( |& block| inject_block_marker ( cfg, span, block) ) . collect ( )
296
310
} ;
297
311
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
-
305
312
let mut branch_spans = vec ! [ ] ;
306
313
307
314
// LLVM implementation requires the entry condition should be assigned id 1.
@@ -357,7 +364,7 @@ impl PatternDecisionCtx {
357
364
true_markers : into_block_markers ( span, & matched_blocks) ,
358
365
} ) ;
359
366
}
360
- assert_eq ! ( branch_spans . len ( ) , decision . conditions_num , "conditions num is not correct" ) ;
367
+
361
368
branch_spans. sort_by ( |lhs, rhs| {
362
369
if lhs. span . contains ( rhs. span ) {
363
370
std:: cmp:: Ordering :: Less
@@ -381,6 +388,15 @@ impl PatternDecisionCtx {
381
388
branch_spans[ idx] . span = span;
382
389
}
383
390
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
+
384
400
( decision, branch_spans)
385
401
}
386
402
}
@@ -481,8 +497,8 @@ impl MCDCInfoBuilder {
481
497
& mut self ,
482
498
cfg : & mut CFG < ' _ > ,
483
499
tcx : TyCtxt < ' _ > ,
484
- matching_block : BasicBlock ,
485
- failure_block : BasicBlock ,
500
+ entry_block : BasicBlock ,
501
+ end_blocks : Option < ( BasicBlock , BasicBlock ) > ,
486
502
inject_block_marker : impl FnMut ( & mut CFG < ' _ > , Span , BasicBlock ) -> BlockMarkerId ,
487
503
) {
488
504
if !self
@@ -497,15 +513,24 @@ impl MCDCInfoBuilder {
497
513
} ;
498
514
499
515
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) ;
502
527
}
503
528
504
529
fn append_mcdc_info (
505
530
& mut self ,
506
531
tcx : TyCtxt < ' _ > ,
507
532
decision : MCDCDecisionSpan ,
508
- mut conditions : Vec < MCDCBranchSpan > ,
533
+ conditions : Vec < MCDCBranchSpan > ,
509
534
) {
510
535
// take_condition() returns Some for decision_result when the decision stack
511
536
// is empty, i.e. when all the conditions of the decision were instrumented,
@@ -521,9 +546,7 @@ impl MCDCInfoBuilder {
521
546
// MCDC is equivalent to normal branch coverage if number of conditions is less than 1, so ignore these decisions.
522
547
// See comment of `MAX_CONDITIONS_NUM_IN_DECISION` for why decisions with oversized conditions are ignored.
523
548
_ => {
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) ;
527
550
if decision. conditions_num > MAX_CONDITIONS_NUM_IN_DECISION {
528
551
tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
529
552
span : decision. span ,
@@ -590,8 +613,8 @@ impl Builder<'_, '_> {
590
613
591
614
pub ( crate ) fn mcdc_finish_pattern_matching_decision (
592
615
& mut self ,
593
- matching_block : BasicBlock ,
594
- failure_block : BasicBlock ,
616
+ entry_block : BasicBlock ,
617
+ end_blocks : Option < ( BasicBlock , BasicBlock ) > ,
595
618
) {
596
619
if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
597
620
&& let Some ( mcdc_info) = branch_info. mcdc_info . as_mut ( )
@@ -606,8 +629,8 @@ impl Builder<'_, '_> {
606
629
mcdc_info. finish_pattern_decision (
607
630
& mut self . cfg ,
608
631
self . tcx ,
609
- matching_block ,
610
- failure_block ,
632
+ entry_block ,
633
+ end_blocks ,
611
634
inject_block_marker,
612
635
) ;
613
636
}
0 commit comments