1- use  rustc_middle:: mir; 
2- use  rustc_middle:: mir:: coverage:: BranchSpan ; 
1+ use  std:: assert_matches:: assert_matches; 
2+ use  std:: collections:: hash_map:: Entry ; 
3+ 
4+ use  rustc_data_structures:: fx:: FxHashMap ; 
5+ use  rustc_middle:: mir:: coverage:: { BlockMarkerId ,  BranchSpan ,  CoverageKind } ; 
6+ use  rustc_middle:: mir:: { self ,  BasicBlock ,  UnOp } ; 
7+ use  rustc_middle:: thir:: { ExprId ,  ExprKind ,  Thir } ; 
38use  rustc_middle:: ty:: TyCtxt ; 
49use  rustc_span:: def_id:: LocalDefId ; 
510
11+ use  crate :: build:: Builder ; 
12+ 
613pub ( crate )  struct  HirBranchInfoBuilder  { 
14+     inversions :  FxHashMap < ExprId ,  Inversion > , 
15+ 
716    num_block_markers :  usize , 
817    branch_spans :  Vec < BranchSpan > , 
918} 
1019
20+ #[ derive( Clone ,  Copy ) ]  
21+ struct  Inversion  { 
22+     /// When visiting the associated expression as a branch condition, treat this 
23+      /// enclosing `!` as the branch condition instead. 
24+      enclosing_not :  ExprId , 
25+     /// True if the associated expression is nested within an odd number of `!` 
26+      /// expressions relative to `enclosing_not` (inclusive of `enclosing_not`). 
27+      is_inverted :  bool , 
28+ } 
29+ 
1130impl  HirBranchInfoBuilder  { 
1231    pub ( crate )  fn  new_if_enabled_and_eligible ( tcx :  TyCtxt < ' _ > ,  def_id :  LocalDefId )  -> Option < Self >  { 
1332        if  tcx. sess . instrument_coverage_branch ( )  && tcx. is_eligible_for_coverage ( def_id)  { 
14-             Some ( Self  {  num_block_markers :  0 ,  branch_spans :  vec ! [ ]  } ) 
33+             Some ( Self  { 
34+                 inversions :  FxHashMap :: default ( ) , 
35+                 num_block_markers :  0 , 
36+                 branch_spans :  vec ! [ ] , 
37+             } ) 
1538        }  else  { 
1639            None 
1740        } 
1841    } 
1942
43+     /// Unary `!` expressions inside an `if` condition are lowered by lowering 
44+      /// their argument instead, and then reversing the then/else arms of that `if`. 
45+      /// 
46+      /// That's awkward for branch coverage instrumentation, so to work around that 
47+      /// we pre-emptively visit any affected `!` expressions, and record extra 
48+      /// information that [`Builder::visit_coverage_branch_condition`] can use to 
49+      /// synthesize branch instrumentation for the enclosing `!`. 
50+      pub ( crate )  fn  visit_unary_not ( & mut  self ,  thir :  & Thir < ' _ > ,  unary_not :  ExprId )  { 
51+         assert_matches ! ( thir[ unary_not] . kind,  ExprKind :: Unary  {  op:  UnOp :: Not ,  .. } ) ; 
52+ 
53+         self . visit_inverted ( 
54+             thir, 
55+             unary_not, 
56+             // Set `is_inverted: false` for the `!` itself, so that its enclosed 
57+             // expression will have `is_inverted: true`. 
58+             Inversion  {  enclosing_not :  unary_not,  is_inverted :  false  } , 
59+         ) ; 
60+     } 
61+ 
62+     fn  visit_inverted ( & mut  self ,  thir :  & Thir < ' _ > ,  expr_id :  ExprId ,  inversion :  Inversion )  { 
63+         match  self . inversions . entry ( expr_id)  { 
64+             // This expression has already been marked by an enclosing `!`. 
65+             Entry :: Occupied ( _)  => return , 
66+             Entry :: Vacant ( entry)  => entry. insert ( inversion) , 
67+         } ; 
68+ 
69+         match  thir[ expr_id] . kind  { 
70+             ExprKind :: Unary  {  op :  UnOp :: Not ,  arg }  => { 
71+                 // Flip the `is_inverted` flag for the contents of this `!`. 
72+                 let  inversion = Inversion  {  is_inverted :  !inversion. is_inverted ,  ..inversion } ; 
73+                 self . visit_inverted ( thir,  arg,  inversion) ; 
74+             } 
75+             ExprKind :: Scope  {  value,  .. }  => self . visit_inverted ( thir,  value,  inversion) , 
76+             ExprKind :: Use  {  source }  => self . visit_inverted ( thir,  source,  inversion) , 
77+             // All other expressions (including `&&` and `||`) don't need any 
78+             // special handling of their contents, so stop visiting. 
79+             _ => { } 
80+         } 
81+     } 
82+ 
83+     fn  next_block_marker_id ( & mut  self )  -> BlockMarkerId  { 
84+         let  id = BlockMarkerId :: from_usize ( self . num_block_markers ) ; 
85+         self . num_block_markers  += 1 ; 
86+         id
87+     } 
88+ 
2089    pub ( crate )  fn  into_done ( self )  -> Option < Box < mir:: coverage:: HirBranchInfo > >  { 
21-         let  Self  {  num_block_markers,  branch_spans }  = self ; 
90+         let  Self  {  inversions :  _ ,   num_block_markers,  branch_spans }  = self ; 
2291
2392        if  num_block_markers == 0  { 
2493            assert ! ( branch_spans. is_empty( ) ) ; 
@@ -28,3 +97,54 @@ impl HirBranchInfoBuilder {
2897        Some ( Box :: new ( mir:: coverage:: HirBranchInfo  {  num_block_markers,  branch_spans } ) ) 
2998    } 
3099} 
100+ 
101+ impl  Builder < ' _ ,  ' _ >  { 
102+     /// If branch coverage is enabled, inject marker statements into `then_block` 
103+      /// and `else_block`, and record their IDs in the table of branch spans. 
104+      pub ( crate )  fn  visit_coverage_branch_condition ( 
105+         & mut  self , 
106+         expr_id :  ExprId , 
107+         then_block :  BasicBlock , 
108+         else_block :  BasicBlock , 
109+     )  { 
110+         // Bail out if branch coverage is not enabled for this function. 
111+         let  Some ( branch_info)  = self . coverage_branch_info . as_ref ( )  else  {  return  } ; 
112+ 
113+         // If this condition expression is nested within one or more `!` expressions, 
114+         // replace it with the enclosing `!` collected by `visit_unary_not`. 
115+         let  ( expr_id,  is_inverted)  = match  branch_info. inversions . get ( & expr_id)  { 
116+             Some ( & Inversion  {  enclosing_not,  is_inverted } )  => ( enclosing_not,  is_inverted) , 
117+             None  => ( expr_id,  false ) , 
118+         } ; 
119+         let  source_info = self . source_info ( self . thir [ expr_id] . span ) ; 
120+ 
121+         // Now that we have `source_info`, we can upgrade to a &mut reference. 
122+         let  branch_info = self . coverage_branch_info . as_mut ( ) . expect ( "upgrading & to &mut" ) ; 
123+ 
124+         let  mut  inject_branch_marker = |block :  BasicBlock | { 
125+             let  id = branch_info. next_block_marker_id ( ) ; 
126+ 
127+             let  marker_statement = mir:: Statement  { 
128+                 source_info, 
129+                 kind :  mir:: StatementKind :: Coverage ( Box :: new ( mir:: Coverage  { 
130+                     kind :  CoverageKind :: BlockMarker  {  id } , 
131+                 } ) ) , 
132+             } ; 
133+             self . cfg . push ( block,  marker_statement) ; 
134+ 
135+             id
136+         } ; 
137+ 
138+         let  mut  true_marker = inject_branch_marker ( then_block) ; 
139+         let  mut  false_marker = inject_branch_marker ( else_block) ; 
140+         if  is_inverted { 
141+             std:: mem:: swap ( & mut  true_marker,  & mut  false_marker) ; 
142+         } 
143+ 
144+         branch_info. branch_spans . push ( BranchSpan  { 
145+             span :  source_info. span , 
146+             true_marker, 
147+             false_marker, 
148+         } ) ; 
149+     } 
150+ } 
0 commit comments