@@ -10,6 +10,7 @@ use rustc_middle::mir::{self, BasicBlock, UnOp};
10
10
use rustc_middle:: thir:: { ExprId , ExprKind , LogicalOp , Thir } ;
11
11
use rustc_middle:: ty:: TyCtxt ;
12
12
use rustc_span:: def_id:: LocalDefId ;
13
+ use rustc_span:: Span ;
13
14
14
15
use crate :: build:: Builder ;
15
16
use crate :: errors:: MCDCExceedsConditionNumLimit ;
@@ -91,8 +92,50 @@ impl BranchInfoBuilder {
91
92
}
92
93
}
93
94
94
- fn get_mcdc_state_mut ( & mut self ) -> Option < & mut MCDCState > {
95
- self . mcdc_state . as_mut ( )
95
+ fn record_conditions_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
96
+ if let Some ( mcdc_state) = self . mcdc_state . as_mut ( ) {
97
+ mcdc_state. record_conditions ( logical_op, span) ;
98
+ }
99
+ }
100
+
101
+ fn fetch_condition_info (
102
+ & mut self ,
103
+ tcx : TyCtxt < ' _ > ,
104
+ true_marker : BlockMarkerId ,
105
+ false_marker : BlockMarkerId ,
106
+ ) -> ConditionInfo {
107
+ let Some ( mcdc_state) = self . mcdc_state . as_mut ( ) else {
108
+ return ConditionInfo :: default ( ) ;
109
+ } ;
110
+ let ( mut condition_info, decision_result) =
111
+ mcdc_state. take_condition ( true_marker, false_marker) ;
112
+ if let Some ( decision) = decision_result {
113
+ match decision. conditions_num {
114
+ 0 => {
115
+ unreachable ! ( "Decision with no condition is not expected" ) ;
116
+ }
117
+ 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
118
+ self . decision_spans . push ( decision) ;
119
+ }
120
+ _ => {
121
+ // Do not generate mcdc mappings and statements for decisions with too many conditions.
122
+ for branch in
123
+ self . branch_spans . iter_mut ( ) . rev ( ) . take ( decision. conditions_num - 1 )
124
+ {
125
+ branch. condition_info = ConditionInfo :: default ( ) ;
126
+ }
127
+ // ConditionInfo of this branch shall also be reset.
128
+ condition_info = ConditionInfo :: default ( ) ;
129
+
130
+ tcx. dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
131
+ span : decision. span ,
132
+ conditions_num : decision. conditions_num ,
133
+ max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
134
+ } ) ;
135
+ }
136
+ }
137
+ }
138
+ condition_info
96
139
}
97
140
98
141
fn next_block_marker_id ( & mut self ) -> BlockMarkerId {
@@ -125,14 +168,14 @@ const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
125
168
struct MCDCState {
126
169
/// To construct condition evaluation tree.
127
170
decision_stack : VecDeque < ConditionInfo > ,
128
- next_condition_id : usize ,
171
+ processing_decision : Option < DecisionSpan > ,
129
172
}
130
173
131
174
impl MCDCState {
132
175
fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
133
176
tcx. sess
134
177
. instrument_coverage_mcdc ( )
135
- . then ( || Self { decision_stack : VecDeque :: new ( ) , next_condition_id : 0 } )
178
+ . then ( || Self { decision_stack : VecDeque :: new ( ) , processing_decision : None } )
136
179
}
137
180
138
181
/// At first we assign ConditionIds for each sub expression.
@@ -175,17 +218,29 @@ impl MCDCState {
175
218
/// As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited.
176
219
/// - 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".
177
220
/// - 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".
178
- fn record_conditions ( & mut self , op : LogicalOp ) {
221
+ fn record_conditions ( & mut self , op : LogicalOp , span : Span ) {
222
+ let decision = match self . processing_decision . as_mut ( ) {
223
+ Some ( decision) => {
224
+ decision. span = decision. span . to ( span) ;
225
+ decision
226
+ }
227
+ None => self . processing_decision . insert ( DecisionSpan {
228
+ span,
229
+ conditions_num : 0 ,
230
+ end_marker : vec ! [ ] ,
231
+ } ) ,
232
+ } ;
233
+
179
234
let parent_condition = self . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
180
235
let lhs_id = if parent_condition. condition_id == ConditionId :: NONE {
181
- self . next_condition_id += 1 ;
182
- ConditionId :: from ( self . next_condition_id )
236
+ decision . conditions_num += 1 ;
237
+ ConditionId :: from ( decision . conditions_num )
183
238
} else {
184
239
parent_condition. condition_id
185
240
} ;
186
241
187
- self . next_condition_id += 1 ;
188
- let rhs_condition_id = ConditionId :: from ( self . next_condition_id ) ;
242
+ decision . conditions_num += 1 ;
243
+ let rhs_condition_id = ConditionId :: from ( decision . conditions_num ) ;
189
244
190
245
let ( lhs, rhs) = match op {
191
246
LogicalOp :: And => {
@@ -219,6 +274,31 @@ impl MCDCState {
219
274
self . decision_stack . push_back ( rhs) ;
220
275
self . decision_stack . push_back ( lhs) ;
221
276
}
277
+
278
+ fn take_condition (
279
+ & mut self ,
280
+ true_marker : BlockMarkerId ,
281
+ false_marker : BlockMarkerId ,
282
+ ) -> ( ConditionInfo , Option < DecisionSpan > ) {
283
+ let Some ( condition_info) = self . decision_stack . pop_back ( ) else {
284
+ return ( ConditionInfo :: default ( ) , None ) ;
285
+ } ;
286
+ let Some ( decision) = self . processing_decision . as_mut ( ) else {
287
+ bug ! ( "Processing decision should have been created before any conditions are taken" ) ;
288
+ } ;
289
+ if condition_info. true_next_id == ConditionId :: NONE {
290
+ decision. end_marker . push ( true_marker) ;
291
+ }
292
+ if condition_info. false_next_id == ConditionId :: NONE {
293
+ decision. end_marker . push ( false_marker) ;
294
+ }
295
+
296
+ if self . decision_stack . is_empty ( ) {
297
+ ( condition_info, self . processing_decision . take ( ) )
298
+ } else {
299
+ ( condition_info, None )
300
+ }
301
+ }
222
302
}
223
303
224
304
impl Builder < ' _ , ' _ > {
@@ -246,12 +326,6 @@ impl Builder<'_, '_> {
246
326
// Now that we have `source_info`, we can upgrade to a &mut reference.
247
327
let branch_info = self . coverage_branch_info . as_mut ( ) . expect ( "upgrading & to &mut" ) ;
248
328
249
- let condition_info = branch_info
250
- . mcdc_state
251
- . as_mut ( )
252
- . and_then ( |state| state. decision_stack . pop_back ( ) )
253
- . unwrap_or_default ( ) ;
254
-
255
329
let mut inject_branch_marker = |block : BasicBlock | {
256
330
let id = branch_info. next_block_marker_id ( ) ;
257
331
@@ -267,6 +341,8 @@ impl Builder<'_, '_> {
267
341
let true_marker = inject_branch_marker ( then_block) ;
268
342
let false_marker = inject_branch_marker ( else_block) ;
269
343
344
+ let condition_info = branch_info. fetch_condition_info ( self . tcx , true_marker, false_marker) ;
345
+
270
346
branch_info. branch_spans . push ( BranchSpan {
271
347
span : source_info. span ,
272
348
condition_info,
@@ -275,64 +351,9 @@ impl Builder<'_, '_> {
275
351
} ) ;
276
352
}
277
353
278
- pub ( crate ) fn visit_coverage_decision ( & mut self , expr_id : ExprId , join_block : BasicBlock ) {
279
- if let Some ( ( mcdc_state, branches) ) = self
280
- . coverage_branch_info
281
- . as_mut ( )
282
- . and_then ( |builder| builder. mcdc_state . as_mut ( ) . zip ( Some ( & mut builder. branch_spans ) ) )
283
- {
284
- assert ! (
285
- mcdc_state. decision_stack. is_empty( ) ,
286
- "All condition should have been checked before the decision ends"
287
- ) ;
288
-
289
- let conditions_num = mcdc_state. next_condition_id ;
290
-
291
- mcdc_state. next_condition_id = 0 ;
292
-
293
- match conditions_num {
294
- 0 => {
295
- unreachable ! ( "Decision with no conditions is not allowed" ) ;
296
- }
297
- 1 ..=MAX_CONDITIONS_NUM_IN_DECISION => {
298
- let span = self . thir [ expr_id] . span ;
299
- let branch_info =
300
- self . coverage_branch_info . as_mut ( ) . expect ( "updating to existed" ) ;
301
- let id = branch_info. next_block_marker_id ( ) ;
302
-
303
- branch_info. decision_spans . push ( DecisionSpan {
304
- span,
305
- conditions_num : conditions_num as u16 ,
306
- join_marker : id,
307
- } ) ;
308
-
309
- let statement = mir:: Statement {
310
- source_info : self . source_info ( span) ,
311
- kind : mir:: StatementKind :: Coverage ( CoverageKind :: BlockMarker { id } ) ,
312
- } ;
313
- self . cfg . push ( join_block, statement) ;
314
- }
315
- _ => {
316
- // Do not generate mcdc mappings and statements for decisions with too many conditions.
317
- for branch in branches. iter_mut ( ) . rev ( ) . take ( conditions_num) {
318
- branch. condition_info = Default :: default ( ) ;
319
- }
320
-
321
- self . tcx . dcx ( ) . emit_warn ( MCDCExceedsConditionNumLimit {
322
- span : self . thir [ expr_id] . span ,
323
- conditions_num,
324
- max_conditions_num : MAX_CONDITIONS_NUM_IN_DECISION ,
325
- } ) ;
326
- }
327
- }
328
- }
329
- }
330
-
331
- pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp ) {
332
- if let Some ( mcdc_state) =
333
- self . coverage_branch_info . as_mut ( ) . and_then ( BranchInfoBuilder :: get_mcdc_state_mut)
334
- {
335
- mcdc_state. record_conditions ( logical_op) ;
354
+ pub ( crate ) fn visit_coverage_branch_operation ( & mut self , logical_op : LogicalOp , span : Span ) {
355
+ if let Some ( branch_info) = self . coverage_branch_info . as_mut ( ) {
356
+ branch_info. record_conditions_operation ( logical_op, span) ;
336
357
}
337
358
}
338
359
}
0 commit comments