Skip to content

Commit 36dd276

Browse files
authored
Unrolled build for rust-lang#138776
Rollup merge of rust-lang#138776 - Zalathar:unexpand, r=oli-obk coverage: Separate span-extraction from unexpansion Historically, coverage instrumentation has relied on eagerly “unexpanding” MIR spans back to ancestor spans that have the same context as the function body, and lie within that body. Doing so makes several subsequent operations more straightforward. In order to support expansion regions, we need to stop doing that, and handle layers of macro-expansion more explicitly. This PR takes a step in that direction, by deferring some of the unexpansion steps, and concentrating them in one place (`spans::extract_refined_covspans`). Unexpansion still takes place as before, but these changes will make it easier to experiment with expansion-aware coverage instrumentation.
2 parents 40507bd + 7fdac5e commit 36dd276

File tree

3 files changed

+81
-90
lines changed

3 files changed

+81
-90
lines changed

compiler/rustc_mir_transform/src/coverage/mod.rs

+7-14
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,9 @@ struct ExtractedHirInfo {
273273
/// Must have the same context and filename as the body span.
274274
fn_sig_span_extended: Option<Span>,
275275
body_span: Span,
276-
/// "Holes" are regions within the body span that should not be included in
277-
/// coverage spans for this function (e.g. closures and nested items).
276+
/// "Holes" are regions within the function body (or its expansions) that
277+
/// should not be included in coverage spans for this function
278+
/// (e.g. closures and nested items).
278279
hole_spans: Vec<Span>,
279280
}
280281

@@ -323,7 +324,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir
323324

324325
let function_source_hash = hash_mir_source(tcx, hir_body);
325326

326-
let hole_spans = extract_hole_spans_from_hir(tcx, body_span, hir_body);
327+
let hole_spans = extract_hole_spans_from_hir(tcx, hir_body);
327328

328329
ExtractedHirInfo {
329330
function_source_hash,
@@ -340,14 +341,9 @@ fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) ->
340341
tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64()
341342
}
342343

343-
fn extract_hole_spans_from_hir<'tcx>(
344-
tcx: TyCtxt<'tcx>,
345-
body_span: Span, // Usually `hir_body.value.span`, but not always
346-
hir_body: &hir::Body<'tcx>,
347-
) -> Vec<Span> {
344+
fn extract_hole_spans_from_hir<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &hir::Body<'tcx>) -> Vec<Span> {
348345
struct HolesVisitor<'tcx> {
349346
tcx: TyCtxt<'tcx>,
350-
body_span: Span,
351347
hole_spans: Vec<Span>,
352348
}
353349

@@ -387,14 +383,11 @@ fn extract_hole_spans_from_hir<'tcx>(
387383
}
388384
impl HolesVisitor<'_> {
389385
fn visit_hole_span(&mut self, hole_span: Span) {
390-
// Discard any holes that aren't directly visible within the body span.
391-
if self.body_span.contains(hole_span) && self.body_span.eq_ctxt(hole_span) {
392-
self.hole_spans.push(hole_span);
393-
}
386+
self.hole_spans.push(hole_span);
394387
}
395388
}
396389

397-
let mut visitor = HolesVisitor { tcx, body_span, hole_spans: vec![] };
390+
let mut visitor = HolesVisitor { tcx, hole_spans: vec![] };
398391

399392
visitor.visit_body(hir_body);
400393
visitor.hole_spans

compiler/rustc_mir_transform/src/coverage/spans.rs

+39-6
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));
@@ -43,7 +69,14 @@ pub(super) fn extract_refined_covspans(
4369
covspans.dedup_by(|b, a| a.span.source_equal(b.span));
4470

4571
// Sort the holes, and merge overlapping/adjacent holes.
46-
let mut holes = hir_info.hole_spans.iter().map(|&span| Hole { span }).collect::<Vec<_>>();
72+
let mut holes = hir_info
73+
.hole_spans
74+
.iter()
75+
.copied()
76+
// Discard any holes that aren't directly visible within the body span.
77+
.filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span))
78+
.map(|span| Hole { span })
79+
.collect::<Vec<_>>();
4780
holes.sort_by(|a, b| compare_spans(a.span, b.span));
4881
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
4982

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

+35-70
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)