1 //! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR
5 //! ./x.py test --keep-stage 1 compiler/rustc_mir --test-args '--show-output coverage'
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).
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).
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(); })`.
32 use coverage_test_macros
::let_bcb
;
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
::{self, Ty, TyCtxt}
;
41 use rustc_span
::{self, BytePos, Pos, Span, DUMMY_SP}
;
43 // All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
44 const TEMP_BLOCK
: BasicBlock
= BasicBlock
::MAX
;
46 struct MockBlocks
<'tcx
> {
47 blocks
: IndexVec
<BasicBlock
, BasicBlockData
<'tcx
>>,
48 dummy_place
: Place
<'tcx
>,
53 impl<'tcx
> MockBlocks
<'tcx
> {
56 blocks
: IndexVec
::new(),
57 dummy_place
: Place { local: RETURN_PLACE, projection: ty::List::empty() }
,
59 bool_ty
: TyCtxt
::BOOL_TY_FOR_UNIT_TESTING
,
63 fn new_temp(&mut self) -> Local
{
64 let index
= self.next_local
;
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()
75 let next_hi
= next_lo
+ BytePos(1);
76 self.blocks
.push(BasicBlockData
{
78 terminator
: Some(Terminator
{
79 source_info
: SourceInfo
::outermost(Span
::with_root_ctxt(next_lo
, next_hi
)),
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, .. }
89 | TerminatorKind
::Call { target: Some(ref mut target), .. }
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
),
103 some_from_block
: Option
<BasicBlock
>,
104 to_kind
: TerminatorKind
<'tcx
>,
106 let new_block
= self.push(to_kind
);
107 if let Some(from_block
) = some_from_block
{
108 self.link(from_block
, new_block
);
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() {
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
));
128 branches
[branch_index
] = (branch_index
as u128
, to_block
);
132 *targets
= SwitchTargets
::new(branches
.into_iter(), otherwise
);
134 ref invalid
=> bug
!("Invalid BasicBlock kind or no to_block: {:?}", invalid
),
138 fn call(&mut self, some_from_block
: Option
<BasicBlock
>) -> BasicBlock
{
141 TerminatorKind
::Call
{
142 func
: Operand
::Copy(self.dummy_place
.clone()),
144 destination
: self.dummy_place
.clone(),
145 target
: Some(TEMP_BLOCK
),
147 from_hir_call
: false,
153 fn goto(&mut self, some_from_block
: Option
<BasicBlock
>) -> BasicBlock
{
154 self.add_block_from(some_from_block
, TerminatorKind
::Goto { target: TEMP_BLOCK }
)
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())),
160 switch_ty
: self.bool_ty
, // just a dummy value
161 targets
: SwitchTargets
::static_if(0, TEMP_BLOCK
, TEMP_BLOCK
),
163 self.add_block_from(some_from_block
, switchint_kind
)
166 fn return_(&mut self, some_from_block
: Option
<BasicBlock
>) -> BasicBlock
{
167 self.add_block_from(some_from_block
, TerminatorKind
::Return
)
170 fn to_body(self) -> Body
<'tcx
> {
171 Body
::new_cfg_only(self.blocks
)
175 fn debug_basic_blocks
<'tcx
>(mir_body
: &Body
<'tcx
>) -> String
{
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());
187 TerminatorKind
::Assert { target, .. }
188 | TerminatorKind
::Call { target: Some(target), .. }
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
)
198 TerminatorKind
::SwitchInt { targets, .. }
=> {
199 format
!("{}{:?}:{} -> {:?}", sp
, bb
, debug
::term_type(kind
), targets
)
201 _
=> format
!("{}{:?}:{}", sp
, bb
, debug
::term_type(kind
)),
208 static PRINT_GRAPHS
: bool
= false;
210 fn print_mir_graphviz(name
: &str, mir_body
: &Body
<'_
>) {
213 "digraph {} {{\n{}\n}}",
220 " {:?} [label=\"{:?}: {}\"];\n{}",
223 debug
::term_type(&data
.terminator().kind
),
227 .map(|successor
| { format!(" {:?}
-> {:?}
;", bb, successor) })
236 fn print_coverage_graphviz(
239 basic_coverage_blocks: &graph::CoverageGraph,
243 "digraph {} {{\n{}
\n}}",
245 basic_coverage_blocks
247 .map(|(bcb, bcb_data)| {
249 " {:?}
[label
=\"{:?}
: {}
\"];\n{}
",
252 debug::term_type(&bcb_data.terminator(mir_body).kind),
253 basic_coverage_blocks
255 .map(|successor| { format!(" {:?} -> {:?};", bcb
, successor
) })
264 /// Create a mock `Body` with a simple flow.
265 fn goto_switchint
<'a
>() -> Body
<'a
> {
266 let mut blocks
= MockBlocks
::new();
267 let start
= blocks
.call(None
);
268 let goto
= blocks
.goto(Some(start
));
269 let switchint
= blocks
.switchint(Some(goto
));
270 let then_call
= blocks
.call(None
);
271 let else_call
= blocks
.call(None
);
272 blocks
.set_branch(switchint
, 0, then_call
);
273 blocks
.set_branch(switchint
, 1, else_call
);
274 blocks
.return_(Some(then_call
));
275 blocks
.return_(Some(else_call
));
277 let mir_body
= blocks
.to_body();
278 print_mir_graphviz("mir_goto_switchint", &mir_body
);
279 /* Graphviz character plots created using: `graph-easy --as=boxart`:
292 ┌─────────────┐ ┌────────────────┐
293 │ bb4: Call │ ◀── │ bb2: SwitchInt │
294 └─────────────┘ └────────────────┘
298 ┌─────────────┐ ┌────────────────┐
299 │ bb6: Return │ │ bb3: Call │
300 └─────────────┘ └────────────────┘
311 macro_rules
! assert_successors
{
312 ($basic_coverage_blocks
:ident
, $i
:ident
, [$
($successor
:ident
),*]) => {
313 let mut successors
= $basic_coverage_blocks
.successors
[$i
].clone();
314 successors
.sort_unstable();
315 assert_eq
!(successors
, vec
![$
($successor
),*]);
320 fn test_covgraph_goto_switchint() {
321 let mir_body
= goto_switchint();
323 eprintln
!("basic_blocks = {}", debug_basic_blocks(&mir_body
));
325 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
326 print_coverage_graphviz("covgraph_goto_switchint ", &mir_body
, &basic_coverage_blocks
);
328 ┌──────────────┐ ┌─────────────────┐
329 │ bcb2: Return │ ◀── │ bcb0: SwitchInt │
330 └──────────────┘ └─────────────────┘
339 basic_coverage_blocks
.num_nodes(),
341 "basic_coverage_blocks: {:?}",
342 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
349 assert_successors
!(basic_coverage_blocks
, bcb0
, [bcb1
, bcb2
]);
350 assert_successors
!(basic_coverage_blocks
, bcb1
, []);
351 assert_successors
!(basic_coverage_blocks
, bcb2
, []);
354 /// Create a mock `Body` with a loop.
355 fn switchint_then_loop_else_return
<'a
>() -> Body
<'a
> {
356 let mut blocks
= MockBlocks
::new();
357 let start
= blocks
.call(None
);
358 let switchint
= blocks
.switchint(Some(start
));
359 let then_call
= blocks
.call(None
);
360 blocks
.set_branch(switchint
, 0, then_call
);
361 let backedge_goto
= blocks
.goto(Some(then_call
));
362 blocks
.link(backedge_goto
, switchint
);
363 let else_return
= blocks
.return_(None
);
364 blocks
.set_branch(switchint
, 1, else_return
);
366 let mir_body
= blocks
.to_body();
367 print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body
);
375 ┌─────────────┐ ┌────────────────┐
376 │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐
377 └─────────────┘ └────────────────┘ │
395 fn test_covgraph_switchint_then_loop_else_return() {
396 let mir_body
= switchint_then_loop_else_return();
397 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
398 print_coverage_graphviz(
399 "covgraph_switchint_then_loop_else_return",
401 &basic_coverage_blocks
,
410 ┌────────────┐ ┌─────────────────┐
411 │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐
412 └────────────┘ └─────────────────┘ │
416 │ ┌─────────────────┐ │
418 │ └─────────────────┘ │
420 └─────────────────────────────────────┘
423 basic_coverage_blocks
.num_nodes(),
425 "basic_coverage_blocks: {:?}",
426 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
434 assert_successors
!(basic_coverage_blocks
, bcb0
, [bcb1
]);
435 assert_successors
!(basic_coverage_blocks
, bcb1
, [bcb2
, bcb3
]);
436 assert_successors
!(basic_coverage_blocks
, bcb2
, []);
437 assert_successors
!(basic_coverage_blocks
, bcb3
, [bcb1
]);
440 /// Create a mock `Body` with nested loops.
441 fn switchint_loop_then_inner_loop_else_break
<'a
>() -> Body
<'a
> {
442 let mut blocks
= MockBlocks
::new();
443 let start
= blocks
.call(None
);
444 let switchint
= blocks
.switchint(Some(start
));
445 let then_call
= blocks
.call(None
);
446 blocks
.set_branch(switchint
, 0, then_call
);
447 let else_return
= blocks
.return_(None
);
448 blocks
.set_branch(switchint
, 1, else_return
);
450 let inner_start
= blocks
.call(Some(then_call
));
451 let inner_switchint
= blocks
.switchint(Some(inner_start
));
452 let inner_then_call
= blocks
.call(None
);
453 blocks
.set_branch(inner_switchint
, 0, inner_then_call
);
454 let inner_backedge_goto
= blocks
.goto(Some(inner_then_call
));
455 blocks
.link(inner_backedge_goto
, inner_switchint
);
456 let inner_else_break_goto
= blocks
.goto(None
);
457 blocks
.set_branch(inner_switchint
, 1, inner_else_break_goto
);
459 let backedge_goto
= blocks
.goto(Some(inner_else_break_goto
));
460 blocks
.link(backedge_goto
, switchint
);
462 let mir_body
= blocks
.to_body();
463 print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body
);
471 ┌─────────────┐ ┌────────────────┐
472 │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐
473 └─────────────┘ └────────────────┘ │
489 ┌─────────────┐ ┌────────────────┐ │
490 │ bb8: Goto │ ◀── │ bb5: SwitchInt │ ◀┐ │
491 └─────────────┘ └────────────────┘ │ │
495 ┌─────────────┐ ┌────────────────┐ │ │
496 │ bb9: Goto │ ─┐ │ bb6: Call │ │ │
497 └─────────────┘ │ └────────────────┘ │ │
501 │ ┌────────────────┐ │ │
503 │ └────────────────┘ │
505 └───────────────────────────┘
511 fn test_covgraph_switchint_loop_then_inner_loop_else_break() {
512 let mir_body
= switchint_loop_then_inner_loop_else_break();
513 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
514 print_coverage_graphviz(
515 "covgraph_switchint_loop_then_inner_loop_else_break",
517 &basic_coverage_blocks
,
526 ┌──────────────┐ ┌─────────────────┐
527 │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐
528 └──────────────┘ └─────────────────┘ │
532 ┌─────────────────┐ │
534 └─────────────────┘ │
538 ┌──────────────┐ ┌─────────────────┐ │
539 │ bcb6: Goto │ ◀── │ bcb4: SwitchInt │ ◀┼────┐
540 └──────────────┘ └─────────────────┘ │ │
544 │ ┌─────────────────┐ │ │
545 │ │ bcb5: Goto │ ─┘ │
546 │ └─────────────────┘ │
548 └────────────────────────────────────────────┘
551 basic_coverage_blocks
.num_nodes(),
553 "basic_coverage_blocks: {:?}",
554 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
565 assert_successors
!(basic_coverage_blocks
, bcb0
, [bcb1
]);
566 assert_successors
!(basic_coverage_blocks
, bcb1
, [bcb2
, bcb3
]);
567 assert_successors
!(basic_coverage_blocks
, bcb2
, []);
568 assert_successors
!(basic_coverage_blocks
, bcb3
, [bcb4
]);
569 assert_successors
!(basic_coverage_blocks
, bcb4
, [bcb5
, bcb6
]);
570 assert_successors
!(basic_coverage_blocks
, bcb5
, [bcb1
]);
571 assert_successors
!(basic_coverage_blocks
, bcb6
, [bcb4
]);
575 fn test_find_loop_backedges_none() {
576 let mir_body
= goto_switchint();
577 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
580 "basic_coverage_blocks = {:?}",
581 basic_coverage_blocks
.iter_enumerated().collect
::<Vec
<_
>>()
583 eprintln
!("successors = {:?}", basic_coverage_blocks
.successors
);
585 let backedges
= graph
::find_loop_backedges(&basic_coverage_blocks
);
587 backedges
.iter_enumerated().map(|(_bcb
, backedges
)| backedges
.len()).sum
::<usize>(),
595 fn test_find_loop_backedges_one() {
596 let mir_body
= switchint_then_loop_else_return();
597 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
598 let backedges
= graph
::find_loop_backedges(&basic_coverage_blocks
);
600 backedges
.iter_enumerated().map(|(_bcb
, backedges
)| backedges
.len()).sum
::<usize>(),
609 assert_eq
!(backedges
[bcb1
], vec
![bcb3
]);
613 fn test_find_loop_backedges_two() {
614 let mir_body
= switchint_loop_then_inner_loop_else_break();
615 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
616 let backedges
= graph
::find_loop_backedges(&basic_coverage_blocks
);
618 backedges
.iter_enumerated().map(|(_bcb
, backedges
)| backedges
.len()).sum
::<usize>(),
629 assert_eq
!(backedges
[bcb1
], vec
![bcb5
]);
630 assert_eq
!(backedges
[bcb4
], vec
![bcb6
]);
634 fn test_traverse_coverage_with_loops() {
635 let mir_body
= switchint_loop_then_inner_loop_else_break();
636 let basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
637 let mut traversed_in_order
= Vec
::new();
638 let mut traversal
= graph
::TraverseCoverageGraphWithLoops
::new(&basic_coverage_blocks
);
639 while let Some(bcb
) = traversal
.next(&basic_coverage_blocks
) {
640 traversed_in_order
.push(bcb
);
645 // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except*
646 // bcb6 are inside the first loop.
648 *traversed_in_order
.last().expect("should have elements"),
650 "bcb6 should not be visited until all nodes inside the first loop have been visited"
654 fn synthesize_body_span_from_terminators(mir_body
: &Body
<'_
>) -> Span
{
655 let mut some_span
: Option
<Span
> = None
;
656 for (_
, data
) in mir_body
.basic_blocks().iter_enumerated() {
657 let term_span
= data
.terminator().source_info
.span
;
658 if let Some(span
) = some_span
.as_mut() {
659 *span
= span
.to(term_span
);
661 some_span
= Some(term_span
)
664 some_span
.expect("body must have at least one BasicBlock")
668 fn test_make_bcb_counters() {
669 rustc_span
::create_default_session_globals_then(|| {
670 let mir_body
= goto_switchint();
671 let body_span
= synthesize_body_span_from_terminators(&mir_body
);
672 let mut basic_coverage_blocks
= graph
::CoverageGraph
::from_mir(&mir_body
);
673 let mut coverage_spans
= Vec
::new();
674 for (bcb
, data
) in basic_coverage_blocks
.iter_enumerated() {
675 if let Some(span
) = spans
::filtered_terminator_span(data
.terminator(&mir_body
)) {
676 coverage_spans
.push(spans
::CoverageSpan
::for_terminator(
677 spans
::function_source_span(span
, body_span
),
684 let mut coverage_counters
= counters
::CoverageCounters
::new(0);
685 let intermediate_expressions
= coverage_counters
686 .make_bcb_counters(&mut basic_coverage_blocks
, &coverage_spans
)
687 .expect("should be Ok");
688 assert_eq
!(intermediate_expressions
.len(), 0);
692 1, // coincidentally, bcb1 has a `Counter` with id = 1
693 match basic_coverage_blocks
[bcb1
].counter().expect("should have a counter") {
694 CoverageKind
::Counter { id, .. }
=> id
,
695 _
=> panic
!("expected a Counter"),
702 2, // coincidentally, bcb2 has a `Counter` with id = 2
703 match basic_coverage_blocks
[bcb2
].counter().expect("should have a counter") {
704 CoverageKind
::Counter { id, .. }
=> id
,
705 _
=> panic
!("expected a Counter"),