Skip to content

Commit b6110e3

Browse files
committed
Port #[coverage] to the new attribute system
1 parent 7f2065a commit b6110e3

File tree

15 files changed

+359
-389
lines changed

15 files changed

+359
-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
@@ -249,6 +265,9 @@ pub enum AttributeKind {
249265
/// Represents `#[const_trait]`.
250266
ConstTrait(Span),
251267

268+
/// Represents `#[coverage]`.
269+
Coverage(Span, CoverageStatus),
270+
252271
///Represents `#[rustc_deny_explicit_impl]`.
253272
DenyExplicitImpl(Span),
254273

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ impl AttributeKind {
2828
ConstStability { .. } => Yes,
2929
ConstStabilityIndirect => No,
3030
ConstTrait(..) => No,
31+
Coverage(..) => No,
3132
DenyExplicitImpl(..) => No,
3233
Deprecation { .. } => Yes,
3334
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_single_argument(args.span);
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: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ 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, OmitGdbPrettyPrinterSectionParser,
20-
OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
19+
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser,
20+
OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser,
21+
UsedParser,
2122
};
2223
use crate::attributes::confusables::ConfusablesParser;
2324
use crate::attributes::deprecation::DeprecationParser;
@@ -137,6 +138,7 @@ attribute_parsers!(
137138
// tidy-alphabetical-end
138139

139140
// tidy-alphabetical-start
141+
Single<CoverageParser>,
140142
Single<DeprecationParser>,
141143
Single<DummyParser>,
142144
Single<ExportNameParser>,
@@ -428,6 +430,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
428430
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
429431
possibilities,
430432
strings: false,
433+
list: false,
434+
},
435+
})
436+
}
437+
438+
pub(crate) fn expected_specific_argument_and_list(
439+
&self,
440+
span: Span,
441+
possibilities: Vec<&'static str>,
442+
) -> ErrorGuaranteed {
443+
self.emit_err(AttributeParseError {
444+
span,
445+
attr_span: self.attr_span,
446+
template: self.template.clone(),
447+
attribute: self.attr_path.clone(),
448+
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
449+
possibilities,
450+
strings: false,
451+
list: true,
431452
},
432453
})
433454
}
@@ -445,6 +466,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
445466
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
446467
possibilities,
447468
strings: true,
469+
list: false,
448470
},
449471
})
450472
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

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

526526
pub(crate) enum AttributeParseErrorReason {
527527
ExpectedNoArgs,
528-
ExpectedStringLiteral { byte_string: Option<Span> },
528+
ExpectedStringLiteral {
529+
byte_string: Option<Span>,
530+
},
529531
ExpectedIntegerLiteral,
530532
ExpectedAtLeastOneArgument,
531533
ExpectedSingleArgument,
532534
ExpectedList,
533535
UnexpectedLiteral,
534536
ExpectedNameValue(Option<Symbol>),
535537
DuplicateKey(Symbol),
536-
ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
538+
ExpectedSpecificArgument {
539+
possibilities: Vec<&'static str>,
540+
strings: bool,
541+
/// Should we tell the user to write a list when they didn't?
542+
list: bool,
543+
},
537544
}
538545

539546
pub(crate) struct AttributeParseError {
@@ -601,7 +608,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
601608
format!("expected this to be of the form `{name} = \"...\"`"),
602609
);
603610
}
604-
AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
611+
AttributeParseErrorReason::ExpectedSpecificArgument {
612+
possibilities,
613+
strings,
614+
list: false,
615+
} => {
605616
let quote = if strings { '"' } else { '`' };
606617
match possibilities.as_slice() {
607618
&[] => {}
@@ -627,6 +638,38 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
627638
}
628639
}
629640
}
641+
AttributeParseErrorReason::ExpectedSpecificArgument {
642+
possibilities,
643+
strings,
644+
list: true,
645+
} => {
646+
let quote = if strings { '"' } else { '`' };
647+
match possibilities.as_slice() {
648+
&[] => {}
649+
&[x] => {
650+
diag.span_label(
651+
self.span,
652+
format!(
653+
"this attribute is only valid with {quote}{x}{quote} as an argument"
654+
),
655+
);
656+
}
657+
[first, second] => {
658+
diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument"));
659+
}
660+
[first @ .., second_to_last, last] => {
661+
let mut res = String::new();
662+
for i in first {
663+
res.push_str(&format!("{quote}{i}{quote}, "));
664+
}
665+
res.push_str(&format!(
666+
"{quote}{second_to_last}{quote} or {quote}{last}{quote}"
667+
));
668+
669+
diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}"));
670+
}
671+
}
672+
}
630673
}
631674

632675
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
@@ -319,6 +319,7 @@ pub fn check_builtin_meta_item(
319319
| sym::rustc_layout_scalar_valid_range_end
320320
| sym::no_implicit_prelude
321321
| sym::automatically_derived
322+
| sym::coverage
322323
) {
323324
return;
324325
}

compiler/rustc_passes/src/check_attr.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
284284
&Attribute::Parsed(AttributeKind::StdInternalSymbol(attr_span)) => {
285285
self.check_rustc_std_internal_symbol(attr_span, span, target)
286286
}
287+
&Attribute::Parsed(AttributeKind::Coverage(attr_span, _)) => {
288+
self.check_coverage(attr_span, span, target)
289+
}
287290
Attribute::Unparsed(attr_item) => {
288291
style = Some(attr_item.style);
289292
match attr.path().as_slice() {
@@ -293,7 +296,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
293296
[sym::diagnostic, sym::on_unimplemented, ..] => {
294297
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
295298
}
296-
[sym::coverage, ..] => self.check_coverage(attr, span, target),
297299
[sym::no_sanitize, ..] => {
298300
self.check_no_sanitize(attr, span, target)
299301
}
@@ -585,7 +587,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
585587

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

@@ -608,7 +610,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
608610
}
609611

610612
self.dcx().emit_err(errors::CoverageAttributeNotAllowed {
611-
attr_span: attr.span(),
613+
attr_span,
612614
not_fn_impl_mod,
613615
no_body,
614616
help: (),

tests/ui/attributes/malformed-attrs.stderr

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

37-
error: malformed `coverage` attribute input
38-
--> $DIR/malformed-attrs.rs:90:1
39-
|
40-
LL | #[coverage]
41-
| ^^^^^^^^^^^
42-
|
43-
help: the following are the possible correct uses
44-
|
45-
LL | #[coverage(off)]
46-
| +++++
47-
LL | #[coverage(on)]
48-
| ++++
49-
5037
error: malformed `no_sanitize` attribute input
5138
--> $DIR/malformed-attrs.rs:92:1
5239
|
@@ -457,6 +444,19 @@ error[E0539]: malformed `link_section` attribute input
457444
LL | #[link_section]
458445
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]`
459446

447+
error[E0539]: malformed `coverage` attribute input
448+
--> $DIR/malformed-attrs.rs:90:1
449+
|
450+
LL | #[coverage]
451+
| ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument
452+
|
453+
help: try changing it to one of the following valid forms of the attribute
454+
|
455+
LL | #[coverage(off)]
456+
| +++++
457+
LL | #[coverage(on)]
458+
| ++++
459+
460460
error[E0565]: malformed `no_implicit_prelude` attribute input
461461
--> $DIR/malformed-attrs.rs:97:1
462462
|

0 commit comments

Comments
 (0)