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