Skip to content

Commit ec38df9

Browse files
committed
Port #[coverage] to the new attribute system
1 parent d2baa49 commit ec38df9

File tree

15 files changed

+357
-389
lines changed

15 files changed

+357
-389
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,22 @@ pub enum DeprecatedSince {
109109
Err,
110110
}
111111

112+
#[derive(
113+
Copy,
114+
Debug,
115+
Eq,
116+
PartialEq,
117+
Encodable,
118+
Decodable,
119+
Clone,
120+
HashStable_Generic,
121+
PrintAttribute
122+
)]
123+
pub enum CoverageStatus {
124+
On,
125+
Off,
126+
}
127+
112128
impl Deprecation {
113129
/// Whether an item marked with #[deprecated(since = "X")] is currently
114130
/// deprecated (i.e., whether X is not greater than the current rustc
@@ -246,6 +262,9 @@ pub enum AttributeKind {
246262
/// Represents `#[const_trait]`.
247263
ConstTrait(Span),
248264

265+
/// Represents `#[coverage]`.
266+
Coverage(Span, CoverageStatus),
267+
249268
///Represents `#[rustc_deny_explicit_impl]`.
250269
DenyExplicitImpl(Span),
251270

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ impl AttributeKind {
2727
ConstStability { .. } => Yes,
2828
ConstStabilityIndirect => No,
2929
ConstTrait(..) => No,
30+
Coverage(..) => No,
3031
DenyExplicitImpl(..) => No,
3132
Deprecation { .. } => Yes,
3233
DoNotImplementViaObject(..) => No,

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy};
1+
use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy};
22
use rustc_feature::{AttributeTemplate, template};
33
use rustc_session::parse::feature_err;
44
use rustc_span::{Span, Symbol, sym};
@@ -52,6 +52,45 @@ impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
5252
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
5353
}
5454

55+
pub(crate) struct CoverageParser;
56+
57+
impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
58+
const PATH: &[Symbol] = &[sym::coverage];
59+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
60+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
61+
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
62+
63+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
64+
let Some(args) = args.list() else {
65+
cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]);
66+
return None;
67+
};
68+
69+
let Some(arg) = args.single() else {
70+
cx.expected_specific_argument(args.span, vec!["on", "off"]);
71+
return None;
72+
};
73+
74+
let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);
75+
76+
let Some(arg) = arg.meta_item() else {
77+
fail_incorrect_argument(args.span);
78+
return None;
79+
};
80+
81+
let status = match arg.path().word_sym() {
82+
Some(sym::off) => CoverageStatus::Off,
83+
Some(sym::on) => CoverageStatus::On,
84+
None | Some(_) => {
85+
fail_incorrect_argument(arg.span());
86+
return None;
87+
}
88+
};
89+
90+
Some(AttributeKind::Coverage(cx.attr_span, status))
91+
}
92+
}
93+
5594
pub(crate) struct ExportNameParser;
5695

5796
impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
1616

1717
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
1818
use crate::attributes::codegen_attrs::{
19-
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser,
20-
TrackCallerParser, UsedParser,
19+
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
20+
TargetFeatureParser, TrackCallerParser, UsedParser,
2121
};
2222
use crate::attributes::confusables::ConfusablesParser;
2323
use crate::attributes::deprecation::DeprecationParser;
@@ -135,6 +135,7 @@ attribute_parsers!(
135135
// tidy-alphabetical-end
136136

137137
// tidy-alphabetical-start
138+
Single<CoverageParser>,
138139
Single<DeprecationParser>,
139140
Single<DummyParser>,
140141
Single<ExportNameParser>,
@@ -423,6 +424,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
423424
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
424425
possibilities,
425426
strings: false,
427+
list: false,
428+
},
429+
})
430+
}
431+
432+
pub(crate) fn expected_specific_argument_and_list(
433+
&self,
434+
span: Span,
435+
possibilities: Vec<&'static str>,
436+
) -> ErrorGuaranteed {
437+
self.emit_err(AttributeParseError {
438+
span,
439+
attr_span: self.attr_span,
440+
template: self.template.clone(),
441+
attribute: self.attr_path.clone(),
442+
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
443+
possibilities,
444+
strings: false,
445+
list: true,
426446
},
427447
})
428448
}
@@ -440,6 +460,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
440460
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
441461
possibilities,
442462
strings: true,
463+
list: false,
443464
},
444465
})
445466
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -516,15 +516,22 @@ pub(crate) struct NakedFunctionIncompatibleAttribute {
516516

517517
pub(crate) enum AttributeParseErrorReason {
518518
ExpectedNoArgs,
519-
ExpectedStringLiteral { byte_string: Option<Span> },
519+
ExpectedStringLiteral {
520+
byte_string: Option<Span>,
521+
},
520522
ExpectedIntegerLiteral,
521523
ExpectedAtLeastOneArgument,
522524
ExpectedSingleArgument,
523525
ExpectedList,
524526
UnexpectedLiteral,
525527
ExpectedNameValue(Option<Symbol>),
526528
DuplicateKey(Symbol),
527-
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
529+
ExpectedSpecificArgument {
530+
possibilities: Vec<&'static str>,
531+
strings: bool,
532+
/// Should we tell the user to write a list when they didn't?
533+
list: bool,
534+
},
528535
}
529536

530537
pub(crate) struct AttributeParseError {
@@ -592,7 +599,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
592599
format!("expected this to be of the form `{name} = \"...\"`"),
593600
);
594601
}
595-
AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
602+
AttributeParseErrorReason::ExpectedSpecificArgument {
603+
possibilities,
604+
strings,
605+
list: false,
606+
} => {
596607
let quote = if strings { '"' } else { '`' };
597608
match possibilities.as_slice() {
598609
&[] => {}
@@ -618,6 +629,38 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
618629
}
619630
}
620631
}
632+
AttributeParseErrorReason::ExpectedSpecificArgument {
633+
possibilities,
634+
strings,
635+
list: true,
636+
} => {
637+
let quote = if strings { '"' } else { '`' };
638+
match possibilities.as_slice() {
639+
&[] => {}
640+
&[x] => {
641+
diag.span_label(
642+
self.span,
643+
format!(
644+
"this attribute is only valid with {quote}{x}{quote} as an argument"
645+
),
646+
);
647+
}
648+
[first, second] => {
649+
diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument"));
650+
}
651+
[first @ .., second_to_last, last] => {
652+
let mut res = String::new();
653+
for i in first {
654+
res.push_str(&format!("{quote}{i}{quote}, "));
655+
}
656+
res.push_str(&format!(
657+
"{quote}{second_to_last}{quote} or {quote}{last}{quote}"
658+
));
659+
660+
diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}"));
661+
}
662+
}
663+
}
621664
}
622665

623666
let suggestions = self.template.suggestions(false, &name);

compiler/rustc_mir_transform/src/coverage/query.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
use rustc_attr_data_structures::{AttributeKind, CoverageStatus, find_attr};
12
use rustc_index::bit_set::DenseBitSet;
23
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
34
use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind};
45
use rustc_middle::mir::{Body, Statement, StatementKind};
56
use rustc_middle::ty::{self, TyCtxt};
67
use rustc_middle::util::Providers;
78
use rustc_span::def_id::LocalDefId;
8-
use rustc_span::sym;
99
use tracing::trace;
1010

1111
use crate::coverage::counters::node_flow::make_node_counters;
@@ -58,26 +58,20 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
5858
/// Query implementation for `coverage_attr_on`.
5959
fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
6060
// Check for annotations directly on this def.
61-
if let Some(attr) = tcx.get_attr(def_id, sym::coverage) {
62-
match attr.meta_item_list().as_deref() {
63-
Some([item]) if item.has_name(sym::off) => return false,
64-
Some([item]) if item.has_name(sym::on) => return true,
65-
Some(_) | None => {
66-
// Other possibilities should have been rejected by `rustc_parse::validate_attr`.
67-
// Use `span_delayed_bug` to avoid an ICE in failing builds (#127880).
68-
tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute");
69-
}
61+
if let Some(coverage_status) =
62+
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status)
63+
{
64+
*coverage_status == CoverageStatus::On
65+
} else {
66+
match tcx.opt_local_parent(def_id) {
67+
// Check the parent def (and so on recursively) until we find an
68+
// enclosing attribute or reach the crate root.
69+
Some(parent) => tcx.coverage_attr_on(parent),
70+
// We reached the crate root without seeing a coverage attribute, so
71+
// allow coverage instrumentation by default.
72+
None => true,
7073
}
7174
}
72-
73-
match tcx.opt_local_parent(def_id) {
74-
// Check the parent def (and so on recursively) until we find an
75-
// enclosing attribute or reach the crate root.
76-
Some(parent) => tcx.coverage_attr_on(parent),
77-
// We reached the crate root without seeing a coverage attribute, so
78-
// allow coverage instrumentation by default.
79-
None => true,
80-
}
8175
}
8276

8377
/// Query implementation for `coverage_ids_info`.

compiler/rustc_parse/src/validate_attr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ pub fn check_builtin_meta_item(
316316
| sym::rustc_layout_scalar_valid_range_start
317317
| sym::rustc_layout_scalar_valid_range_end
318318
| sym::no_implicit_prelude
319+
| sym::coverage
319320
) {
320321
return;
321322
}

compiler/rustc_passes/src/check_attr.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
269269
&Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => {
270270
self.check_rustc_std_internal_symbol(attr_span, span, target)
271271
}
272+
&Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => {
273+
self.check_coverage(attr_span, span, target)
274+
}
272275
Attribute::Unparsed(attr_item) => {
273276
style = Some(attr_item.style);
274277
match attr.path().as_slice() {
@@ -278,7 +281,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
278281
[sym::diagnostic, sym::on_unimplemented, ..] => {
279282
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
280283
}
281-
[sym::coverage, ..] => self.check_coverage(attr, span, target),
282284
[sym::no_sanitize, ..] => {
283285
self.check_no_sanitize(attr, span, target)
284286
}
@@ -566,7 +568,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
566568

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

@@ -589,7 +591,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
589591
}
590592

591593
self.dcx().emit_err(errors::CoverageAttributeNotAllowed {
592-
attr_span: attr.span(),
594+
attr_span,
593595
not_fn_impl_mod,
594596
no_body,
595597
help: (),

tests/ui/attributes/malformed-attrs.stderr

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,6 @@ error: malformed `crate_name` attribute input
4040
LL | #[crate_name]
4141
| ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]`
4242

43-
error: malformed `coverage` attribute input
44-
--> $DIR/malformed-attrs.rs:90:1
45-
|
46-
LL | #[coverage]
47-
| ^^^^^^^^^^^
48-
|
49-
help: the following are the possible correct uses
50-
|
51-
LL | #[coverage(off)]
52-
| +++++
53-
LL | #[coverage(on)]
54-
| ++++
55-
5643
error: malformed `no_sanitize` attribute input
5744
--> $DIR/malformed-attrs.rs:92:1
5845
|
@@ -466,6 +453,19 @@ error[E0539]: malformed `link_section` attribute input
466453
LL | #[link_section]
467454
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]`
468455

456+
error[E0539]: malformed `coverage` attribute input
457+
--> $DIR/malformed-attrs.rs:90:1
458+
|
459+
LL | #[coverage]
460+
| ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
461+
|
462+
help: try changing it to one of the following valid forms of the attribute
463+
|
464+
LL | #[coverage(off)]
465+
| +++++
466+
LL | #[coverage(on)]
467+
| ++++
468+
469469
error[E0565]: malformed `no_implicit_prelude` attribute input
470470
--> $DIR/malformed-attrs.rs:97:1
471471
|

0 commit comments

Comments
 (0)