Skip to content

Commit 83b56eb

Browse files
committed
coverage: Separate span-extraction from unexpansion
1 parent 5d85a71 commit 83b56eb

File tree

2 files changed

+66
-75
lines changed

2 files changed

+66
-75
lines changed

compiler/rustc_mir_transform/src/coverage/spans.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
66
use tracing::{debug, debug_span, instrument};
77

88
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
9-
use crate::coverage::spans::from_mir::{
10-
ExtractedCovspans, Hole, SpanFromMir, extract_covspans_from_mir,
11-
};
12-
use crate::coverage::{ExtractedHirInfo, mappings};
9+
use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir};
10+
use crate::coverage::{ExtractedHirInfo, mappings, unexpand};
1311

1412
mod from_mir;
1513

@@ -19,7 +17,35 @@ pub(super) fn extract_refined_covspans(
1917
graph: &CoverageGraph,
2018
code_mappings: &mut impl Extend<mappings::CodeMapping>,
2119
) {
22-
let ExtractedCovspans { mut covspans } = extract_covspans_from_mir(mir_body, hir_info, graph);
20+
let &ExtractedHirInfo { body_span, .. } = hir_info;
21+
22+
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
23+
let mut covspans = raw_spans
24+
.into_iter()
25+
.filter_map(|RawSpanFromMir { raw_span, bcb }| try {
26+
let (span, expn_kind) =
27+
unexpand::unexpand_into_body_span_with_expn_kind(raw_span, body_span)?;
28+
// Discard any spans that fill the entire body, because they tend
29+
// to represent compiler-inserted code, e.g. implicitly returning `()`.
30+
if span.source_equal(body_span) {
31+
return None;
32+
};
33+
SpanFromMir { span, expn_kind, bcb }
34+
})
35+
.collect::<Vec<_>>();
36+
37+
// Only proceed if we found at least one usable span.
38+
if covspans.is_empty() {
39+
return;
40+
}
41+
42+
// Also add the adjusted function signature span, if available.
43+
// Otherwise, add a fake span at the start of the body, to avoid an ugly
44+
// gap between the start of the body and the first real span.
45+
// FIXME: Find a more principled way to solve this problem.
46+
covspans.push(SpanFromMir::for_fn_sig(
47+
hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo()),
48+
));
2349

2450
// First, perform the passes that need macro information.
2551
covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb));

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

Lines changed: 35 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,56 @@
1+
use std::iter;
2+
13
use rustc_middle::bug;
24
use rustc_middle::mir::coverage::CoverageKind;
35
use rustc_middle::mir::{
46
self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
57
};
68
use rustc_span::{ExpnKind, Span};
79

8-
use crate::coverage::ExtractedHirInfo;
9-
use crate::coverage::graph::{
10-
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
11-
};
10+
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
1211
use crate::coverage::spans::Covspan;
13-
use crate::coverage::unexpand::unexpand_into_body_span_with_expn_kind;
1412

15-
pub(crate) struct ExtractedCovspans {
16-
pub(crate) covspans: Vec<SpanFromMir>,
13+
#[derive(Debug)]
14+
pub(crate) struct RawSpanFromMir {
15+
/// A span that has been extracted from a MIR statement/terminator, but
16+
/// hasn't been "unexpanded", so it might not lie within the function body
17+
/// span and might be part of an expansion with a different context.
18+
pub(crate) raw_span: Span,
19+
pub(crate) bcb: BasicCoverageBlock,
1720
}
1821

19-
/// Traverses the MIR body to produce an initial collection of coverage-relevant
20-
/// spans, each associated with a node in the coverage graph (BCB) and possibly
21-
/// other metadata.
22-
pub(crate) fn extract_covspans_from_mir(
23-
mir_body: &mir::Body<'_>,
24-
hir_info: &ExtractedHirInfo,
22+
/// Generates an initial set of coverage spans from the statements and
23+
/// terminators in the function's MIR body, each associated with its
24+
/// corresponding node in the coverage graph.
25+
///
26+
/// This is necessarily an inexact process, because MIR isn't designed to
27+
/// capture source spans at the level of detail we would want for coverage,
28+
/// but it's good enough to be better than nothing.
29+
pub(crate) fn extract_raw_spans_from_mir<'tcx>(
30+
mir_body: &mir::Body<'tcx>,
2531
graph: &CoverageGraph,
26-
) -> ExtractedCovspans {
27-
let &ExtractedHirInfo { body_span, .. } = hir_info;
28-
29-
let mut covspans = vec![];
32+
) -> Vec<RawSpanFromMir> {
33+
let mut raw_spans = vec![];
3034

35+
// We only care about blocks that are part of the coverage graph.
3136
for (bcb, bcb_data) in graph.iter_enumerated() {
32-
bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data, &mut covspans);
33-
}
37+
let make_raw_span = |raw_span: Span| RawSpanFromMir { raw_span, bcb };
3438

35-
// Only add the signature span if we found at least one span in the body.
36-
if !covspans.is_empty() {
37-
// If there is no usable signature span, add a fake one (before refinement)
38-
// to avoid an ugly gap between the body start and the first real span.
39-
// FIXME: Find a more principled way to solve this problem.
40-
let fn_sig_span = hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo());
41-
covspans.push(SpanFromMir::for_fn_sig(fn_sig_span));
42-
}
39+
// A coverage graph node can consist of multiple basic blocks.
40+
for &bb in &bcb_data.basic_blocks {
41+
let bb_data = &mir_body[bb];
4342

44-
ExtractedCovspans { covspans }
45-
}
43+
let statements = bb_data.statements.iter();
44+
raw_spans.extend(statements.filter_map(filtered_statement_span).map(make_raw_span));
4645

47-
// Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of
48-
// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One coverage span is generated
49-
// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
50-
// merge some coverage spans, at which point a coverage span may represent multiple
51-
// `Statement`s and/or `Terminator`s.)
52-
fn bcb_to_initial_coverage_spans<'a, 'tcx>(
53-
mir_body: &'a mir::Body<'tcx>,
54-
body_span: Span,
55-
bcb: BasicCoverageBlock,
56-
bcb_data: &'a BasicCoverageBlockData,
57-
initial_covspans: &mut Vec<SpanFromMir>,
58-
) {
59-
for &bb in &bcb_data.basic_blocks {
60-
let data = &mir_body[bb];
61-
62-
let unexpand = move |expn_span| {
63-
unexpand_into_body_span_with_expn_kind(expn_span, body_span)
64-
// Discard any spans that fill the entire body, because they tend
65-
// to represent compiler-inserted code, e.g. implicitly returning `()`.
66-
.filter(|(span, _)| !span.source_equal(body_span))
67-
};
68-
69-
let mut extract_statement_span = |statement| {
70-
let expn_span = filtered_statement_span(statement)?;
71-
let (span, expn_kind) = unexpand(expn_span)?;
72-
73-
initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
74-
Some(())
75-
};
76-
for statement in data.statements.iter() {
77-
extract_statement_span(statement);
46+
// There's only one terminator, but wrap it in an iterator to
47+
// mirror the handling of statements.
48+
let terminator = iter::once(bb_data.terminator());
49+
raw_spans.extend(terminator.filter_map(filtered_terminator_span).map(make_raw_span));
7850
}
79-
80-
let mut extract_terminator_span = |terminator| {
81-
let expn_span = filtered_terminator_span(terminator)?;
82-
let (span, expn_kind) = unexpand(expn_span)?;
83-
84-
initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
85-
Some(())
86-
};
87-
extract_terminator_span(data.terminator());
8851
}
52+
53+
raw_spans
8954
}
9055

9156
/// If the MIR `Statement` has a span contributive to computing coverage spans,
@@ -219,7 +184,7 @@ pub(crate) struct SpanFromMir {
219184
}
220185

221186
impl SpanFromMir {
222-
fn for_fn_sig(fn_sig_span: Span) -> Self {
187+
pub(crate) fn for_fn_sig(fn_sig_span: Span) -> Self {
223188
Self::new(fn_sig_span, None, START_BCB)
224189
}
225190

0 commit comments

Comments
 (0)