coverage: Make is_eligible_for_coverage
a hook method
This will allow MIR building to check whether a function is eligible for coverage instrumentation, and avoid collecting branch coverage info if it is not.
This commit is contained in:
parent
975109892c
commit
73475d0d59
@ -6,6 +6,7 @@
|
|||||||
use crate::mir;
|
use crate::mir;
|
||||||
use crate::query::TyCtxtAt;
|
use crate::query::TyCtxtAt;
|
||||||
use crate::ty::{Ty, TyCtxt};
|
use crate::ty::{Ty, TyCtxt};
|
||||||
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
macro_rules! declare_hooks {
|
macro_rules! declare_hooks {
|
||||||
@ -70,4 +71,10 @@ fn clone(&self) -> Self { *self }
|
|||||||
|
|
||||||
/// Getting a &core::panic::Location referring to a span.
|
/// Getting a &core::panic::Location referring to a span.
|
||||||
hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>;
|
hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>;
|
||||||
|
|
||||||
|
/// Returns `true` if this def is a function-like thing that is eligible for
|
||||||
|
/// coverage instrumentation under `-Cinstrument-coverage`.
|
||||||
|
///
|
||||||
|
/// (Eligible functions might nevertheless be skipped for other reasons.)
|
||||||
|
hook is_eligible_for_coverage(key: LocalDefId) -> bool;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
use crate::MirPass;
|
use crate::MirPass;
|
||||||
|
|
||||||
use rustc_middle::hir;
|
use rustc_middle::hir;
|
||||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
|
||||||
use rustc_middle::mir::coverage::*;
|
use rustc_middle::mir::coverage::*;
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
|
self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
|
||||||
@ -44,7 +43,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
|
|||||||
|
|
||||||
let def_id = mir_source.def_id().expect_local();
|
let def_id = mir_source.def_id().expect_local();
|
||||||
|
|
||||||
if !is_eligible_for_coverage(tcx, def_id) {
|
if !tcx.is_eligible_for_coverage(def_id) {
|
||||||
trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)");
|
trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -349,37 +348,6 @@ fn check_code_region(code_region: CodeRegion) -> Option<CodeRegion> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
|
||||||
// Only instrument functions, methods, and closures (not constants since they are evaluated
|
|
||||||
// at compile time by Miri).
|
|
||||||
// FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
|
|
||||||
// expressions get coverage spans, we will probably have to "carve out" space for const
|
|
||||||
// expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
|
|
||||||
// be tricky if const expressions have no corresponding statements in the enclosing MIR.
|
|
||||||
// Closures are carved out by their initial `Assign` statement.)
|
|
||||||
if !tcx.def_kind(def_id).is_fn_like() {
|
|
||||||
trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't instrument functions with `#[automatically_derived]` on their
|
|
||||||
// enclosing impl block, on the assumption that most users won't care about
|
|
||||||
// coverage for derived impls.
|
|
||||||
if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id())
|
|
||||||
&& tcx.is_automatically_derived(impl_of)
|
|
||||||
{
|
|
||||||
trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
|
|
||||||
trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Function information extracted from HIR by the coverage instrumentor.
|
/// Function information extracted from HIR by the coverage instrumentor.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ExtractedHirInfo {
|
struct ExtractedHirInfo {
|
||||||
|
@ -1,16 +1,51 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
use rustc_data_structures::captures::Captures;
|
use rustc_data_structures::captures::Captures;
|
||||||
use rustc_middle::mir::coverage::*;
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||||
use rustc_middle::mir::{Body, CoverageIdsInfo};
|
use rustc_middle::mir::coverage::{CounterId, CoverageKind};
|
||||||
use rustc_middle::ty::{self};
|
use rustc_middle::mir::{Body, Coverage, CoverageIdsInfo, Statement, StatementKind};
|
||||||
|
use rustc_middle::query::TyCtxtAt;
|
||||||
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
use rustc_middle::util::Providers;
|
use rustc_middle::util::Providers;
|
||||||
|
use rustc_span::def_id::LocalDefId;
|
||||||
|
|
||||||
/// Registers query/hook implementations related to coverage.
|
/// Registers query/hook implementations related to coverage.
|
||||||
pub(crate) fn provide(providers: &mut Providers) {
|
pub(crate) fn provide(providers: &mut Providers) {
|
||||||
|
providers.hooks.is_eligible_for_coverage =
|
||||||
|
|TyCtxtAt { tcx, .. }, def_id| is_eligible_for_coverage(tcx, def_id);
|
||||||
providers.queries.coverage_ids_info = coverage_ids_info;
|
providers.queries.coverage_ids_info = coverage_ids_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hook implementation for [`TyCtxt::is_eligible_for_coverage`].
|
||||||
|
fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||||
|
// Only instrument functions, methods, and closures (not constants since they are evaluated
|
||||||
|
// at compile time by Miri).
|
||||||
|
// FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
|
||||||
|
// expressions get coverage spans, we will probably have to "carve out" space for const
|
||||||
|
// expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
|
||||||
|
// be tricky if const expressions have no corresponding statements in the enclosing MIR.
|
||||||
|
// Closures are carved out by their initial `Assign` statement.)
|
||||||
|
if !tcx.def_kind(def_id).is_fn_like() {
|
||||||
|
trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't instrument functions with `#[automatically_derived]` on their
|
||||||
|
// enclosing impl block, on the assumption that most users won't care about
|
||||||
|
// coverage for derived impls.
|
||||||
|
if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id())
|
||||||
|
&& tcx.is_automatically_derived(impl_of)
|
||||||
|
{
|
||||||
|
trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
|
||||||
|
trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Query implementation for `coverage_ids_info`.
|
/// Query implementation for `coverage_ids_info`.
|
||||||
fn coverage_ids_info<'tcx>(
|
fn coverage_ids_info<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
Loading…
Reference in New Issue
Block a user