1- use rustc_middle:: mir;
2- use rustc_middle:: mir:: coverage:: BranchSpan ;
1+ use rustc_middle:: mir:: coverage:: { BlockMarkerId , BranchSpan , CoverageKind } ;
2+ use rustc_middle:: mir:: { self , BasicBlock , Statement , StatementKind , UnOp } ;
3+ use rustc_middle:: thir:: { ExprId , ExprKind } ;
34use rustc_middle:: ty:: TyCtxt ;
45use rustc_span:: def_id:: LocalDefId ;
56
7+ use crate :: build:: Builder ;
8+
69pub ( crate ) struct HirBranchInfoBuilder {
710 num_block_markers : usize ,
811 branch_spans : Vec < BranchSpan > ,
@@ -17,6 +20,12 @@ impl HirBranchInfoBuilder {
1720 }
1821 }
1922
23+ fn next_block_marker_id ( & mut self ) -> BlockMarkerId {
24+ let id = BlockMarkerId :: from_usize ( self . num_block_markers ) ;
25+ self . num_block_markers += 1 ;
26+ id
27+ }
28+
2029 pub ( crate ) fn into_done ( self ) -> Option < Box < mir:: coverage:: HirBranchInfo > > {
2130 let Self { num_block_markers, branch_spans } = self ;
2231
@@ -28,3 +37,72 @@ impl HirBranchInfoBuilder {
2837 Some ( Box :: new ( mir:: coverage:: HirBranchInfo { num_block_markers, branch_spans } ) )
2938 }
3039}
40+
41+ impl < ' a , ' tcx > Builder < ' a , ' tcx > {
42+ /// If branch coverage is enabled, inject marker statements into `then_block`
43+ /// and `else_block`, and record their IDs in the table of branch spans.
44+ pub ( crate ) fn coverage_record_branch (
45+ & mut self ,
46+ cond_expr_id : ExprId ,
47+ enclosing_not_expr_id : Option < ExprId > ,
48+ then_block : BasicBlock ,
49+ else_block : BasicBlock ,
50+ ) {
51+ // If branch coverage instrumentation isn't enabled, do nothing.
52+ if self . coverage_branch_info . is_none ( ) {
53+ return ;
54+ }
55+
56+ // If the condition is nested in an odd number of `!` expressions, we
57+ // need to reverse the meaning of the then/else block markers, so that
58+ // they correspond to the outermost `!` expression being true/false.
59+ let swap_arm_markers = {
60+ let mut curr = enclosing_not_expr_id. unwrap_or ( cond_expr_id) ;
61+ let mut swap_arm_markers = false ;
62+
63+ while curr != cond_expr_id {
64+ match self . thir [ curr] . kind {
65+ ExprKind :: Scope { value, .. } => curr = value,
66+ ExprKind :: Unary { op : UnOp :: Not , arg } => {
67+ swap_arm_markers = !swap_arm_markers;
68+ curr = arg;
69+ }
70+ _ => unreachable ! ( "ensured by `Builder::then_else_break_inner`" ) ,
71+ }
72+ }
73+ swap_arm_markers
74+ } ;
75+
76+ let cond_source_info =
77+ self . source_info ( self . thir [ enclosing_not_expr_id. unwrap_or ( cond_expr_id) ] . span ) ;
78+
79+ let branch_info_builder =
80+ self . coverage_branch_info . as_mut ( ) . expect ( "confirmed present above" ) ;
81+
82+ let mut inject_branch_marker = |block : BasicBlock | {
83+ let id = branch_info_builder. next_block_marker_id ( ) ;
84+
85+ let marker_statement = Statement {
86+ source_info : cond_source_info,
87+ kind : StatementKind :: Coverage ( Box :: new ( mir:: Coverage {
88+ kind : CoverageKind :: BlockMarker { id } ,
89+ } ) ) ,
90+ } ;
91+ self . cfg . push ( block, marker_statement) ;
92+
93+ id
94+ } ;
95+
96+ let mut true_marker = inject_branch_marker ( then_block) ;
97+ let mut false_marker = inject_branch_marker ( else_block) ;
98+ if swap_arm_markers {
99+ std:: mem:: swap ( & mut true_marker, & mut false_marker) ;
100+ }
101+
102+ branch_info_builder. branch_spans . push ( BranchSpan {
103+ span : cond_source_info. span ,
104+ true_marker,
105+ false_marker,
106+ } ) ;
107+ }
108+ }
0 commit comments