coverage: Only merge adjacent coverage spans

This also removes some manipulation of the function signature span that only
made sense in the context of merging non-adjacent spans.
This commit is contained in:
Zalathar 2025-04-12 17:52:55 +10:00
parent 651e9cf327
commit 4d5a1acebf
139 changed files with 2982 additions and 1919 deletions

View file

@ -91,7 +91,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
// When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need
// to give the same treatment to _all_ functions, because `llvm-cov`
// seems to ignore functions that don't have any ordinary code spans.
if let Some(span) = hir_info.fn_sig_span_extended {
if let Some(span) = hir_info.fn_sig_span {
code_mappings.push(CodeMapping { span, bcb: START_BCB });
}
} else {

View file

@ -268,9 +268,9 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb
struct ExtractedHirInfo {
function_source_hash: u64,
is_async_fn: bool,
/// The span of the function's signature, extended to the start of `body_span`.
/// The span of the function's signature, if available.
/// Must have the same context and filename as the body span.
fn_sig_span_extended: Option<Span>,
fn_sig_span: Option<Span>,
body_span: Span,
/// "Holes" are regions within the function body (or its expansions) that
/// should not be included in coverage spans for this function
@ -308,30 +308,20 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir
// The actual signature span is only used if it has the same context and
// filename as the body, and precedes the body.
let fn_sig_span_extended = maybe_fn_sig
.map(|fn_sig| fn_sig.span)
.filter(|&fn_sig_span| {
let source_map = tcx.sess.source_map();
let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo());
let fn_sig_span = maybe_fn_sig.map(|fn_sig| fn_sig.span).filter(|&fn_sig_span| {
let source_map = tcx.sess.source_map();
let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo());
fn_sig_span.eq_ctxt(body_span)
&& fn_sig_span.hi() <= body_span.lo()
&& file_idx(fn_sig_span) == file_idx(body_span)
})
// If so, extend it to the start of the body span.
.map(|fn_sig_span| fn_sig_span.with_hi(body_span.lo()));
fn_sig_span.eq_ctxt(body_span)
&& fn_sig_span.hi() <= body_span.lo()
&& file_idx(fn_sig_span) == file_idx(body_span)
});
let function_source_hash = hash_mir_source(tcx, hir_body);
let hole_spans = extract_hole_spans_from_hir(tcx, hir_body);
ExtractedHirInfo {
function_source_hash,
is_async_fn,
fn_sig_span_extended,
body_span,
hole_spans,
}
ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span, body_span, hole_spans }
}
fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 {

View file

@ -42,12 +42,12 @@ pub(super) fn extract_refined_covspans<'tcx>(
return;
}
// Also add the adjusted function signature span, if available.
// Also add the function signature span, if available.
// Otherwise, add a fake span at the start of the body, to avoid an ugly
// gap between the start of the body and the first real span.
// FIXME: Find a more principled way to solve this problem.
covspans.push(SpanFromMir::for_fn_sig(
hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo()),
hir_info.fn_sig_span.unwrap_or_else(|| body_span.shrink_to_lo()),
));
// First, perform the passes that need macro information.
@ -227,19 +227,21 @@ struct Covspan {
}
impl Covspan {
/// If `self` and `other` can be merged (i.e. they have the same BCB),
/// mutates `self.span` to also include `other.span` and returns true.
/// If `self` and `other` can be merged, mutates `self.span` to also
/// include `other.span` and returns true.
///
/// Note that compatible covspans can be merged even if their underlying
/// spans are not overlapping/adjacent; any space between them will also be
/// part of the merged covspan.
/// Two covspans can be merged if they have the same BCB, and they are
/// overlapping or adjacent.
fn merge_if_eligible(&mut self, other: &Self) -> bool {
if self.bcb != other.bcb {
return false;
}
let eligible_for_merge =
|a: &Self, b: &Self| (a.bcb == b.bcb) && a.span.overlaps_or_adjacent(b.span);
self.span = self.span.to(other.span);
true
if eligible_for_merge(self, other) {
self.span = self.span.to(other.span);
true
} else {
false
}
}
}