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