Rollup merge of #130013 - jonathan-conder:await_coverage, r=Zalathar
coverage: Count await when the Future is immediately ready Currently `await` is only counted towards coverage if the containing function is suspended and resumed at least once. This is because it expands to code which contains a branch on the discriminant of `Poll`. By treating it like a branching macro (e.g. `assert!`), these implementation details will be hidden from the coverage results. I added a test to ensure the fix works in simple cases, but the heuristic of picking only the first await-related covspan might be unreliable. I plan on testing more thoroughly with a real codebase over the next couple of weeks. closes #98712
This commit is contained in:
commit
11d5614a74
8 changed files with 161 additions and 64 deletions
|
@ -3,7 +3,7 @@ use std::collections::VecDeque;
|
|||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
|
||||
use tracing::{debug, debug_span, instrument};
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
|
||||
|
@ -25,7 +25,7 @@ pub(super) fn extract_refined_covspans(
|
|||
|
||||
// First, perform the passes that need macro information.
|
||||
covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
|
||||
remove_unwanted_macro_spans(&mut covspans);
|
||||
remove_unwanted_expansion_spans(&mut covspans);
|
||||
split_visible_macro_spans(&mut covspans);
|
||||
|
||||
// We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
|
||||
|
@ -76,18 +76,24 @@ pub(super) fn extract_refined_covspans(
|
|||
/// invocation, which is unhelpful. Keeping only the first such span seems to
|
||||
/// give better mappings, so remove the others.
|
||||
///
|
||||
/// Similarly, `await` expands to a branch on the discriminant of `Poll`, which
|
||||
/// leads to incorrect coverage if the `Future` is immediately ready (#98712).
|
||||
///
|
||||
/// (The input spans should be sorted in BCB dominator order, so that the
|
||||
/// retained "first" span is likely to dominate the others.)
|
||||
fn remove_unwanted_macro_spans(covspans: &mut Vec<SpanFromMir>) {
|
||||
let mut seen_macro_spans = FxHashSet::default();
|
||||
covspans.retain(|covspan| {
|
||||
// Ignore (retain) non-macro-expansion spans.
|
||||
if covspan.visible_macro.is_none() {
|
||||
return true;
|
||||
}
|
||||
fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
|
||||
let mut deduplicated_spans = FxHashSet::default();
|
||||
|
||||
// Retain only the first macro-expanded covspan with this span.
|
||||
seen_macro_spans.insert(covspan.span)
|
||||
covspans.retain(|covspan| {
|
||||
match covspan.expn_kind {
|
||||
// Retain only the first await-related or macro-expanded covspan with this span.
|
||||
Some(ExpnKind::Desugaring(kind)) if kind == DesugaringKind::Await => {
|
||||
deduplicated_spans.insert(covspan.span)
|
||||
}
|
||||
Some(ExpnKind::Macro(MacroKind::Bang, _)) => deduplicated_spans.insert(covspan.span),
|
||||
// Ignore (retain) other spans.
|
||||
_ => true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -99,7 +105,9 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
|
|||
let mut extra_spans = vec![];
|
||||
|
||||
covspans.retain(|covspan| {
|
||||
let Some(visible_macro) = covspan.visible_macro else { return true };
|
||||
let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else {
|
||||
return true;
|
||||
};
|
||||
|
||||
let split_len = visible_macro.as_str().len() as u32 + 1;
|
||||
let (before, after) = covspan.span.split_at(split_len);
|
||||
|
@ -111,8 +119,8 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
|
|||
return true;
|
||||
}
|
||||
|
||||
extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb));
|
||||
extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb));
|
||||
extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb));
|
||||
extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb));
|
||||
false // Discard the original covspan that we just split.
|
||||
});
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@ use rustc_middle::mir::coverage::CoverageKind;
|
|||
use rustc_middle::mir::{
|
||||
self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
|
||||
};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::{ExpnKind, Span};
|
||||
|
||||
use crate::coverage::graph::{
|
||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||
};
|
||||
use crate::coverage::spans::Covspan;
|
||||
use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro;
|
||||
use crate::coverage::unexpand::unexpand_into_body_span_with_expn_kind;
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
pub(crate) struct ExtractedCovspans {
|
||||
|
@ -60,7 +60,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
|
|||
let data = &mir_body[bb];
|
||||
|
||||
let unexpand = move |expn_span| {
|
||||
unexpand_into_body_span_with_visible_macro(expn_span, body_span)
|
||||
unexpand_into_body_span_with_expn_kind(expn_span, body_span)
|
||||
// Discard any spans that fill the entire body, because they tend
|
||||
// to represent compiler-inserted code, e.g. implicitly returning `()`.
|
||||
.filter(|(span, _)| !span.source_equal(body_span))
|
||||
|
@ -68,9 +68,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
|
|||
|
||||
let mut extract_statement_span = |statement| {
|
||||
let expn_span = filtered_statement_span(statement)?;
|
||||
let (span, visible_macro) = unexpand(expn_span)?;
|
||||
let (span, expn_kind) = unexpand(expn_span)?;
|
||||
|
||||
initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
|
||||
initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
|
||||
Some(())
|
||||
};
|
||||
for statement in data.statements.iter() {
|
||||
|
@ -79,9 +79,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
|
|||
|
||||
let mut extract_terminator_span = |terminator| {
|
||||
let expn_span = filtered_terminator_span(terminator)?;
|
||||
let (span, visible_macro) = unexpand(expn_span)?;
|
||||
let (span, expn_kind) = unexpand(expn_span)?;
|
||||
|
||||
initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
|
||||
initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
|
||||
Some(())
|
||||
};
|
||||
extract_terminator_span(data.terminator());
|
||||
|
@ -214,7 +214,7 @@ pub(crate) struct SpanFromMir {
|
|||
/// With the exception of `fn_sig_span`, this should always be contained
|
||||
/// within `body_span`.
|
||||
pub(crate) span: Span,
|
||||
pub(crate) visible_macro: Option<Symbol>,
|
||||
pub(crate) expn_kind: Option<ExpnKind>,
|
||||
pub(crate) bcb: BasicCoverageBlock,
|
||||
}
|
||||
|
||||
|
@ -223,12 +223,12 @@ impl SpanFromMir {
|
|||
Self::new(fn_sig_span, None, START_BCB)
|
||||
}
|
||||
|
||||
pub(crate) fn new(span: Span, visible_macro: Option<Symbol>, bcb: BasicCoverageBlock) -> Self {
|
||||
Self { span, visible_macro, bcb }
|
||||
pub(crate) fn new(span: Span, expn_kind: Option<ExpnKind>, bcb: BasicCoverageBlock) -> Self {
|
||||
Self { span, expn_kind, bcb }
|
||||
}
|
||||
|
||||
pub(crate) fn into_covspan(self) -> Covspan {
|
||||
let Self { span, visible_macro: _, bcb } = self;
|
||||
let Self { span, expn_kind: _, bcb } = self;
|
||||
Covspan { span, bcb }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||
use rustc_span::{ExpnKind, Span};
|
||||
|
||||
/// Walks through the expansion ancestors of `original_span` to find a span that
|
||||
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
|
||||
|
@ -13,20 +13,15 @@ pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> O
|
|||
///
|
||||
/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
|
||||
/// the returned symbol will be the name of that macro (e.g. `foo`).
|
||||
pub(crate) fn unexpand_into_body_span_with_visible_macro(
|
||||
pub(crate) fn unexpand_into_body_span_with_expn_kind(
|
||||
original_span: Span,
|
||||
body_span: Span,
|
||||
) -> Option<(Span, Option<Symbol>)> {
|
||||
) -> Option<(Span, Option<ExpnKind>)> {
|
||||
let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
|
||||
|
||||
let visible_macro = prev
|
||||
.map(|prev| match prev.ctxt().outer_expn_data().kind {
|
||||
ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
|
||||
_ => None,
|
||||
})
|
||||
.flatten();
|
||||
let expn_kind = prev.map(|prev| prev.ctxt().outer_expn_data().kind);
|
||||
|
||||
Some((span, visible_macro))
|
||||
Some((span, expn_kind))
|
||||
}
|
||||
|
||||
/// Walks through the expansion ancestors of `original_span` to find a span that
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue