1use std::assert_matches;
2use std::collections::hash_map::Entry;
3
4use rustc_data_structures::fx::FxHashMap;
5use rustc_hir::HirId;
6use rustc_middle::mir::coverage::{
7 BlockMarkerId, BranchSpan, CoverageEarlyInfo, CoverageKind, PointKind,
8};
9use rustc_middle::mir::{self, BasicBlock, SourceInfo, Statement, UnOp};
10use rustc_middle::thir::{self, ExprId, ExprKind, Pat, Thir};
11use rustc_middle::ty::TyCtxt;
12use rustc_span::def_id::LocalDefId;
13
14use crate::builder::{Builder, CFG};
15
16pub(crate) struct CoverageInfoBuilder {
22 nots: FxHashMap<ExprId, NotInfo>,
24
25 markers: BlockMarkerGen,
26
27 branch_info: Option<BranchInfo>,
29}
30
31#[derive(#[automatically_derived]
impl ::core::default::Default for BranchInfo {
#[inline]
fn default() -> BranchInfo {
BranchInfo { branch_spans: ::core::default::Default::default() }
}
}Default)]
32struct BranchInfo {
33 branch_spans: Vec<BranchSpan>,
34}
35
36#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for NotInfo { }
#[automatically_derived]
impl ::core::clone::Clone for NotInfo {
#[inline]
fn clone(&self) -> NotInfo {
let _: ::core::clone::AssertParamIsClone<ExprId>;
let _: ::core::clone::AssertParamIsClone<bool>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NotInfo { }Copy)]
37struct NotInfo {
38 enclosing_not: ExprId,
41 is_flipped: bool,
44}
45
46#[derive(#[automatically_derived]
impl ::core::default::Default for BlockMarkerGen {
#[inline]
fn default() -> BlockMarkerGen {
BlockMarkerGen {
num_block_markers: ::core::default::Default::default(),
}
}
}Default)]
47struct BlockMarkerGen {
48 num_block_markers: usize,
49}
50
51impl BlockMarkerGen {
52 fn next_block_marker_id(&mut self) -> BlockMarkerId {
53 let id = BlockMarkerId::from_usize(self.num_block_markers);
54 self.num_block_markers += 1;
55 id
56 }
57
58 fn inject_block_marker(
59 &mut self,
60 cfg: &mut CFG<'_>,
61 source_info: SourceInfo,
62 block: BasicBlock,
63 ) -> BlockMarkerId {
64 let id = self.next_block_marker_id();
65 let marker_statement = mir::Statement::new(
66 source_info,
67 mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }),
68 );
69 cfg.push(block, marker_statement);
70
71 id
72 }
73}
74
75impl CoverageInfoBuilder {
76 pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Self> {
79 if !tcx.sess.instrument_coverage() || !tcx.is_eligible_for_coverage(def_id) {
80 return None;
81 }
82
83 Some(Self {
84 nots: FxHashMap::default(),
85 markers: BlockMarkerGen::default(),
86 branch_info: tcx.sess.instrument_coverage_branch().then(BranchInfo::default),
87 })
88 }
89
90 pub(crate) fn visit_unary_not(&mut self, thir: &Thir<'_>, unary_not: ExprId) {
98 {
match thir[unary_not].kind {
ExprKind::Unary { op: UnOp::Not, .. } => {}
ref left_val => {
::core::panicking::assert_matches_failed(left_val,
"ExprKind::Unary { op: UnOp::Not, .. }",
::core::option::Option::None);
}
}
};assert_matches!(thir[unary_not].kind, ExprKind::Unary { op: UnOp::Not, .. });
99
100 if self.branch_info.is_none() {
103 return;
104 }
105
106 self.visit_with_not_info(
107 thir,
108 unary_not,
109 NotInfo { enclosing_not: unary_not, is_flipped: false },
112 );
113 }
114
115 fn visit_with_not_info(&mut self, thir: &Thir<'_>, expr_id: ExprId, not_info: NotInfo) {
116 match self.nots.entry(expr_id) {
117 Entry::Occupied(_) => return,
119 Entry::Vacant(entry) => entry.insert(not_info),
120 };
121
122 match thir[expr_id].kind {
123 ExprKind::Unary { op: UnOp::Not, arg } => {
124 let not_info = NotInfo { is_flipped: !not_info.is_flipped, ..not_info };
126 self.visit_with_not_info(thir, arg, not_info);
127 }
128 ExprKind::Scope { value, .. } => self.visit_with_not_info(thir, value, not_info),
129 ExprKind::ValueExpr { source } => self.visit_with_not_info(thir, source, not_info),
130 _ => {}
133 }
134 }
135
136 fn register_two_way_branch<'tcx>(
137 &mut self,
138 cfg: &mut CFG<'tcx>,
139 source_info: SourceInfo,
140 true_block: BasicBlock,
141 false_block: BasicBlock,
142 ) {
143 let Some(branch_info) = self.branch_info.as_mut() else { return };
145
146 let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block);
147 let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block);
148
149 branch_info.branch_spans.push(BranchSpan {
150 span: source_info.span,
151 true_marker,
152 false_marker,
153 });
154 }
155
156 pub(crate) fn into_done(self) -> Box<CoverageEarlyInfo> {
157 let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, branch_info } = self;
158
159 let branch_spans =
160 branch_info.map(|branch_info| branch_info.branch_spans).unwrap_or_default();
161
162 Box::new(CoverageEarlyInfo { num_block_markers, branch_spans })
165 }
166
167 pub(crate) fn as_done(&self) -> Box<CoverageEarlyInfo> {
168 let &Self { nots: _, markers: BlockMarkerGen { num_block_markers }, ref branch_info } =
169 self;
170
171 let branch_spans = branch_info
172 .as_ref()
173 .map(|branch_info| branch_info.branch_spans.as_slice())
174 .unwrap_or_default()
175 .to_owned();
176
177 Box::new(CoverageEarlyInfo { num_block_markers, branch_spans })
180 }
181}
182
183impl<'tcx> Builder<'_, 'tcx> {
184 pub(crate) fn push_coverage_point_for_expr(
189 &mut self,
190 block: BasicBlock,
191 source_info: SourceInfo,
192 hir_id: HirId,
193 ) {
194 if !self.tcx.sess.instrument_coverage() {
195 return;
196 }
197 self.push_coverage_point_inner(block, source_info, PointKind::Expr, hir_id);
198 }
199
200 pub(crate) fn push_coverage_point_for_implicit_else(
206 &mut self,
207 block: BasicBlock,
208 source_info: SourceInfo,
209 if_expr: &thir::Expr<'tcx>,
210 ) {
211 if !self.tcx.sess.instrument_coverage() {
212 return;
213 }
214 let hir_id = HirId { owner: self.hir_id.owner, local_id: if_expr.temp_scope_id };
216 self.push_coverage_point_inner(block, source_info, PointKind::ImplicitElse, hir_id);
217 }
218
219 pub(crate) fn push_coverage_point_for_fn_end(
224 &mut self,
225 block: BasicBlock,
226 source_info: SourceInfo,
227 fn_hir_id: HirId,
228 ) {
229 if !self.tcx.sess.instrument_coverage() {
230 return;
231 }
232 self.push_coverage_point_inner(block, source_info, PointKind::FunctionEnd, fn_hir_id);
233 }
234
235 fn push_coverage_point_inner(
236 &mut self,
237 block: BasicBlock,
238 source_info: SourceInfo,
239 point_kind: PointKind,
240 hir_id: HirId,
241 ) {
242 if !self.tcx.sess.instrument_coverage() {
::core::panicking::panic("assertion failed: self.tcx.sess.instrument_coverage()")
};assert!(self.tcx.sess.instrument_coverage());
243
244 let stmt = Statement::new(
245 source_info,
246 mir::StatementKind::Coverage(CoverageKind::Point { point_kind, hir_id }),
247 );
248 self.cfg.push(block, stmt);
249 }
250
251 pub(crate) fn visit_coverage_standalone_condition(
254 &mut self,
255 mut expr_id: ExprId, place: mir::Place<'tcx>, block: &mut BasicBlock,
258 ) {
259 let Some(coverage_info) = self.coverage_info.as_mut() else { return };
261 if !self.tcx.sess.instrument_coverage_condition() {
262 return;
263 };
264
265 while let ExprKind::ValueExpr { source: inner } | ExprKind::Scope { value: inner, .. } =
267 self.thir[expr_id].kind
268 {
269 expr_id = inner;
270 }
271 if let ExprKind::LogicalOp { .. } = self.thir[expr_id].kind {
274 return;
275 }
276
277 let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
278
279 let true_block = self.cfg.start_new_block();
292 let false_block = self.cfg.start_new_block();
293 self.cfg.terminate(
294 *block,
295 source_info,
296 mir::TerminatorKind::if_(mir::Operand::Copy(place), true_block, false_block),
297 );
298
299 coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
300
301 let join_block = self.cfg.start_new_block();
302 self.cfg.goto(true_block, source_info, join_block);
303 self.cfg.goto(false_block, source_info, join_block);
304 *block = join_block;
306 }
307
308 pub(crate) fn visit_coverage_branch_condition(
311 &mut self,
312 mut expr_id: ExprId,
313 mut true_block: BasicBlock,
314 mut false_block: BasicBlock,
315 ) {
316 let Some(coverage_info) = self.coverage_info.as_mut() else { return };
318
319 if let Some(&NotInfo { enclosing_not, is_flipped }) = coverage_info.nots.get(&expr_id) {
322 expr_id = enclosing_not;
323 if is_flipped {
324 std::mem::swap(&mut true_block, &mut false_block);
325 }
326 }
327
328 let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
329
330 coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
331 }
332
333 pub(crate) fn visit_coverage_conditional_let(
338 &mut self,
339 pattern: &Pat<'tcx>, true_block: BasicBlock,
341 false_block: BasicBlock,
342 ) {
343 let Some(coverage_info) = self.coverage_info.as_mut() else { return };
345
346 let source_info = SourceInfo { span: pattern.span, scope: self.source_scope };
347 coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
348 }
349}