@@ -10,6 +10,7 @@ use rustc_middle::mir::{self, BasicBlock, UnOp};
1010use rustc_middle:: thir:: { ExprId , ExprKind , LogicalOp , Thir } ;
1111use rustc_middle:: ty:: TyCtxt ;
1212use rustc_span:: def_id:: LocalDefId ;
13+ use rustc_span:: Span ;
1314
1415use crate :: build:: Builder ;
1516use crate :: errors:: MCDCExceedsConditionNumLimit ;
@@ -91,8 +92,50 @@ impl BranchInfoBuilder {
9192 }
9293 }
9394
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
96139 }
97140
98141 fn next_block_marker_id ( & mut self ) -> BlockMarkerId {
@@ -125,14 +168,14 @@ const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
125168struct MCDCState {
126169 /// To construct condition evaluation tree.
127170 decision_stack : VecDeque < ConditionInfo > ,
128- next_condition_id : usize ,
171+ processing_decision : Option < DecisionSpan > ,
129172}
130173
131174impl MCDCState {
132175 fn new_if_enabled ( tcx : TyCtxt < ' _ > ) -> Option < Self > {
133176 tcx. sess
134177 . instrument_coverage_mcdc ( )
135- . then ( || Self { decision_stack : VecDeque :: new ( ) , next_condition_id : 0 } )
178+ . then ( || Self { decision_stack : VecDeque :: new ( ) , processing_decision : None } )
136179 }
137180
138181 /// At first we assign ConditionIds for each sub expression.
@@ -175,17 +218,29 @@ impl MCDCState {
175218 /// 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.
176219 /// - 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".
177220 /// - 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+
179234 let parent_condition = self . decision_stack . pop_back ( ) . unwrap_or_default ( ) ;
180235 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 )
183238 } else {
184239 parent_condition. condition_id
185240 } ;
186241
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 ) ;
189244
190245 let ( lhs, rhs) = match op {
191246 LogicalOp :: And => {
@@ -219,6 +274,31 @@ impl MCDCState {
219274 self . decision_stack . push_back ( rhs) ;
220275 self . decision_stack . push_back ( lhs) ;
221276 }
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+ }
222302}
223303
224304impl Builder < ' _ , ' _ > {
@@ -246,12 +326,6 @@ impl Builder<'_, '_> {
246326 // Now that we have `source_info`, we can upgrade to a &mut reference.
247327 let branch_info = self . coverage_branch_info . as_mut ( ) . expect ( "upgrading & to &mut" ) ;
248328
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-
255329 let mut inject_branch_marker = |block : BasicBlock | {
256330 let id = branch_info. next_block_marker_id ( ) ;
257331
@@ -267,6 +341,8 @@ impl Builder<'_, '_> {
267341 let true_marker = inject_branch_marker ( then_block) ;
268342 let false_marker = inject_branch_marker ( else_block) ;
269343
344+ let condition_info = branch_info. fetch_condition_info ( self . tcx , true_marker, false_marker) ;
345+
270346 branch_info. branch_spans . push ( BranchSpan {
271347 span : source_info. span ,
272348 condition_info,
@@ -275,64 +351,9 @@ impl Builder<'_, '_> {
275351 } ) ;
276352 }
277353
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) ;
336357 }
337358 }
338359}
0 commit comments