]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/coverage/tests.rs
Rollup merge of #104046 - RalfJung:run-miri-run, r=oli-obk
[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                             .basic_blocks
226                             .successors(bb)
227                             .map(|successor| { format!("    {:?} -> {:?};", bb, successor) })
228                             .join("\n")
229                     )
230                 })
231                 .join("\n")
232         );
233     }
234 }
235
236 fn print_coverage_graphviz(
237     name: &str,
238     mir_body: &Body<'_>,
239     basic_coverage_blocks: &graph::CoverageGraph,
240 ) {
241     if PRINT_GRAPHS {
242         println!(
243             "digraph {} {{\n{}\n}}",
244             name,
245             basic_coverage_blocks
246                 .iter_enumerated()
247                 .map(|(bcb, bcb_data)| {
248                     format!(
249                         "    {:?} [label=\"{:?}: {}\"];\n{}",
250                         bcb,
251                         bcb,
252                         debug::term_type(&bcb_data.terminator(mir_body).kind),
253                         basic_coverage_blocks
254                             .successors(bcb)
255                             .map(|successor| { format!("    {:?} -> {:?};", bcb, successor) })
256                             .join("\n")
257                     )
258                 })
259                 .join("\n")
260         );
261     }
262 }
263
264 /// Create a mock `Body` with a simple flow.
265 fn goto_switchint<'a>() -> Body<'a> {
266     let mut blocks = MockBlocks::new();
267     let start = blocks.call(None);
268     let goto = blocks.goto(Some(start));
269     let switchint = blocks.switchint(Some(goto));
270     let then_call = blocks.call(None);
271     let else_call = blocks.call(None);
272     blocks.set_branch(switchint, 0, then_call);
273     blocks.set_branch(switchint, 1, else_call);
274     blocks.return_(Some(then_call));
275     blocks.return_(Some(else_call));
276
277     let mir_body = blocks.to_body();
278     print_mir_graphviz("mir_goto_switchint", &mir_body);
279     /* Graphviz character plots created using: `graph-easy --as=boxart`:
280                         ┌────────────────┐
281                         │   bb0: Call    │
282                         └────────────────┘
283                           │
284                           │
285                           ▼
286                         ┌────────────────┐
287                         │   bb1: Goto    │
288                         └────────────────┘
289                           │
290                           │
291                           ▼
292     ┌─────────────┐     ┌────────────────┐
293     │  bb4: Call  │ ◀── │ bb2: SwitchInt │
294     └─────────────┘     └────────────────┘
295       │                   │
296       │                   │
297       ▼                   ▼
298     ┌─────────────┐     ┌────────────────┐
299     │ bb6: Return │     │   bb3: Call    │
300     └─────────────┘     └────────────────┘
301                           │
302                           │
303                           ▼
304                         ┌────────────────┐
305                         │  bb5: Return   │
306                         └────────────────┘
307     */
308     mir_body
309 }
310
311 macro_rules! assert_successors {
312     ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => {
313         let mut successors = $basic_coverage_blocks.successors[$i].clone();
314         successors.sort_unstable();
315         assert_eq!(successors, vec![$($successor),*]);
316     }
317 }
318
319 #[test]
320 fn test_covgraph_goto_switchint() {
321     let mir_body = goto_switchint();
322     if false {
323         eprintln!("basic_blocks = {}", debug_basic_blocks(&mir_body));
324     }
325     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
326     print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &basic_coverage_blocks);
327     /*
328     ┌──────────────┐     ┌─────────────────┐
329     │ bcb2: Return │ ◀── │ bcb0: SwitchInt │
330     └──────────────┘     └─────────────────┘
331                            │
332                            │
333                            ▼
334                          ┌─────────────────┐
335                          │  bcb1: Return   │
336                          └─────────────────┘
337     */
338     assert_eq!(
339         basic_coverage_blocks.num_nodes(),
340         3,
341         "basic_coverage_blocks: {:?}",
342         basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
343     );
344
345     let_bcb!(0);
346     let_bcb!(1);
347     let_bcb!(2);
348
349     assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]);
350     assert_successors!(basic_coverage_blocks, bcb1, []);
351     assert_successors!(basic_coverage_blocks, bcb2, []);
352 }
353
354 /// Create a mock `Body` with a loop.
355 fn switchint_then_loop_else_return<'a>() -> Body<'a> {
356     let mut blocks = MockBlocks::new();
357     let start = blocks.call(None);
358     let switchint = blocks.switchint(Some(start));
359     let then_call = blocks.call(None);
360     blocks.set_branch(switchint, 0, then_call);
361     let backedge_goto = blocks.goto(Some(then_call));
362     blocks.link(backedge_goto, switchint);
363     let else_return = blocks.return_(None);
364     blocks.set_branch(switchint, 1, else_return);
365
366     let mir_body = blocks.to_body();
367     print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body);
368     /*
369                         ┌────────────────┐
370                         │   bb0: Call    │
371                         └────────────────┘
372                           │
373                           │
374                           ▼
375     ┌─────────────┐     ┌────────────────┐
376     │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐
377     └─────────────┘     └────────────────┘  │
378                           │                 │
379                           │                 │
380                           ▼                 │
381                         ┌────────────────┐  │
382                         │   bb2: Call    │  │
383                         └────────────────┘  │
384                           │                 │
385                           │                 │
386                           ▼                 │
387                         ┌────────────────┐  │
388                         │   bb3: Goto    │ ─┘
389                         └────────────────┘
390     */
391     mir_body
392 }
393
394 #[test]
395 fn test_covgraph_switchint_then_loop_else_return() {
396     let mir_body = switchint_then_loop_else_return();
397     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
398     print_coverage_graphviz(
399         "covgraph_switchint_then_loop_else_return",
400         &mir_body,
401         &basic_coverage_blocks,
402     );
403     /*
404                        ┌─────────────────┐
405                        │   bcb0: Call    │
406                        └─────────────────┘
407                          │
408                          │
409                          ▼
410     ┌────────────┐     ┌─────────────────┐
411     │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐
412     └────────────┘     └─────────────────┘  │
413       │                  │                  │
414       │                  │                  │
415       │                  ▼                  │
416       │                ┌─────────────────┐  │
417       │                │  bcb2: Return   │  │
418       │                └─────────────────┘  │
419       │                                     │
420       └─────────────────────────────────────┘
421     */
422     assert_eq!(
423         basic_coverage_blocks.num_nodes(),
424         4,
425         "basic_coverage_blocks: {:?}",
426         basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
427     );
428
429     let_bcb!(0);
430     let_bcb!(1);
431     let_bcb!(2);
432     let_bcb!(3);
433
434     assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
435     assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
436     assert_successors!(basic_coverage_blocks, bcb2, []);
437     assert_successors!(basic_coverage_blocks, bcb3, [bcb1]);
438 }
439
440 /// Create a mock `Body` with nested loops.
441 fn switchint_loop_then_inner_loop_else_break<'a>() -> Body<'a> {
442     let mut blocks = MockBlocks::new();
443     let start = blocks.call(None);
444     let switchint = blocks.switchint(Some(start));
445     let then_call = blocks.call(None);
446     blocks.set_branch(switchint, 0, then_call);
447     let else_return = blocks.return_(None);
448     blocks.set_branch(switchint, 1, else_return);
449
450     let inner_start = blocks.call(Some(then_call));
451     let inner_switchint = blocks.switchint(Some(inner_start));
452     let inner_then_call = blocks.call(None);
453     blocks.set_branch(inner_switchint, 0, inner_then_call);
454     let inner_backedge_goto = blocks.goto(Some(inner_then_call));
455     blocks.link(inner_backedge_goto, inner_switchint);
456     let inner_else_break_goto = blocks.goto(None);
457     blocks.set_branch(inner_switchint, 1, inner_else_break_goto);
458
459     let backedge_goto = blocks.goto(Some(inner_else_break_goto));
460     blocks.link(backedge_goto, switchint);
461
462     let mir_body = blocks.to_body();
463     print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body);
464     /*
465                         ┌────────────────┐
466                         │   bb0: Call    │
467                         └────────────────┘
468                           │
469                           │
470                           ▼
471     ┌─────────────┐     ┌────────────────┐
472     │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐
473     └─────────────┘     └────────────────┘       │
474                           │                      │
475                           │                      │
476                           ▼                      │
477                         ┌────────────────┐       │
478                         │   bb2: Call    │       │
479                         └────────────────┘       │
480                           │                      │
481                           │                      │
482                           ▼                      │
483                         ┌────────────────┐       │
484                         │   bb4: Call    │       │
485                         └────────────────┘       │
486                           │                      │
487                           │                      │
488                           ▼                      │
489     ┌─────────────┐     ┌────────────────┐       │
490     │  bb8: Goto  │ ◀── │ bb5: SwitchInt │ ◀┐    │
491     └─────────────┘     └────────────────┘  │    │
492       │                   │                 │    │
493       │                   │                 │    │
494       ▼                   ▼                 │    │
495     ┌─────────────┐     ┌────────────────┐  │    │
496     │  bb9: Goto  │ ─┐  │   bb6: Call    │  │    │
497     └─────────────┘  │  └────────────────┘  │    │
498                      │    │                 │    │
499                      │    │                 │    │
500                      │    ▼                 │    │
501                      │  ┌────────────────┐  │    │
502                      │  │   bb7: Goto    │ ─┘    │
503                      │  └────────────────┘       │
504                      │                           │
505                      └───────────────────────────┘
506     */
507     mir_body
508 }
509
510 #[test]
511 fn test_covgraph_switchint_loop_then_inner_loop_else_break() {
512     let mir_body = switchint_loop_then_inner_loop_else_break();
513     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
514     print_coverage_graphviz(
515         "covgraph_switchint_loop_then_inner_loop_else_break",
516         &mir_body,
517         &basic_coverage_blocks,
518     );
519     /*
520                          ┌─────────────────┐
521                          │   bcb0: Call    │
522                          └─────────────────┘
523                            │
524                            │
525                            ▼
526     ┌──────────────┐     ┌─────────────────┐
527     │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐
528     └──────────────┘     └─────────────────┘  │
529                            │                  │
530                            │                  │
531                            ▼                  │
532                          ┌─────────────────┐  │
533                          │   bcb3: Call    │  │
534                          └─────────────────┘  │
535                            │                  │
536                            │                  │
537                            ▼                  │
538     ┌──────────────┐     ┌─────────────────┐  │
539     │  bcb6: Goto  │ ◀── │ bcb4: SwitchInt │ ◀┼────┐
540     └──────────────┘     └─────────────────┘  │    │
541       │                    │                  │    │
542       │                    │                  │    │
543       │                    ▼                  │    │
544       │                  ┌─────────────────┐  │    │
545       │                  │   bcb5: Goto    │ ─┘    │
546       │                  └─────────────────┘       │
547       │                                            │
548       └────────────────────────────────────────────┘
549     */
550     assert_eq!(
551         basic_coverage_blocks.num_nodes(),
552         7,
553         "basic_coverage_blocks: {:?}",
554         basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
555     );
556
557     let_bcb!(0);
558     let_bcb!(1);
559     let_bcb!(2);
560     let_bcb!(3);
561     let_bcb!(4);
562     let_bcb!(5);
563     let_bcb!(6);
564
565     assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
566     assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
567     assert_successors!(basic_coverage_blocks, bcb2, []);
568     assert_successors!(basic_coverage_blocks, bcb3, [bcb4]);
569     assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]);
570     assert_successors!(basic_coverage_blocks, bcb5, [bcb1]);
571     assert_successors!(basic_coverage_blocks, bcb6, [bcb4]);
572 }
573
574 #[test]
575 fn test_find_loop_backedges_none() {
576     let mir_body = goto_switchint();
577     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
578     if false {
579         eprintln!(
580             "basic_coverage_blocks = {:?}",
581             basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
582         );
583         eprintln!("successors = {:?}", basic_coverage_blocks.successors);
584     }
585     let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
586     assert_eq!(
587         backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
588         0,
589         "backedges: {:?}",
590         backedges
591     );
592 }
593
594 #[test]
595 fn test_find_loop_backedges_one() {
596     let mir_body = switchint_then_loop_else_return();
597     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
598     let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
599     assert_eq!(
600         backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
601         1,
602         "backedges: {:?}",
603         backedges
604     );
605
606     let_bcb!(1);
607     let_bcb!(3);
608
609     assert_eq!(backedges[bcb1], vec![bcb3]);
610 }
611
612 #[test]
613 fn test_find_loop_backedges_two() {
614     let mir_body = switchint_loop_then_inner_loop_else_break();
615     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
616     let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
617     assert_eq!(
618         backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
619         2,
620         "backedges: {:?}",
621         backedges
622     );
623
624     let_bcb!(1);
625     let_bcb!(4);
626     let_bcb!(5);
627     let_bcb!(6);
628
629     assert_eq!(backedges[bcb1], vec![bcb5]);
630     assert_eq!(backedges[bcb4], vec![bcb6]);
631 }
632
633 #[test]
634 fn test_traverse_coverage_with_loops() {
635     let mir_body = switchint_loop_then_inner_loop_else_break();
636     let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
637     let mut traversed_in_order = Vec::new();
638     let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks);
639     while let Some(bcb) = traversal.next(&basic_coverage_blocks) {
640         traversed_in_order.push(bcb);
641     }
642
643     let_bcb!(6);
644
645     // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except*
646     // bcb6 are inside the first loop.
647     assert_eq!(
648         *traversed_in_order.last().expect("should have elements"),
649         bcb6,
650         "bcb6 should not be visited until all nodes inside the first loop have been visited"
651     );
652 }
653
654 fn synthesize_body_span_from_terminators(mir_body: &Body<'_>) -> Span {
655     let mut some_span: Option<Span> = None;
656     for (_, data) in mir_body.basic_blocks.iter_enumerated() {
657         let term_span = data.terminator().source_info.span;
658         if let Some(span) = some_span.as_mut() {
659             *span = span.to(term_span);
660         } else {
661             some_span = Some(term_span)
662         }
663     }
664     some_span.expect("body must have at least one BasicBlock")
665 }
666
667 #[test]
668 fn test_make_bcb_counters() {
669     rustc_span::create_default_session_globals_then(|| {
670         let mir_body = goto_switchint();
671         let body_span = synthesize_body_span_from_terminators(&mir_body);
672         let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
673         let mut coverage_spans = Vec::new();
674         for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
675             if let Some(span) = spans::filtered_terminator_span(data.terminator(&mir_body)) {
676                 coverage_spans.push(spans::CoverageSpan::for_terminator(
677                     spans::function_source_span(span, body_span),
678                     span,
679                     bcb,
680                     data.last_bb(),
681                 ));
682             }
683         }
684         let mut coverage_counters = counters::CoverageCounters::new(0);
685         let intermediate_expressions = coverage_counters
686             .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans)
687             .expect("should be Ok");
688         assert_eq!(intermediate_expressions.len(), 0);
689
690         let_bcb!(1);
691         assert_eq!(
692             1, // coincidentally, bcb1 has a `Counter` with id = 1
693             match basic_coverage_blocks[bcb1].counter().expect("should have a counter") {
694                 CoverageKind::Counter { id, .. } => id,
695                 _ => panic!("expected a Counter"),
696             }
697             .as_u32()
698         );
699
700         let_bcb!(2);
701         assert_eq!(
702             2, // coincidentally, bcb2 has a `Counter` with id = 2
703             match basic_coverage_blocks[bcb2].counter().expect("should have a counter") {
704                 CoverageKind::Counter { id, .. } => id,
705                 _ => panic!("expected a Counter"),
706             }
707             .as_u32()
708         );
709     });
710 }