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