]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir/src/transform/coverage/tests.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / compiler / rustc_mir / src / transform / coverage / tests.rs
1 //! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR
2 //! pass.
3 //!
4 //! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage`
5 //! functions and algorithms. Mocked objects include instances of `mir::Body`; including
6 //! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on
7 //! real, runtime versions of these mocked-up objects have constraints (such as cross-thread
8 //! limitations) and deep dependencies on other elements of the full Rust compiler (which is
9 //! *not* constructed or mocked for these tests).
10 //!
11 //! Of particular note, attempting to simply print elements of the `mir::Body` with default
12 //! `Debug` formatting can fail because some `Debug` format implementations require the
13 //! `TyCtxt`, obtained via a static global variable that is *not* set for these tests.
14 //! Initializing the global type context is prohibitively complex for the scope and scale of these
15 //! tests (essentially requiring initializing the entire compiler).
16 //!
17 //! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which
18 //! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some
19 //! basic, coverage-specific features would be impossible to test, but thankfully initializing these
20 //! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument
21 //! to: `rustc_span::with_default_session_globals(|| { test_here(); })`.
22
23 use super::counters;
24 use super::debug;
25 use super::graph;
26 use super::spans;
27
28 use coverage_test_macros::let_bcb;
29
30 use rustc_data_structures::graph::WithNumNodes;
31 use rustc_data_structures::graph::WithSuccessors;
32 use rustc_index::vec::{Idx, IndexVec};
33 use rustc_middle::mir::coverage::CoverageKind;
34 use rustc_middle::mir::*;
35 use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags};
36 use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP};
37
38 // All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
39 const TEMP_BLOCK: BasicBlock = BasicBlock::MAX;
40
41 fn dummy_ty() -> &'static TyS<'static> {
42 thread_local! {
43 static DUMMY_TYS: &'static TyS<'static> = Box::leak(box TyS::make_for_test(
44 ty::Bool,
45 TypeFlags::empty(),
46 DebruijnIndex::from_usize(0),
47 ));
48 }
49
50 &DUMMY_TYS.with(|tys| *tys)
51 }
52
53 struct MockBlocks<'tcx> {
54 blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
55 dummy_place: Place<'tcx>,
56 next_local: usize,
57 }
58
59 impl<'tcx> MockBlocks<'tcx> {
60 fn new() -> Self {
61 Self {
62 blocks: IndexVec::new(),
63 dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() },
64 next_local: 0,
65 }
66 }
67
68 fn new_temp(&mut self) -> Local {
69 let index = self.next_local;
70 self.next_local += 1;
71 Local::new(index)
72 }
73
74 fn push(&mut self, kind: TerminatorKind<'tcx>) -> BasicBlock {
75 let next_lo = if let Some(last) = self.blocks.last() {
76 self.blocks[last].terminator().source_info.span.hi()
77 } else {
78 BytePos(1)
79 };
80 let next_hi = next_lo + BytePos(1);
81 self.blocks.push(BasicBlockData {
82 statements: vec![],
83 terminator: Some(Terminator {
84 source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)),
85 kind,
86 }),
87 is_cleanup: false,
88 })
89 }
90
91 fn link(&mut self, from_block: BasicBlock, to_block: BasicBlock) {
92 match self.blocks[from_block].terminator_mut().kind {
93 TerminatorKind::Assert { ref mut target, .. }
94 | TerminatorKind::Call { destination: Some((_, ref mut target)), .. }
95 | TerminatorKind::Drop { ref mut target, .. }
96 | TerminatorKind::DropAndReplace { ref mut target, .. }
97 | TerminatorKind::FalseEdge { real_target: ref mut target, .. }
98 | TerminatorKind::FalseUnwind { real_target: ref mut target, .. }
99 | TerminatorKind::Goto { ref mut target }
100 | TerminatorKind::InlineAsm { destination: Some(ref mut target), .. }
101 | TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block,
102 ref invalid => bug!("Invalid from_block: {:?}", invalid),
103 }
104 }
105
106 fn add_block_from(
107 &mut self,
108 some_from_block: Option<BasicBlock>,
109 to_kind: TerminatorKind<'tcx>,
110 ) -> BasicBlock {
111 let new_block = self.push(to_kind);
112 if let Some(from_block) = some_from_block {
113 self.link(from_block, new_block);
114 }
115 new_block
116 }
117
118 fn set_branch(&mut self, switchint: BasicBlock, branch_index: usize, to_block: BasicBlock) {
119 match self.blocks[switchint].terminator_mut().kind {
120 TerminatorKind::SwitchInt { ref mut targets, .. } => {
121 let mut branches = targets.iter().collect::<Vec<_>>();
122 let otherwise = if branch_index == branches.len() {
123 to_block
124 } else {
125 let old_otherwise = targets.otherwise();
126 if branch_index > branches.len() {
127 branches.push((branches.len() as u128, old_otherwise));
128 while branches.len() < branch_index {
129 branches.push((branches.len() as u128, TEMP_BLOCK));
130 }
131 to_block
132 } else {
133 branches[branch_index] = (branch_index as u128, to_block);
134 old_otherwise
135 }
136 };
137 *targets = SwitchTargets::new(branches.into_iter(), otherwise);
138 }
139 ref invalid => bug!("Invalid BasicBlock kind or no to_block: {:?}", invalid),
140 }
141 }
142
143 fn call(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
144 self.add_block_from(
145 some_from_block,
146 TerminatorKind::Call {
147 func: Operand::Copy(self.dummy_place.clone()),
148 args: vec![],
149 destination: Some((self.dummy_place.clone(), TEMP_BLOCK)),
150 cleanup: None,
151 from_hir_call: false,
152 fn_span: DUMMY_SP,
153 },
154 )
155 }
156
157 fn goto(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
158 self.add_block_from(some_from_block, TerminatorKind::Goto { target: TEMP_BLOCK })
159 }
160
161 fn switchint(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
162 let switchint_kind = TerminatorKind::SwitchInt {
163 discr: Operand::Move(Place::from(self.new_temp())),
164 switch_ty: dummy_ty(),
165 targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK),
166 };
167 self.add_block_from(some_from_block, switchint_kind)
168 }
169
170 fn return_(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
171 self.add_block_from(some_from_block, TerminatorKind::Return)
172 }
173
174 fn to_body(self) -> Body<'tcx> {
175 Body::new_cfg_only(self.blocks)
176 }
177 }
178
179 fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String {
180 format!(
181 "{:?}",
182 mir_body
183 .basic_blocks()
184 .iter_enumerated()
185 .map(|(bb, data)| {
186 let term = &data.terminator();
187 let kind = &term.kind;
188 let span = term.source_info.span;
189 let sp = format!("(span:{},{})", span.lo().to_u32(), span.hi().to_u32());
190 match kind {
191 TerminatorKind::Assert { target, .. }
192 | TerminatorKind::Call { destination: Some((_, target)), .. }
193 | TerminatorKind::Drop { target, .. }
194 | TerminatorKind::DropAndReplace { target, .. }
195 | TerminatorKind::FalseEdge { real_target: target, .. }
196 | TerminatorKind::FalseUnwind { real_target: target, .. }
197 | TerminatorKind::Goto { target }
198 | TerminatorKind::InlineAsm { destination: Some(target), .. }
199 | TerminatorKind::Yield { resume: target, .. } => {
200 format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), target)
201 }
202 TerminatorKind::SwitchInt { targets, .. } => {
203 format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), targets)
204 }
205 _ => format!("{}{:?}:{}", sp, bb, debug::term_type(kind)),
206 }
207 })
208 .collect::<Vec<_>>()
209 )
210 }
211
212 static PRINT_GRAPHS: bool = false;
213
214 fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) {
215 if PRINT_GRAPHS {
216 println!(
217 "digraph {} {{\n{}\n}}",
218 name,
219 mir_body
220 .basic_blocks()
221 .iter_enumerated()
222 .map(|(bb, data)| {
223 format!(
224 " {:?} [label=\"{:?}: {}\"];\n{}",
225 bb,
226 bb,
227 debug::term_type(&data.terminator().kind),
228 mir_body
229 .successors(bb)
230 .map(|successor| { format!(" {:?} -> {:?};", bb, successor) })
231 .collect::<Vec<_>>()
232 .join("\n")
233 )
234 })
235 .collect::<Vec<_>>()
236 .join("\n")
237 );
238 }
239 }
240
241 fn print_coverage_graphviz(
242 name: &str,
243 mir_body: &Body<'_>,
244 basic_coverage_blocks: &graph::CoverageGraph,
245 ) {
246 if PRINT_GRAPHS {
247 println!(
248 "digraph {} {{\n{}\n}}",
249 name,
250 basic_coverage_blocks
251 .iter_enumerated()
252 .map(|(bcb, bcb_data)| {
253 format!(
254 " {:?} [label=\"{:?}: {}\"];\n{}",
255 bcb,
256 bcb,
257 debug::term_type(&bcb_data.terminator(mir_body).kind),
258 basic_coverage_blocks
259 .successors(bcb)
260 .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) })
261 .collect::<Vec<_>>()
262 .join("\n")
263 )
264 })
265 .collect::<Vec<_>>()
266 .join("\n")
267 );
268 }
269 }
270
271 /// Create a mock `Body` with a simple flow.
272 fn goto_switchint() -> Body<'a> {
273 let mut blocks = MockBlocks::new();
274 let start = blocks.call(None);
275 let goto = blocks.goto(Some(start));
276 let switchint = blocks.switchint(Some(goto));
277 let then_call = blocks.call(None);
278 let else_call = blocks.call(None);
279 blocks.set_branch(switchint, 0, then_call);
280 blocks.set_branch(switchint, 1, else_call);
281 blocks.return_(Some(then_call));
282 blocks.return_(Some(else_call));
283
284 let mir_body = blocks.to_body();
285 print_mir_graphviz("mir_goto_switchint", &mir_body);
286 /* Graphviz character plots created using: `graph-easy --as=boxart`:
287 ┌────────────────┐
288 │ bb0: Call │
289 └────────────────┘
290
291
292
293 ┌────────────────┐
294 │ bb1: Goto │
295 └────────────────┘
296
297
298
299 ┌─────────────┐ ┌────────────────┐
300 │ bb4: Call │ ◀── │ bb2: SwitchInt │
301 └─────────────┘ └────────────────┘
302 │ │
303 │ │
304 ▼ ▼
305 ┌─────────────┐ ┌────────────────┐
306 │ bb6: Return │ │ bb3: Call │
307 └─────────────┘ └────────────────┘
308
309
310
311 ┌────────────────┐
312 │ bb5: Return │
313 └────────────────┘
314 */
315 mir_body
316 }
317
318 macro_rules! assert_successors {
319 ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => {
320 let mut successors = $basic_coverage_blocks.successors[$i].clone();
321 successors.sort_unstable();
322 assert_eq!(successors, vec![$($successor),*]);
323 }
324 }
325
326 #[test]
327 fn test_covgraph_goto_switchint() {
328 let mir_body = goto_switchint();
329 if false {
330 eprintln!("basic_blocks = {}", debug_basic_blocks(&mir_body));
331 }
332 let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
333 print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &basic_coverage_blocks);
334 /*
335 ┌──────────────┐ ┌─────────────────┐
336 │ bcb2: Return │ ◀── │ bcb0: SwitchInt │
337 └──────────────┘ └─────────────────┘
338
339
340
341 ┌─────────────────┐
342 │ bcb1: Return │
343 └─────────────────┘
344 */
345 assert_eq!(
346 basic_coverage_blocks.num_nodes(),
347 3,
348 "basic_coverage_blocks: {:?}",
349 basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
350 );
351
352 let_bcb!(0);
353 let_bcb!(1);
354 let_bcb!(2);
355
356 assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]);
357 assert_successors!(basic_coverage_blocks, bcb1, []);
358 assert_successors!(basic_coverage_blocks, bcb2, []);
359 }
360
361 /// Create a mock `Body` with a loop.
362 fn switchint_then_loop_else_return() -> Body<'a> {
363 let mut blocks = MockBlocks::new();
364 let start = blocks.call(None);
365 let switchint = blocks.switchint(Some(start));
366 let then_call = blocks.call(None);
367 blocks.set_branch(switchint, 0, then_call);
368 let backedge_goto = blocks.goto(Some(then_call));
369 blocks.link(backedge_goto, switchint);
370 let else_return = blocks.return_(None);
371 blocks.set_branch(switchint, 1, else_return);
372
373 let mir_body = blocks.to_body();
374 print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body);
375 /*
376 ┌────────────────┐
377 │ bb0: Call │
378 └────────────────┘
379
380
381
382 ┌─────────────┐ ┌────────────────┐
383 │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐
384 └─────────────┘ └────────────────┘ │
385 │ │
386 │ │
387 ▼ │
388 ┌────────────────┐ │
389 │ bb2: Call │ │
390 └────────────────┘ │
391 │ │
392 │ │
393 ▼ │
394 ┌────────────────┐ │
395 │ bb3: Goto │ ─┘
396 └────────────────┘
397 */
398 mir_body
399 }
400
401 #[test]
402 fn test_covgraph_switchint_then_loop_else_return() {
403 let mir_body = switchint_then_loop_else_return();
404 let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
405 print_coverage_graphviz(
406 "covgraph_switchint_then_loop_else_return",
407 &mir_body,
408 &basic_coverage_blocks,
409 );
410 /*
411 ┌─────────────────┐
412 │ bcb0: Call │
413 └─────────────────┘
414
415
416
417 ┌────────────┐ ┌─────────────────┐
418 │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐
419 └────────────┘ └─────────────────┘ │
420 │ │ │
421 │ │ │
422 │ ▼ │
423 │ ┌─────────────────┐ │
424 │ │ bcb2: Return │ │
425 │ └─────────────────┘ │
426 │ │
427 └─────────────────────────────────────┘
428 */
429 assert_eq!(
430 basic_coverage_blocks.num_nodes(),
431 4,
432 "basic_coverage_blocks: {:?}",
433 basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
434 );
435
436 let_bcb!(0);
437 let_bcb!(1);
438 let_bcb!(2);
439 let_bcb!(3);
440
441 assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
442 assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
443 assert_successors!(basic_coverage_blocks, bcb2, []);
444 assert_successors!(basic_coverage_blocks, bcb3, [bcb1]);
445 }
446
447 /// Create a mock `Body` with nested loops.
448 fn switchint_loop_then_inner_loop_else_break() -> Body<'a> {
449 let mut blocks = MockBlocks::new();
450 let start = blocks.call(None);
451 let switchint = blocks.switchint(Some(start));
452 let then_call = blocks.call(None);
453 blocks.set_branch(switchint, 0, then_call);
454 let else_return = blocks.return_(None);
455 blocks.set_branch(switchint, 1, else_return);
456
457 let inner_start = blocks.call(Some(then_call));
458 let inner_switchint = blocks.switchint(Some(inner_start));
459 let inner_then_call = blocks.call(None);
460 blocks.set_branch(inner_switchint, 0, inner_then_call);
461 let inner_backedge_goto = blocks.goto(Some(inner_then_call));
462 blocks.link(inner_backedge_goto, inner_switchint);
463 let inner_else_break_goto = blocks.goto(None);
464 blocks.set_branch(inner_switchint, 1, inner_else_break_goto);
465
466 let backedge_goto = blocks.goto(Some(inner_else_break_goto));
467 blocks.link(backedge_goto, switchint);
468
469 let mir_body = blocks.to_body();
470 print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body);
471 /*
472 ┌────────────────┐
473 │ bb0: Call │
474 └────────────────┘
475
476
477
478 ┌─────────────┐ ┌────────────────┐
479 │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐
480 └─────────────┘ └────────────────┘ │
481 │ │
482 │ │
483 ▼ │
484 ┌────────────────┐ │
485 │ bb2: Call │ │
486 └────────────────┘ │
487 │ │
488 │ │
489 ▼ │
490 ┌────────────────┐ │
491 │ bb4: Call │ │
492 └────────────────┘ │
493 │ │
494 │ │
495 ▼ │
496 ┌─────────────┐ ┌────────────────┐ │
497 │ bb8: Goto │ ◀── │ bb5: SwitchInt │ ◀┐ │
498 └─────────────┘ └────────────────┘ │ │
499 │ │ │ │
500 │ │ │ │
501 ▼ ▼ │ │
502 ┌─────────────┐ ┌────────────────┐ │ │
503 │ bb9: Goto │ ─┐ │ bb6: Call │ │ │
504 └─────────────┘ │ └────────────────┘ │ │
505 │ │ │ │
506 │ │ │ │
507 │ ▼ │ │
508 │ ┌────────────────┐ │ │
509 │ │ bb7: Goto │ ─┘ │
510 │ └────────────────┘ │
511 │ │
512 └───────────────────────────┘
513 */
514 mir_body
515 }
516
517 #[test]
518 fn test_covgraph_switchint_loop_then_inner_loop_else_break() {
519 let mir_body = switchint_loop_then_inner_loop_else_break();
520 let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
521 print_coverage_graphviz(
522 "covgraph_switchint_loop_then_inner_loop_else_break",
523 &mir_body,
524 &basic_coverage_blocks,
525 );
526 /*
527 ┌─────────────────┐
528 │ bcb0: Call │
529 └─────────────────┘
530
531
532
533 ┌──────────────┐ ┌─────────────────┐
534 │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐
535 └──────────────┘ └─────────────────┘ │
536 │ │
537 │ │
538 ▼ │
539 ┌─────────────────┐ │
540 │ bcb3: Call │ │
541 └─────────────────┘ │
542 │ │
543 │ │
544 ▼ │
545 ┌──────────────┐ ┌─────────────────┐ │
546 │ bcb6: Goto │ ◀── │ bcb4: SwitchInt │ ◀┼────┐
547 └──────────────┘ └─────────────────┘ │ │
548 │ │ │ │
549 │ │ │ │
550 │ ▼ │ │
551 │ ┌─────────────────┐ │ │
552 │ │ bcb5: Goto │ ─┘ │
553 │ └─────────────────┘ │
554 │ │
555 └────────────────────────────────────────────┘
556 */
557 assert_eq!(
558 basic_coverage_blocks.num_nodes(),
559 7,
560 "basic_coverage_blocks: {:?}",
561 basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
562 );
563
564 let_bcb!(0);
565 let_bcb!(1);
566 let_bcb!(2);
567 let_bcb!(3);
568 let_bcb!(4);
569 let_bcb!(5);
570 let_bcb!(6);
571
572 assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
573 assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
574 assert_successors!(basic_coverage_blocks, bcb2, []);
575 assert_successors!(basic_coverage_blocks, bcb3, [bcb4]);
576 assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]);
577 assert_successors!(basic_coverage_blocks, bcb5, [bcb1]);
578 assert_successors!(basic_coverage_blocks, bcb6, [bcb4]);
579 }
580
581 #[test]
582 fn test_find_loop_backedges_none() {
583 let mir_body = goto_switchint();
584 let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
585 if false {
586 eprintln!(
587 "basic_coverage_blocks = {:?}",
588 basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
589 );
590 eprintln!("successors = {:?}", basic_coverage_blocks.successors);
591 }
592 let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
593 assert_eq!(
594 backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
595 0,
596 "backedges: {:?}",
597 backedges
598 );
599 }
600
601 #[test]
602 fn test_find_loop_backedges_one() {
603 let mir_body = switchint_then_loop_else_return();
604 let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
605 let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
606 assert_eq!(
607 backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
608 1,
609 "backedges: {:?}",
610 backedges
611 );
612
613 let_bcb!(1);
614 let_bcb!(3);
615
616 assert_eq!(backedges[bcb1], vec![bcb3]);
617 }
618
619 #[test]
620 fn test_find_loop_backedges_two() {
621 let mir_body = switchint_loop_then_inner_loop_else_break();
622 let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
623 let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
624 assert_eq!(
625 backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
626 2,
627 "backedges: {:?}",
628 backedges
629 );
630
631 let_bcb!(1);
632 let_bcb!(4);
633 let_bcb!(5);
634 let_bcb!(6);
635
636 assert_eq!(backedges[bcb1], vec![bcb5]);
637 assert_eq!(backedges[bcb4], vec![bcb6]);
638 }
639
640 #[test]
641 fn test_traverse_coverage_with_loops() {
642 let mir_body = switchint_loop_then_inner_loop_else_break();
643 let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
644 let mut traversed_in_order = Vec::new();
645 let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks);
646 while let Some(bcb) = traversal.next(&basic_coverage_blocks) {
647 traversed_in_order.push(bcb);
648 }
649
650 let_bcb!(6);
651
652 // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except*
653 // bcb6 are inside the first loop.
654 assert_eq!(
655 *traversed_in_order.last().expect("should have elements"),
656 bcb6,
657 "bcb6 should not be visited until all nodes inside the first loop have been visited"
658 );
659 }
660
661 fn synthesize_body_span_from_terminators(mir_body: &Body<'_>) -> Span {
662 let mut some_span: Option<Span> = None;
663 for (_, data) in mir_body.basic_blocks().iter_enumerated() {
664 let term_span = data.terminator().source_info.span;
665 if let Some(span) = some_span.as_mut() {
666 *span = span.to(term_span);
667 } else {
668 some_span = Some(term_span)
669 }
670 }
671 some_span.expect("body must have at least one BasicBlock")
672 }
673
674 #[test]
675 fn test_make_bcb_counters() {
676 rustc_span::with_default_session_globals(|| {
677 let mir_body = goto_switchint();
678 let body_span = synthesize_body_span_from_terminators(&mir_body);
679 let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
680 let mut coverage_spans = Vec::new();
681 for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
682 if let Some(span) =
683 spans::filtered_terminator_span(data.terminator(&mir_body), body_span)
684 {
685 coverage_spans.push(spans::CoverageSpan::for_terminator(span, bcb, data.last_bb()));
686 }
687 }
688 let mut coverage_counters = counters::CoverageCounters::new(0);
689 let intermediate_expressions = coverage_counters
690 .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans)
691 .expect("should be Ok");
692 assert_eq!(intermediate_expressions.len(), 0);
693
694 let_bcb!(1);
695 assert_eq!(
696 1, // coincidentally, bcb1 has a `Counter` with id = 1
697 match basic_coverage_blocks[bcb1].counter().expect("should have a counter") {
698 CoverageKind::Counter { id, .. } => id,
699 _ => panic!("expected a Counter"),
700 }
701 .as_u32()
702 );
703
704 let_bcb!(2);
705 assert_eq!(
706 2, // coincidentally, bcb2 has a `Counter` with id = 2
707 match basic_coverage_blocks[bcb2].counter().expect("should have a counter") {
708 CoverageKind::Counter { id, .. } => id,
709 _ => panic!("expected a Counter"),
710 }
711 .as_u32()
712 );
713 });
714 }