Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5c37696

Browse files
committedOct 10, 2023
Auto merge of #116409 - Zalathar:span-extraction, r=oli-obk
coverage: Separate initial span extraction from span processing One of the main subtasks of coverage instrumentation is looking through MIR to determine a list of source code spans that require coverage counters. That task is in turn subdivided into a few main steps: - Getting the initial spans from MIR statements/terminators - Processing the list of spans to merge or truncate nearby spans as necessary - Grouping the processed spans by their corresponding coverage graph node --- This PR enforces a firmer separation between the first two steps (span extraction and span processing), which ends up slightly simplifying both steps, since they don't need to deal with state that is only meaningful for the other step. --- `@rustbot` label +A-code-coverage
2 parents 091bb74 + 6c44425 commit 5c37696

File tree

2 files changed

+215
-237
lines changed

2 files changed

+215
-237
lines changed
 

‎compiler/rustc_mir_transform/src/coverage/spans.rs

Lines changed: 31 additions & 237 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB};
1+
use std::cell::OnceCell;
22

33
use rustc_data_structures::graph::WithNumNodes;
44
use rustc_index::IndexVec;
5-
use rustc_middle::mir::{
6-
self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
7-
TerminatorKind,
8-
};
9-
use rustc_span::source_map::original_sp;
5+
use rustc_middle::mir::{self, AggregateKind, Rvalue, Statement, StatementKind};
106
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
117

12-
use std::cell::OnceCell;
8+
use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
9+
10+
mod from_mir;
1311

1412
pub(super) struct CoverageSpans {
1513
/// Map from BCBs to their list of coverage spans.
@@ -53,27 +51,13 @@ impl CoverageSpans {
5351
}
5452
}
5553

56-
#[derive(Debug, Copy, Clone)]
57-
pub(super) enum CoverageStatement {
58-
Statement(BasicBlock, Span, usize),
59-
Terminator(BasicBlock, Span),
60-
}
61-
62-
impl CoverageStatement {
63-
pub fn span(&self) -> Span {
64-
match self {
65-
Self::Statement(_, span, _) | Self::Terminator(_, span) => *span,
66-
}
67-
}
68-
}
69-
7054
/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that
7155
/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s.
7256
/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent
7357
/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the
74-
/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s.
58+
/// `merged_spans` vectors, and the `Span`s to cover the extent of the combined `Span`s.
7559
///
76-
/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that
60+
/// Note: A span merged into another CoverageSpan may come from a `BasicBlock` that
7761
/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches
7862
/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock`
7963
/// `dominates()` the `BasicBlock`s in this `CoverageSpan`.
@@ -83,7 +67,9 @@ struct CoverageSpan {
8367
pub expn_span: Span,
8468
pub current_macro_or_none: OnceCell<Option<Symbol>>,
8569
pub bcb: BasicCoverageBlock,
86-
pub coverage_statements: Vec<CoverageStatement>,
70+
/// List of all the original spans from MIR that have been merged into this
71+
/// span. Mainly used to precisely skip over gaps when truncating a span.
72+
pub merged_spans: Vec<Span>,
8773
pub is_closure: bool,
8874
}
8975

@@ -94,7 +80,7 @@ impl CoverageSpan {
9480
expn_span: fn_sig_span,
9581
current_macro_or_none: Default::default(),
9682
bcb: START_BCB,
97-
coverage_statements: vec![],
83+
merged_spans: vec![],
9884
is_closure: false,
9985
}
10086
}
@@ -104,8 +90,6 @@ impl CoverageSpan {
10490
span: Span,
10591
expn_span: Span,
10692
bcb: BasicCoverageBlock,
107-
bb: BasicBlock,
108-
stmt_index: usize,
10993
) -> Self {
11094
let is_closure = match statement.kind {
11195
StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => {
@@ -119,39 +103,32 @@ impl CoverageSpan {
119103
expn_span,
120104
current_macro_or_none: Default::default(),
121105
bcb,
122-
coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
106+
merged_spans: vec![span],
123107
is_closure,
124108
}
125109
}
126110

127-
pub fn for_terminator(
128-
span: Span,
129-
expn_span: Span,
130-
bcb: BasicCoverageBlock,
131-
bb: BasicBlock,
132-
) -> Self {
111+
pub fn for_terminator(span: Span, expn_span: Span, bcb: BasicCoverageBlock) -> Self {
133112
Self {
134113
span,
135114
expn_span,
136115
current_macro_or_none: Default::default(),
137116
bcb,
138-
coverage_statements: vec![CoverageStatement::Terminator(bb, span)],
117+
merged_spans: vec![span],
139118
is_closure: false,
140119
}
141120
}
142121

143122
pub fn merge_from(&mut self, mut other: CoverageSpan) {
144123
debug_assert!(self.is_mergeable(&other));
145124
self.span = self.span.to(other.span);
146-
self.coverage_statements.append(&mut other.coverage_statements);
125+
self.merged_spans.append(&mut other.merged_spans);
147126
}
148127

149128
pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
150-
self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos);
151-
if let Some(highest_covstmt) =
152-
self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi())
153-
{
154-
self.span = self.span.with_hi(highest_covstmt.span().hi());
129+
self.merged_spans.retain(|span| span.hi() <= cutoff_pos);
130+
if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() {
131+
self.span = self.span.with_hi(max_hi);
155132
}
156133
}
157134

@@ -205,13 +182,7 @@ impl CoverageSpan {
205182
/// * Merge spans that represent continuous (both in source code and control flow), non-branching
206183
/// execution
207184
/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
208-
struct CoverageSpansGenerator<'a, 'tcx> {
209-
/// The MIR, used to look up `BasicBlockData`.
210-
mir_body: &'a mir::Body<'tcx>,
211-
212-
/// A `Span` covering the signature of function for the MIR.
213-
fn_sig_span: Span,
214-
185+
struct CoverageSpansGenerator<'a> {
215186
/// A `Span` covering the function body of the MIR (typically from left curly brace to right
216187
/// curly brace).
217188
body_span: Span,
@@ -221,7 +192,7 @@ struct CoverageSpansGenerator<'a, 'tcx> {
221192

222193
/// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative
223194
/// dominance between the `BasicCoverageBlock`s of equal `Span`s.
224-
sorted_spans_iter: Option<std::vec::IntoIter<CoverageSpan>>,
195+
sorted_spans_iter: std::vec::IntoIter<CoverageSpan>,
225196

226197
/// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the
227198
/// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to
@@ -261,7 +232,7 @@ struct CoverageSpansGenerator<'a, 'tcx> {
261232
refined_spans: Vec<CoverageSpan>,
262233
}
263234

264-
impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
235+
impl<'a> CoverageSpansGenerator<'a> {
265236
/// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be
266237
/// counted.
267238
///
@@ -284,17 +255,22 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
284255
/// Note the resulting vector of `CoverageSpan`s may not be fully sorted (and does not need
285256
/// to be).
286257
pub(super) fn generate_coverage_spans(
287-
mir_body: &'a mir::Body<'tcx>,
258+
mir_body: &mir::Body<'_>,
288259
fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span`
289260
body_span: Span,
290261
basic_coverage_blocks: &'a CoverageGraph,
291262
) -> Vec<CoverageSpan> {
292-
let mut coverage_spans = Self {
263+
let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans(
293264
mir_body,
294265
fn_sig_span,
295266
body_span,
296267
basic_coverage_blocks,
297-
sorted_spans_iter: None,
268+
);
269+
270+
let coverage_spans = Self {
271+
body_span,
272+
basic_coverage_blocks,
273+
sorted_spans_iter: sorted_spans.into_iter(),
298274
refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2),
299275
some_curr: None,
300276
curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
@@ -304,48 +280,9 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
304280
pending_dups: Vec::new(),
305281
};
306282

307-
let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans();
308-
309-
coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter());
310-
311283
coverage_spans.to_refined_spans()
312284
}
313285

314-
fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> {
315-
let mut initial_spans =
316-
Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks.len() * 2);
317-
for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() {
318-
initial_spans.extend(self.bcb_to_initial_coverage_spans(bcb, bcb_data));
319-
}
320-
321-
if initial_spans.is_empty() {
322-
// This can happen if, for example, the function is unreachable (contains only a
323-
// `BasicBlock`(s) with an `Unreachable` terminator).
324-
return initial_spans;
325-
}
326-
327-
initial_spans.push(CoverageSpan::for_fn_sig(self.fn_sig_span));
328-
329-
initial_spans.sort_by(|a, b| {
330-
// First sort by span start.
331-
Ord::cmp(&a.span.lo(), &b.span.lo())
332-
// If span starts are the same, sort by span end in reverse order.
333-
// This ensures that if spans A and B are adjacent in the list,
334-
// and they overlap but are not equal, then either:
335-
// - Span A extends further left, or
336-
// - Both have the same start and span A extends further right
337-
.then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
338-
// If both spans are equal, sort the BCBs in dominator order,
339-
// so that dominating BCBs come before other BCBs they dominate.
340-
.then_with(|| self.basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
341-
// If two spans are otherwise identical, put closure spans first,
342-
// as this seems to be what the refinement step expects.
343-
.then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
344-
});
345-
346-
initial_spans
347-
}
348-
349286
/// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and
350287
/// de-duplicated `CoverageSpan`s.
351288
fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
@@ -485,48 +422,6 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
485422
}
486423
}
487424

488-
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
489-
// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
490-
// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
491-
// merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple
492-
// `Statement`s and/or `Terminator`s.)
493-
fn bcb_to_initial_coverage_spans(
494-
&self,
495-
bcb: BasicCoverageBlock,
496-
bcb_data: &'a BasicCoverageBlockData,
497-
) -> Vec<CoverageSpan> {
498-
bcb_data
499-
.basic_blocks
500-
.iter()
501-
.flat_map(|&bb| {
502-
let data = &self.mir_body[bb];
503-
data.statements
504-
.iter()
505-
.enumerate()
506-
.filter_map(move |(index, statement)| {
507-
filtered_statement_span(statement).map(|span| {
508-
CoverageSpan::for_statement(
509-
statement,
510-
function_source_span(span, self.body_span),
511-
span,
512-
bcb,
513-
bb,
514-
index,
515-
)
516-
})
517-
})
518-
.chain(filtered_terminator_span(data.terminator()).map(|span| {
519-
CoverageSpan::for_terminator(
520-
function_source_span(span, self.body_span),
521-
span,
522-
bcb,
523-
bb,
524-
)
525-
}))
526-
})
527-
.collect()
528-
}
529-
530425
fn curr(&self) -> &CoverageSpan {
531426
self.some_curr
532427
.as_ref()
@@ -589,7 +484,7 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
589484
self.some_prev = Some(curr);
590485
self.prev_original_span = self.curr_original_span;
591486
}
592-
while let Some(curr) = self.sorted_spans_iter.as_mut().unwrap().next() {
487+
while let Some(curr) = self.sorted_spans_iter.next() {
593488
debug!("FOR curr={:?}", curr);
594489
if self.some_prev.is_some() && self.prev_starts_after_next(&curr) {
595490
debug!(
@@ -757,7 +652,7 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
757652
if self.pending_dups.is_empty() {
758653
let curr_span = self.curr().span;
759654
self.prev_mut().cutoff_statements_at(curr_span.lo());
760-
if self.prev().coverage_statements.is_empty() {
655+
if self.prev().merged_spans.is_empty() {
761656
debug!(" ... no non-overlapping statements to add");
762657
} else {
763658
debug!(" ... adding modified prev={:?}", self.prev());
@@ -774,104 +669,3 @@ impl<'a, 'tcx> CoverageSpansGenerator<'a, 'tcx> {
774669
self.basic_coverage_blocks.dominates(dom_covspan.bcb, covspan.bcb)
775670
}
776671
}
777-
778-
/// If the MIR `Statement` has a span contributive to computing coverage spans,
779-
/// return it; otherwise return `None`.
780-
fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
781-
match statement.kind {
782-
// These statements have spans that are often outside the scope of the executed source code
783-
// for their parent `BasicBlock`.
784-
StatementKind::StorageLive(_)
785-
| StatementKind::StorageDead(_)
786-
// Coverage should not be encountered, but don't inject coverage coverage
787-
| StatementKind::Coverage(_)
788-
// Ignore `ConstEvalCounter`s
789-
| StatementKind::ConstEvalCounter
790-
// Ignore `Nop`s
791-
| StatementKind::Nop => None,
792-
793-
// FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead`
794-
// statements be more consistent?
795-
//
796-
// FakeReadCause::ForGuardBinding, in this example:
797-
// match somenum {
798-
// x if x < 1 => { ... }
799-
// }...
800-
// The BasicBlock within the match arm code included one of these statements, but the span
801-
// for it covered the `1` in this source. The actual statements have nothing to do with that
802-
// source span:
803-
// FakeRead(ForGuardBinding, _4);
804-
// where `_4` is:
805-
// _4 = &_1; (at the span for the first `x`)
806-
// and `_1` is the `Place` for `somenum`.
807-
//
808-
// If and when the Issue is resolved, remove this special case match pattern:
809-
StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
810-
811-
// Retain spans from all other statements
812-
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
813-
| StatementKind::Intrinsic(..)
814-
| StatementKind::Assign(_)
815-
| StatementKind::SetDiscriminant { .. }
816-
| StatementKind::Deinit(..)
817-
| StatementKind::Retag(_, _)
818-
| StatementKind::PlaceMention(..)
819-
| StatementKind::AscribeUserType(_, _) => {
820-
Some(statement.source_info.span)
821-
}
822-
}
823-
}
824-
825-
/// If the MIR `Terminator` has a span contributive to computing coverage spans,
826-
/// return it; otherwise return `None`.
827-
fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
828-
match terminator.kind {
829-
// These terminators have spans that don't positively contribute to computing a reasonable
830-
// span of actually executed source code. (For example, SwitchInt terminators extracted from
831-
// an `if condition { block }` has a span that includes the executed block, if true,
832-
// but for coverage, the code region executed, up to *and* through the SwitchInt,
833-
// actually stops before the if's block.)
834-
TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
835-
| TerminatorKind::Assert { .. }
836-
| TerminatorKind::Drop { .. }
837-
| TerminatorKind::SwitchInt { .. }
838-
// For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
839-
| TerminatorKind::FalseEdge { .. }
840-
| TerminatorKind::Goto { .. } => None,
841-
842-
// Call `func` operand can have a more specific span when part of a chain of calls
843-
| TerminatorKind::Call { ref func, .. } => {
844-
let mut span = terminator.source_info.span;
845-
if let mir::Operand::Constant(box constant) = func {
846-
if constant.span.lo() > span.lo() {
847-
span = span.with_lo(constant.span.lo());
848-
}
849-
}
850-
Some(span)
851-
}
852-
853-
// Retain spans from all other terminators
854-
TerminatorKind::UnwindResume
855-
| TerminatorKind::UnwindTerminate(_)
856-
| TerminatorKind::Return
857-
| TerminatorKind::Yield { .. }
858-
| TerminatorKind::GeneratorDrop
859-
| TerminatorKind::FalseUnwind { .. }
860-
| TerminatorKind::InlineAsm { .. } => {
861-
Some(terminator.source_info.span)
862-
}
863-
}
864-
}
865-
866-
/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
867-
/// within the function's body source. This span is guaranteed to be contained
868-
/// within, or equal to, the `body_span`. If the extrapolated span is not
869-
/// contained within the `body_span`, the `body_span` is returned.
870-
///
871-
/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
872-
/// etc.).
873-
#[inline]
874-
fn function_source_span(span: Span, body_span: Span) -> Span {
875-
let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
876-
if body_span.contains(original_span) { original_span } else { body_span }
877-
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
use rustc_middle::mir::{
2+
self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
3+
};
4+
use rustc_span::Span;
5+
6+
use crate::coverage::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
7+
use crate::coverage::spans::CoverageSpan;
8+
9+
pub(super) fn mir_to_initial_sorted_coverage_spans(
10+
mir_body: &mir::Body<'_>,
11+
fn_sig_span: Span,
12+
body_span: Span,
13+
basic_coverage_blocks: &CoverageGraph,
14+
) -> Vec<CoverageSpan> {
15+
let mut initial_spans = Vec::<CoverageSpan>::with_capacity(mir_body.basic_blocks.len() * 2);
16+
for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
17+
initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data));
18+
}
19+
20+
if initial_spans.is_empty() {
21+
// This can happen if, for example, the function is unreachable (contains only a
22+
// `BasicBlock`(s) with an `Unreachable` terminator).
23+
return initial_spans;
24+
}
25+
26+
initial_spans.push(CoverageSpan::for_fn_sig(fn_sig_span));
27+
28+
initial_spans.sort_by(|a, b| {
29+
// First sort by span start.
30+
Ord::cmp(&a.span.lo(), &b.span.lo())
31+
// If span starts are the same, sort by span end in reverse order.
32+
// This ensures that if spans A and B are adjacent in the list,
33+
// and they overlap but are not equal, then either:
34+
// - Span A extends further left, or
35+
// - Both have the same start and span A extends further right
36+
.then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
37+
// If both spans are equal, sort the BCBs in dominator order,
38+
// so that dominating BCBs come before other BCBs they dominate.
39+
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
40+
// If two spans are otherwise identical, put closure spans first,
41+
// as this seems to be what the refinement step expects.
42+
.then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
43+
});
44+
45+
initial_spans
46+
}
47+
48+
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
49+
// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
50+
// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
51+
// merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple
52+
// `Statement`s and/or `Terminator`s.)
53+
fn bcb_to_initial_coverage_spans(
54+
mir_body: &mir::Body<'_>,
55+
body_span: Span,
56+
bcb: BasicCoverageBlock,
57+
bcb_data: &BasicCoverageBlockData,
58+
) -> Vec<CoverageSpan> {
59+
bcb_data
60+
.basic_blocks
61+
.iter()
62+
.flat_map(|&bb| {
63+
let data = &mir_body[bb];
64+
data.statements
65+
.iter()
66+
.filter_map(move |statement| {
67+
filtered_statement_span(statement).map(|span| {
68+
CoverageSpan::for_statement(
69+
statement,
70+
function_source_span(span, body_span),
71+
span,
72+
bcb,
73+
)
74+
})
75+
})
76+
.chain(filtered_terminator_span(data.terminator()).map(|span| {
77+
CoverageSpan::for_terminator(function_source_span(span, body_span), span, bcb)
78+
}))
79+
})
80+
.collect()
81+
}
82+
83+
/// If the MIR `Statement` has a span contributive to computing coverage spans,
84+
/// return it; otherwise return `None`.
85+
fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
86+
match statement.kind {
87+
// These statements have spans that are often outside the scope of the executed source code
88+
// for their parent `BasicBlock`.
89+
StatementKind::StorageLive(_)
90+
| StatementKind::StorageDead(_)
91+
// Coverage should not be encountered, but don't inject coverage coverage
92+
| StatementKind::Coverage(_)
93+
// Ignore `ConstEvalCounter`s
94+
| StatementKind::ConstEvalCounter
95+
// Ignore `Nop`s
96+
| StatementKind::Nop => None,
97+
98+
// FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead`
99+
// statements be more consistent?
100+
//
101+
// FakeReadCause::ForGuardBinding, in this example:
102+
// match somenum {
103+
// x if x < 1 => { ... }
104+
// }...
105+
// The BasicBlock within the match arm code included one of these statements, but the span
106+
// for it covered the `1` in this source. The actual statements have nothing to do with that
107+
// source span:
108+
// FakeRead(ForGuardBinding, _4);
109+
// where `_4` is:
110+
// _4 = &_1; (at the span for the first `x`)
111+
// and `_1` is the `Place` for `somenum`.
112+
//
113+
// If and when the Issue is resolved, remove this special case match pattern:
114+
StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
115+
116+
// Retain spans from all other statements
117+
StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
118+
| StatementKind::Intrinsic(..)
119+
| StatementKind::Assign(_)
120+
| StatementKind::SetDiscriminant { .. }
121+
| StatementKind::Deinit(..)
122+
| StatementKind::Retag(_, _)
123+
| StatementKind::PlaceMention(..)
124+
| StatementKind::AscribeUserType(_, _) => {
125+
Some(statement.source_info.span)
126+
}
127+
}
128+
}
129+
130+
/// If the MIR `Terminator` has a span contributive to computing coverage spans,
131+
/// return it; otherwise return `None`.
132+
fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
133+
match terminator.kind {
134+
// These terminators have spans that don't positively contribute to computing a reasonable
135+
// span of actually executed source code. (For example, SwitchInt terminators extracted from
136+
// an `if condition { block }` has a span that includes the executed block, if true,
137+
// but for coverage, the code region executed, up to *and* through the SwitchInt,
138+
// actually stops before the if's block.)
139+
TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
140+
| TerminatorKind::Assert { .. }
141+
| TerminatorKind::Drop { .. }
142+
| TerminatorKind::SwitchInt { .. }
143+
// For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
144+
| TerminatorKind::FalseEdge { .. }
145+
| TerminatorKind::Goto { .. } => None,
146+
147+
// Call `func` operand can have a more specific span when part of a chain of calls
148+
| TerminatorKind::Call { ref func, .. } => {
149+
let mut span = terminator.source_info.span;
150+
if let mir::Operand::Constant(box constant) = func {
151+
if constant.span.lo() > span.lo() {
152+
span = span.with_lo(constant.span.lo());
153+
}
154+
}
155+
Some(span)
156+
}
157+
158+
// Retain spans from all other terminators
159+
TerminatorKind::UnwindResume
160+
| TerminatorKind::UnwindTerminate(_)
161+
| TerminatorKind::Return
162+
| TerminatorKind::Yield { .. }
163+
| TerminatorKind::GeneratorDrop
164+
| TerminatorKind::FalseUnwind { .. }
165+
| TerminatorKind::InlineAsm { .. } => {
166+
Some(terminator.source_info.span)
167+
}
168+
}
169+
}
170+
171+
/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
172+
/// within the function's body source. This span is guaranteed to be contained
173+
/// within, or equal to, the `body_span`. If the extrapolated span is not
174+
/// contained within the `body_span`, the `body_span` is returned.
175+
///
176+
/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
177+
/// etc.).
178+
#[inline]
179+
fn function_source_span(span: Span, body_span: Span) -> Span {
180+
use rustc_span::source_map::original_sp;
181+
182+
let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
183+
if body_span.contains(original_span) { original_span } else { body_span }
184+
}

0 commit comments

Comments
 (0)
Please sign in to comment.