Skip to content

coverage: Treat #[automatically_derived] as #[coverage(off)] #144560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 6 additions & 14 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,10 @@ pub enum DeprecatedSince {
Err,
}

#[derive(
Copy,
Debug,
Eq,
PartialEq,
Encodable,
Decodable,
Clone,
HashStable_Generic,
PrintAttribute
)]
pub enum CoverageStatus {
/// Successfully-parsed value of a `#[coverage(..)]` attribute.
#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)]
#[derive(HashStable_Generic, PrintAttribute)]
pub enum CoverageAttrKind {
On,
Off,
}
Expand Down Expand Up @@ -304,8 +296,8 @@ pub enum AttributeKind {
/// Represents `#[const_trait]`.
ConstTrait(Span),

/// Represents `#[coverage]`.
Coverage(Span, CoverageStatus),
/// Represents `#[coverage(..)]`.
Coverage(Span, CoverageAttrKind),

///Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy};
use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy};
use rustc_feature::{AttributeTemplate, template};
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
Expand Down Expand Up @@ -78,16 +78,16 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
return None;
};

let status = match arg.path().word_sym() {
Some(sym::off) => CoverageStatus::Off,
Some(sym::on) => CoverageStatus::On,
let kind = match arg.path().word_sym() {
Some(sym::off) => CoverageAttrKind::Off,
Some(sym::on) => CoverageAttrKind::On,
None | Some(_) => {
fail_incorrect_argument(arg.span());
return None;
}
};

Some(AttributeKind::Coverage(cx.attr_span, status))
Some(AttributeKind::Coverage(cx.attr_span, kind))
}
}

Expand Down
48 changes: 25 additions & 23 deletions compiler/rustc_mir_transform/src/coverage/query.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_attr_data_structures::{AttributeKind, CoverageStatus, find_attr};
use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, find_attr};
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind};
Expand Down Expand Up @@ -32,16 +32,6 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
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_assoc(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::NAKED) {
trace!("InstrumentCoverage skipped for {def_id:?} (`#[naked]`)");
return false;
Expand All @@ -57,20 +47,32 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {

/// Query implementation for `coverage_attr_on`.
fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
// Check for annotations directly on this def.
if let Some(coverage_status) =
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status)
// Check for a `#[coverage(..)]` attribute on this def.
if let Some(kind) =
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_sp, kind) => kind)
{
*coverage_status == CoverageStatus::On
} else {
match tcx.opt_local_parent(def_id) {
// Check the parent def (and so on recursively) until we find an
// enclosing attribute or reach the crate root.
Some(parent) => tcx.coverage_attr_on(parent),
// We reached the crate root without seeing a coverage attribute, so
// allow coverage instrumentation by default.
None => true,
match kind {
CoverageAttrKind::On => return true,
CoverageAttrKind::Off => return false,
}
};

// Treat `#[automatically_derived]` as an implied `#[coverage(off)]`, on
// the assumption that most users won't want coverage for derived impls.
//
// This affects not just the associated items of an impl block, but also
// any closures and other nested functions within those associated items.
if tcx.is_automatically_derived(def_id.to_def_id()) {
return false;
}

// Check the parent def (and so on recursively) until we find an
// enclosing attribute or reach the crate root.
match tcx.opt_local_parent(def_id) {
Some(parent) => tcx.coverage_attr_on(parent),
// We reached the crate root without seeing a coverage attribute, so
// allow coverage instrumentation by default.
None => true,
}
}

Expand Down
23 changes: 23 additions & 0 deletions tests/coverage/auto-derived.auto.cov-map
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn::inner_fn_on
Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 09, 00, 19, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 23, 01, 01, 09, 00, 0a]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 25)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16)
- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 35)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
Highest counter ID seen: c0

Function name: auto_derived::main
Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 51, 1) to (start + 0, 10)
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c0

54 changes: 54 additions & 0 deletions tests/coverage/auto-derived.auto.coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
LL| |#![feature(coverage_attribute)]
LL| |//@ edition: 2024
LL| |//@ revisions: base auto on
LL| |
LL| |// Tests for how `#[automatically_derived]` affects coverage instrumentation.
LL| |//
LL| |// The actual behaviour is an implementation detail, so this test mostly exists
LL| |// to show when that behaviour has been accidentally or deliberately changed.
LL| |//
LL| |// Revision guide:
LL| |// - base: Test baseline instrumentation behaviour without `#[automatically_derived]`
LL| |// - auto: Test how `#[automatically_derived]` affects instrumentation
LL| |// - on: Test interaction between auto-derived and `#[coverage(on)]`
LL| |
LL| |struct MyStruct;
LL| |
LL| |trait MyTrait {
LL| | fn my_assoc_fn();
LL| |}
LL| |
LL| |#[cfg_attr(auto, automatically_derived)]
LL| |#[cfg_attr(on, automatically_derived)]
LL| |#[cfg_attr(on, coverage(on))]
LL| |impl MyTrait for MyStruct {
LL| | fn my_assoc_fn() {
LL| | fn inner_fn() {
LL| | say("in inner fn");
LL| | }
LL| |
LL| | #[coverage(on)]
LL| 1| fn inner_fn_on() {
LL| 1| say("in inner fn (on)");
LL| 1| }
LL| |
LL| | let closure = || {
LL| | say("in closure");
LL| | };
LL| |
LL| | closure();
LL| | inner_fn();
LL| | inner_fn_on();
LL| | }
LL| |}
LL| |
LL| |#[coverage(off)]
LL| |#[inline(never)]
LL| |fn say(s: &str) {
LL| | println!("{s}");
LL| |}
LL| |
LL| 1|fn main() {
LL| 1| MyStruct::my_assoc_fn();
LL| 1|}

61 changes: 61 additions & 0 deletions tests/coverage/auto-derived.base.cov-map
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn
Raw bytes (34): 0x[01, 01, 00, 06, 01, 19, 05, 00, 15, 01, 0a, 0d, 00, 14, 01, 04, 09, 00, 12, 01, 01, 09, 00, 11, 01, 01, 09, 00, 14, 01, 01, 05, 00, 06]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 6
- Code(Counter(0)) at (prev + 25, 5) to (start + 0, 21)
- Code(Counter(0)) at (prev + 10, 13) to (start + 0, 20)
- Code(Counter(0)) at (prev + 4, 9) to (start + 0, 18)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 17)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 20)
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6)
Highest counter ID seen: c0

Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn::inner_fn
Raw bytes (24): 0x[01, 01, 00, 04, 01, 1a, 09, 00, 16, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1e, 01, 01, 09, 00, 0a]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 26, 9) to (start + 0, 22)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16)
- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 30)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
Highest counter ID seen: c0

Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn::inner_fn_on
Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 09, 00, 19, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 23, 01, 01, 09, 00, 0a]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 25)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16)
- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 35)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
Highest counter ID seen: c0

Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn::{closure#0}
Raw bytes (24): 0x[01, 01, 00, 04, 01, 23, 1a, 00, 1b, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1d, 01, 01, 09, 00, 0a]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 35, 26) to (start + 0, 27)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16)
- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 29)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
Highest counter ID seen: c0

Function name: auto_derived::main
Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 51, 1) to (start + 0, 10)
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c0

54 changes: 54 additions & 0 deletions tests/coverage/auto-derived.base.coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
LL| |#![feature(coverage_attribute)]
LL| |//@ edition: 2024
LL| |//@ revisions: base auto on
LL| |
LL| |// Tests for how `#[automatically_derived]` affects coverage instrumentation.
LL| |//
LL| |// The actual behaviour is an implementation detail, so this test mostly exists
LL| |// to show when that behaviour has been accidentally or deliberately changed.
LL| |//
LL| |// Revision guide:
LL| |// - base: Test baseline instrumentation behaviour without `#[automatically_derived]`
LL| |// - auto: Test how `#[automatically_derived]` affects instrumentation
LL| |// - on: Test interaction between auto-derived and `#[coverage(on)]`
LL| |
LL| |struct MyStruct;
LL| |
LL| |trait MyTrait {
LL| | fn my_assoc_fn();
LL| |}
LL| |
LL| |#[cfg_attr(auto, automatically_derived)]
LL| |#[cfg_attr(on, automatically_derived)]
LL| |#[cfg_attr(on, coverage(on))]
LL| |impl MyTrait for MyStruct {
LL| 1| fn my_assoc_fn() {
LL| 1| fn inner_fn() {
LL| 1| say("in inner fn");
LL| 1| }
LL| |
LL| | #[coverage(on)]
LL| 1| fn inner_fn_on() {
LL| 1| say("in inner fn (on)");
LL| 1| }
LL| |
LL| 1| let closure = || {
LL| 1| say("in closure");
LL| 1| };
LL| |
LL| 1| closure();
LL| 1| inner_fn();
LL| 1| inner_fn_on();
LL| 1| }
LL| |}
LL| |
LL| |#[coverage(off)]
LL| |#[inline(never)]
LL| |fn say(s: &str) {
LL| | println!("{s}");
LL| |}
LL| |
LL| 1|fn main() {
LL| 1| MyStruct::my_assoc_fn();
LL| 1|}

61 changes: 61 additions & 0 deletions tests/coverage/auto-derived.on.cov-map
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn
Raw bytes (34): 0x[01, 01, 00, 06, 01, 19, 05, 00, 15, 01, 0a, 0d, 00, 14, 01, 04, 09, 00, 12, 01, 01, 09, 00, 11, 01, 01, 09, 00, 14, 01, 01, 05, 00, 06]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 6
- Code(Counter(0)) at (prev + 25, 5) to (start + 0, 21)
- Code(Counter(0)) at (prev + 10, 13) to (start + 0, 20)
- Code(Counter(0)) at (prev + 4, 9) to (start + 0, 18)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 17)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 20)
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6)
Highest counter ID seen: c0

Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn::inner_fn
Raw bytes (24): 0x[01, 01, 00, 04, 01, 1a, 09, 00, 16, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1e, 01, 01, 09, 00, 0a]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 26, 9) to (start + 0, 22)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16)
- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 30)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
Highest counter ID seen: c0

Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn::inner_fn_on
Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 09, 00, 19, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 23, 01, 01, 09, 00, 0a]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 25)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16)
- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 35)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
Highest counter ID seen: c0

Function name: <auto_derived::MyStruct as auto_derived::MyTrait>::my_assoc_fn::{closure#0}
Raw bytes (24): 0x[01, 01, 00, 04, 01, 23, 1a, 00, 1b, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1d, 01, 01, 09, 00, 0a]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 4
- Code(Counter(0)) at (prev + 35, 26) to (start + 0, 27)
- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16)
- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 29)
- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10)
Highest counter ID seen: c0

Function name: auto_derived::main
Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => $DIR/auto-derived.rs
Number of expressions: 0
Number of file 0 mappings: 3
- Code(Counter(0)) at (prev + 51, 1) to (start + 0, 10)
- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26)
- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2)
Highest counter ID seen: c0

Loading
Loading