@@ -101,10 +101,14 @@ impl BranchInfoBuilder {
101
101
tcx : TyCtxt < ' _ > ,
102
102
true_marker : BlockMarkerId ,
103
103
false_marker : BlockMarkerId ,
104
- ) -> Option < ConditionInfo > {
104
+ ) -> Option < ( u16 , ConditionInfo ) > {
105
105
let mcdc_state = self . mcdc_state . as_mut ( ) ?;
106
+ let decision_depth = mcdc_state. decision_depth ( ) ;
106
107
let ( mut condition_info, decision_result) =
107
108
mcdc_state. take_condition ( true_marker, false_marker) ;
109
+ // take_condition() returns Some for decision_result when the decision stack
110
+ // is empty, i.e. when all the conditions of the decision were instrumented,
111
+ // and the decision is "complete".
108
112
if let Some ( decision) = decision_result {
109
113
match decision. conditions_num {
110
114
0 => {
@@ -131,7 +135,7 @@ impl BranchInfoBuilder {
131
135
}
132
136
}
133
137
}
134
- condition_info
138
+ condition_info. map ( |cond_info| ( decision_depth , cond_info ) )
135
139
}
136
140
137
141
fn add_two_way_branch < ' tcx > (
@@ -199,17 +203,28 @@ impl BranchInfoBuilder {
199
203
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
200
204
const MAX_CONDITIONS_NUM_IN_DECISION : usize = 6 ;
201
205
202
- struct MCDCState {
206
+ #[ derive( Default ) ]
207
+ struct MCDCDecisionCtx {
203
208
/// To construct condition evaluation tree.
204
209
decision_stack : VecDeque < ConditionInfo > ,
205
210
processing_decision : Option < MCDCDecisionSpan > ,
206
211
}
207
212
213
+ struct MCDCState {
214
+ decision_ctx_stack : Vec < MCDCDecisionCtx > ,
215
+ }
216
+
208
217
impl MCDCState {
209
218
fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
210
219
tcx. sess
211
220
. instrument_coverage_mcdc ( )
212
- . then ( || Self { decision_stack : VecDeque :: new ( ) , processing_decision : None } )
221
+ . then ( || Self { decision_ctx_stack : vec ! [ MCDCDecisionCtx :: default ( ) ] } )
222
+ }
223
+
224
+ #[ inline]
225
+ fn decision_depth ( & self ) -> u16 {
226
+ self . decision_ctx_stack . len ( ) . checked_sub ( 1 ) . expect ( "Unexpected empty decision stack" )
227
+ as u16
213
228
}
214
229
215
230
// At first we assign ConditionIds for each sub expression.
@@ -253,20 +268,23 @@ impl MCDCState {
253
268
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
254
269
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
255
270
fn record_conditions ( & mut self , op : LogicalOp , span : Span ) {
256
- let decision = match self . processing_decision . as_mut ( ) {
271
+ let decision_depth = self . decision_depth ( ) ;
272
+ let decision_ctx =
273
+ self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
274
+ let decision = match decision_ctx. processing_decision . as_mut ( ) {
257
275
Some ( decision) => {
258
276
decision. span = decision. span . to ( span) ;
259
277
decision
260
278
}
261
- None => self . processing_decision . insert ( MCDCDecisionSpan {
279
+ None => decision_ctx . processing_decision . insert ( MCDCDecisionSpan {
262
280
span,
263
281
conditions_num : 0 ,
264
282
end_markers : vec ! [ ] ,
265
- decision_depth : 0 ,
283
+ decision_depth : decision_depth ,
266
284
} ) ,
267
285
} ;
268
286
269
- let parent_condition = self . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
287
+ let parent_condition = decision_ctx . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
270
288
let lhs_id = if parent_condition. condition_id == ConditionId :: NONE {
271
289
decision. conditions_num += 1 ;
272
290
ConditionId :: from ( decision. conditions_num )
@@ -306,19 +324,21 @@ impl MCDCState {
306
324
}
307
325
} ;
308
326
// We visit expressions tree in pre-order, so place the left-hand side on the top.
309
- self . decision_stack . push_back ( rhs) ;
310
- self . decision_stack . push_back ( lhs) ;
327
+ decision_ctx . decision_stack . push_back ( rhs) ;
328
+ decision_ctx . decision_stack . push_back ( lhs) ;
311
329
}
312
330
313
331
fn take_condition (
314
332
& mut self ,
315
333
true_marker : BlockMarkerId ,
316
334
false_marker : BlockMarkerId ,
317
335
) -> ( Option < ConditionInfo > , Option < MCDCDecisionSpan > ) {
318
- let Some ( condition_info) = self . decision_stack . pop_back ( ) else {
336
+ let decision_ctx =
337
+ self . decision_ctx_stack . last_mut ( ) . expect ( "Unexpected empty decision_ctx_stack" ) ;
338
+ let Some ( condition_info) = decision_ctx. decision_stack . pop_back ( ) else {
319
339
return ( None , None ) ;
320
340
} ;
321
- let Some ( decision) = self . processing_decision . as_mut ( ) else {
341
+ let Some ( decision) = decision_ctx . processing_decision . as_mut ( ) else {
322
342
bug ! ( "Processing decision should have been created before any conditions are taken" ) ;
323
343
} ;
324
344
if condition_info. true_next_id == ConditionId :: NONE {
@@ -328,8 +348,8 @@ impl MCDCState {
328
348
decision. end_markers . push ( false_marker) ;
329
349
}
330
350
331
- if self . decision_stack . is_empty ( ) {
332
- ( Some ( condition_info) , self . processing_decision . take ( ) )
351
+ if decision_ctx . decision_stack . is_empty ( ) {
352
+ ( Some ( condition_info) , decision_ctx . processing_decision . take ( ) )
333
353
} else {
334
354
( Some ( condition_info) , None )
335
355
}
@@ -365,14 +385,17 @@ impl Builder<'_, '_> {
365
385
|block| branch_info. inject_block_marker ( & mut self . cfg , source_info, block) ;
366
386
let true_marker = inject_block_marker ( then_block) ;
367
387
let false_marker = inject_block_marker ( else_block) ;
368
- let condition_info =
369
- branch_info. fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker) ;
388
+ let ( decision_depth, condition_info) = branch_info
389
+ . fetch_mcdc_condition_info ( self . tcx , true_marker, false_marker)
390
+ . map_or ( ( 0 , None ) , |( decision_depth, condition_info) | {
391
+ ( decision_depth, Some ( condition_info) )
392
+ } ) ;
370
393
branch_info. mcdc_branch_spans . push ( MCDCBranchSpan {
371
394
span : source_info. span ,
372
395
condition_info,
373
396
true_marker,
374
397
false_marker,
375
- decision_depth : 0 ,
398
+ decision_depth,
376
399
} ) ;
377
400
return ;
378
401
}
@@ -387,4 +410,20 @@ impl Builder<'_, '_> {
387
410
mcdc_state. record_conditions ( logical_op, span) ;
388
411
}
389
412
}
413
+
414
+ pub ( crate ) fn mcdc_increment_depth_if_enabled ( & mut self ) {
415
+ if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
416
+ && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
417
+ {
418
+ mcdc_state. decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
419
+ } ;
420
+ }
421
+
422
+ pub ( crate ) fn mcdc_decrement_depth_if_enabled ( & mut self ) {
423
+ if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
424
+ && let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
425
+ {
426
+ mcdc_state. decision_ctx_stack . pop ( ) . expect ( "Unexpected empty decision stack" ) ;
427
+ } ;
428
+ }
390
429
}
0 commit comments