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