Skip to content

Port #[coverage] to the new attribute system #143891

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
19 changes: 19 additions & 0 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,22 @@ pub enum DeprecatedSince {
Err,
}

#[derive(
Copy,
Debug,
Eq,
PartialEq,
Encodable,
Decodable,
Clone,
HashStable_Generic,
PrintAttribute
)]
pub enum CoverageStatus {
On,
Off,
}

impl Deprecation {
/// Whether an item marked with #[deprecated(since = "X")] is currently
/// deprecated (i.e., whether X is not greater than the current rustc
Expand Down Expand Up @@ -249,6 +265,9 @@ pub enum AttributeKind {
/// Represents `#[const_trait]`.
ConstTrait(Span),

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

///Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl AttributeKind {
ConstStability { .. } => Yes,
ConstStabilityIndirect => No,
ConstTrait(..) => No,
Coverage(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotImplementViaObject(..) => No,
Expand Down
41 changes: 40 additions & 1 deletion 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, OptimizeAttr, UsedBy};
use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy};
use rustc_feature::{AttributeTemplate, template};
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, sym};
Expand Down Expand Up @@ -52,6 +52,45 @@ impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
}

pub(crate) struct CoverageParser;

impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
const PATH: &[Symbol] = &[sym::coverage];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);

fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let Some(args) = args.list() else {
cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]);
return None;
};

let Some(arg) = args.single() else {
cx.expected_single_argument(args.span);
return None;
};

let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);

let Some(arg) = arg.meta_item() else {
fail_incorrect_argument(args.span);
return None;
};

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

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

pub(crate) struct ExportNameParser;

impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
Expand Down
25 changes: 23 additions & 2 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};

use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::codegen_attrs::{
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser,
TrackCallerParser, UsedParser,
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
TargetFeatureParser, TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
Expand Down Expand Up @@ -137,6 +137,7 @@ attribute_parsers!(
// tidy-alphabetical-end

// tidy-alphabetical-start
Single<CoverageParser>,
Single<DeprecationParser>,
Single<DummyParser>,
Single<ExportNameParser>,
Expand Down Expand Up @@ -426,6 +427,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
list: false,
},
})
}

pub(crate) fn expected_specific_argument_and_list(
&self,
span: Span,
possibilities: Vec<&'static str>,
) -> ErrorGuaranteed {
self.emit_err(AttributeParseError {
span,
attr_span: self.attr_span,
template: self.template.clone(),
attribute: self.attr_path.clone(),
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: false,
list: true,
},
})
}
Expand All @@ -443,6 +463,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: true,
list: false,
},
})
}
Expand Down
49 changes: 46 additions & 3 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,15 +516,22 @@ pub(crate) struct NakedFunctionIncompatibleAttribute {

pub(crate) enum AttributeParseErrorReason {
ExpectedNoArgs,
ExpectedStringLiteral { byte_string: Option<Span> },
ExpectedStringLiteral {
byte_string: Option<Span>,
},
ExpectedIntegerLiteral,
ExpectedAtLeastOneArgument,
ExpectedSingleArgument,
ExpectedList,
UnexpectedLiteral,
ExpectedNameValue(Option<Symbol>),
DuplicateKey(Symbol),
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
ExpectedSpecificArgument {
possibilities: Vec<&'static str>,
strings: bool,
/// Should we tell the user to write a list when they didn't?
list: bool,
},
}

pub(crate) struct AttributeParseError {
Expand Down Expand Up @@ -592,7 +599,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
format!("expected this to be of the form `{name} = \"...\"`"),
);
}
AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings,
list: false,
} => {
let quote = if strings { '"' } else { '`' };
match possibilities.as_slice() {
&[] => {}
Expand All @@ -618,6 +629,38 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
}
}
}
AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings,
list: true,
} => {
let quote = if strings { '"' } else { '`' };
match possibilities.as_slice() {
&[] => {}
&[x] => {
diag.span_label(
self.span,
format!(
"this attribute is only valid with {quote}{x}{quote} as an argument"
),
);
}
[first, second] => {
diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument"));
}
[first @ .., second_to_last, last] => {
let mut res = String::new();
for i in first {
res.push_str(&format!("{quote}{i}{quote}, "));
}
res.push_str(&format!(
"{quote}{second_to_last}{quote} or {quote}{last}{quote}"
));

diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}"));
}
}
}
}

let suggestions = self.template.suggestions(false, &name);
Expand Down
32 changes: 13 additions & 19 deletions compiler/rustc_mir_transform/src/coverage/query.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use rustc_attr_data_structures::{AttributeKind, CoverageStatus, 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};
use rustc_middle::mir::{Body, Statement, StatementKind};
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::util::Providers;
use rustc_span::def_id::LocalDefId;
use rustc_span::sym;
use tracing::trace;

use crate::coverage::counters::node_flow::make_node_counters;
Expand Down Expand Up @@ -58,26 +58,20 @@ 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(attr) = tcx.get_attr(def_id, sym::coverage) {
match attr.meta_item_list().as_deref() {
Some([item]) if item.has_name(sym::off) => return false,
Some([item]) if item.has_name(sym::on) => return true,
Some(_) | None => {
// Other possibilities should have been rejected by `rustc_parse::validate_attr`.
// Use `span_delayed_bug` to avoid an ICE in failing builds (#127880).
tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute");
}
if let Some(coverage_status) =
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status)
{
*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 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,
}
}

/// Query implementation for `coverage_ids_info`.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_parse/src/validate_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ pub fn check_builtin_meta_item(
| sym::rustc_layout_scalar_valid_range_end
| sym::no_implicit_prelude
| sym::automatically_derived
| sym::coverage
) {
return;
}
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
&Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => {
self.check_rustc_std_internal_symbol(attr_span, span, target)
}
&Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => {
self.check_coverage(attr_span, span, target)
}
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
match attr.path().as_slice() {
Expand All @@ -288,7 +291,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::diagnostic, sym::on_unimplemented, ..] => {
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
}
[sym::coverage, ..] => self.check_coverage(attr, span, target),
[sym::no_sanitize, ..] => {
self.check_no_sanitize(attr, span, target)
}
Expand Down Expand Up @@ -580,7 +582,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {

/// Checks that `#[coverage(..)]` is applied to a function/closure/method,
/// or to an impl block or module.
fn check_coverage(&self, attr: &Attribute, target_span: Span, target: Target) {
fn check_coverage(&self, attr_span: Span, target_span: Span, target: Target) {
let mut not_fn_impl_mod = None;
let mut no_body = None;

Expand All @@ -603,7 +605,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}

self.dcx().emit_err(errors::CoverageAttributeNotAllowed {
attr_span: attr.span(),
attr_span,
not_fn_impl_mod,
no_body,
help: (),
Expand Down
26 changes: 13 additions & 13 deletions tests/ui/attributes/malformed-attrs.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,6 @@ error: malformed `crate_name` attribute input
LL | #[crate_name]
| ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]`

error: malformed `coverage` attribute input
--> $DIR/malformed-attrs.rs:90:1
|
LL | #[coverage]
| ^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++

error: malformed `no_sanitize` attribute input
--> $DIR/malformed-attrs.rs:92:1
|
Expand Down Expand Up @@ -460,6 +447,19 @@ error[E0539]: malformed `link_section` attribute input
LL | #[link_section]
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]`

error[E0539]: malformed `coverage` attribute input
--> $DIR/malformed-attrs.rs:90:1
|
LL | #[coverage]
| ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
|
help: try changing it to one of the following valid forms of the attribute
|
LL | #[coverage(off)]
| +++++
LL | #[coverage(on)]
| ++++

error[E0565]: malformed `no_implicit_prelude` attribute input
--> $DIR/malformed-attrs.rs:97:1
|
Expand Down
Loading
Loading