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