@@ -111,6 +111,9 @@ impl BranchInfoBuilder {
111
111
let decision_depth = mcdc_state. decision_depth ;
112
112
let ( mut condition_info, decision_result) =
113
113
mcdc_state. take_condition ( true_marker, false_marker) ;
114
+ // take_condition() returns Some for decision_result when the decision stack
115
+ // is empty, i.e. when all the conditions of the decision were instrumented,
116
+ // and the decision is "complete".
114
117
if let Some ( decision) = decision_result {
115
118
match decision. conditions_num {
116
119
0 => {
@@ -180,22 +183,25 @@ impl BranchInfoBuilder {
180
183
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
181
184
const MAX_CONDITIONS_NUM_IN_DECISION : usize = 6 ;
182
185
183
- struct MCDCState {
186
+ #[ derive( Default ) ]
187
+ struct MCDCDecisionCtx {
184
188
/// To construct condition evaluation tree.
185
189
decision_stack : VecDeque < ConditionInfo > ,
186
190
processing_decision : Option < MCDCDecisionSpan > ,
191
+ }
192
+
193
+ struct MCDCState {
194
+ decision_ctx_stack : Vec < MCDCDecisionCtx > ,
195
+ /// To track the nesting level of boolean expressions.
187
196
decision_depth : u16 ,
188
197
}
189
198
190
199
impl MCDCState {
191
200
fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
192
- tcx. sess
193
- . instrument_coverage_mcdc ( )
194
- . then ( || Self {
195
- decision_stack : VecDeque :: new ( ) ,
196
- processing_decision : None ,
197
- decision_depth : 0 ,
198
- } )
201
+ tcx. sess . instrument_coverage_mcdc ( ) . then ( || Self {
202
+ decision_ctx_stack : vec ! [ MCDCDecisionCtx :: default ( ) ] ,
203
+ decision_depth : 0 ,
204
+ } )
199
205
}
200
206
201
207
// At first we assign ConditionIds for each sub expression.
@@ -239,20 +245,21 @@ impl MCDCState {
239
245
// - 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".
240
246
// - 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".
241
247
fn record_conditions ( & mut self , op : LogicalOp , span : Span ) {
242
- let decision = match self . processing_decision . as_mut ( ) {
248
+ let decision_ctx = & mut self . decision_ctx_stack [ self . decision_depth as usize ] ;
249
+ let decision = match decision_ctx. processing_decision . as_mut ( ) {
243
250
Some ( decision) => {
244
251
decision. span = decision. span . to ( span) ;
245
252
decision
246
253
}
247
- None => self . processing_decision . insert ( MCDCDecisionSpan {
254
+ None => decision_ctx . processing_decision . insert ( MCDCDecisionSpan {
248
255
span,
249
256
conditions_num : 0 ,
250
257
end_markers : vec ! [ ] ,
251
- decision_depth : 0 ,
258
+ decision_depth : self . decision_depth ,
252
259
} ) ,
253
260
} ;
254
261
255
- let parent_condition = self . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
262
+ let parent_condition = decision_ctx . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
256
263
let lhs_id = if parent_condition. condition_id == ConditionId :: NONE {
257
264
decision. conditions_num += 1 ;
258
265
ConditionId :: from ( decision. conditions_num )
@@ -292,19 +299,20 @@ impl MCDCState {
292
299
}
293
300
} ;
294
301
// We visit expressions tree in pre-order, so place the left-hand side on the top.
295
- self . decision_stack . push_back ( rhs) ;
296
- self . decision_stack . push_back ( lhs) ;
302
+ decision_ctx . decision_stack . push_back ( rhs) ;
303
+ decision_ctx . decision_stack . push_back ( lhs) ;
297
304
}
298
305
299
306
fn take_condition (
300
307
& mut self ,
301
308
true_marker : BlockMarkerId ,
302
309
false_marker : BlockMarkerId ,
303
310
) -> ( Option < ConditionInfo > , Option < MCDCDecisionSpan > ) {
304
- let Some ( condition_info) = self . decision_stack . pop_back ( ) else {
311
+ let decision_ctx = & mut self . decision_ctx_stack [ self . decision_depth as usize ] ;
312
+ let Some ( condition_info) = decision_ctx. decision_stack . pop_back ( ) else {
305
313
return ( None , None ) ;
306
314
} ;
307
- let Some ( decision) = self . processing_decision . as_mut ( ) else {
315
+ let Some ( decision) = decision_ctx . processing_decision . as_mut ( ) else {
308
316
bug ! ( "Processing decision should have been created before any conditions are taken" ) ;
309
317
} ;
310
318
if condition_info. true_next_id == ConditionId :: NONE {
@@ -314,8 +322,8 @@ impl MCDCState {
314
322
decision. end_markers . push ( false_marker) ;
315
323
}
316
324
317
- if self . decision_stack . is_empty ( ) {
318
- ( Some ( condition_info) , self . processing_decision . take ( ) )
325
+ if decision_ctx . decision_stack . is_empty ( ) {
326
+ ( Some ( condition_info) , decision_ctx . processing_decision . take ( ) )
319
327
} else {
320
328
( Some ( condition_info) , None )
321
329
}
@@ -391,6 +399,7 @@ impl Builder<'_, '_> {
391
399
if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
392
400
&& let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
393
401
{
402
+ mcdc_state. decision_ctx_stack . push ( MCDCDecisionCtx :: default ( ) ) ;
394
403
mcdc_state. decision_depth += 1 ;
395
404
} ;
396
405
}
@@ -399,6 +408,12 @@ impl Builder<'_, '_> {
399
408
if let Some ( branch_info) = self . coverage_branch_info . as_mut ( )
400
409
&& let Some ( mcdc_state) = branch_info. mcdc_state . as_mut ( )
401
410
{
411
+ #[ allow( unused) ]
412
+ let Some ( MCDCDecisionCtx { decision_stack, processing_decision } ) =
413
+ mcdc_state. decision_ctx_stack . pop ( )
414
+ else {
415
+ bug ! ( "Unexpected empty Decision stack" ) ;
416
+ } ;
402
417
mcdc_state. decision_depth -= 1 ;
403
418
} ;
404
419
}
0 commit comments