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 } ;
3
4
use rustc_middle:: ty:: TyCtxt ;
4
5
use rustc_span:: def_id:: LocalDefId ;
5
6
7
+ use crate :: build:: Builder ;
8
+
6
9
pub ( crate ) struct HirBranchInfoBuilder {
7
10
num_block_markers : usize ,
8
11
branch_spans : Vec < BranchSpan > ,
@@ -17,6 +20,12 @@ impl HirBranchInfoBuilder {
17
20
}
18
21
}
19
22
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
+
20
29
pub ( crate ) fn into_done ( self ) -> Option < Box < mir:: coverage:: HirBranchInfo > > {
21
30
let Self { num_block_markers, branch_spans } = self ;
22
31
@@ -28,3 +37,72 @@ impl HirBranchInfoBuilder {
28
37
Some ( Box :: new ( mir:: coverage:: HirBranchInfo { num_block_markers, branch_spans } ) )
29
38
}
30
39
}
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