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