]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir_transform/src/coverage/tests.rs
New upstream version 1.68.2+dfsg1
[rustc.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 }