Rollup merge of #143891 - scrabsha:push-xxtttopqoprr, r=jdonszelmann

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

r? ``````@jdonszelmann``````
This commit is contained in:
Matthias Krüger
2025-07-18 04:27:52 +02:00
committed by GitHub
17 changed files with 434 additions and 462 deletions

View File

@@ -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};
@@ -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 {

View File

@@ -17,8 +17,9 @@ use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::codegen_attrs::{
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser,
OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser,
OmitGdbPrettyPrinterSectionParser, OptimizeParser, TargetFeatureParser, TrackCallerParser,
UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
@@ -139,6 +140,7 @@ attribute_parsers!(
// tidy-alphabetical-end
// tidy-alphabetical-start
Single<CoverageParser>,
Single<DeprecationParser>,
Single<DummyParser>,
Single<ExportNameParser>,
@@ -452,6 +454,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,
},
})
}
@@ -469,6 +490,7 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
reason: AttributeParseErrorReason::ExpectedSpecificArgument {
possibilities,
strings: true,
list: false,
},
})
}

View File

@@ -533,7 +533,9 @@ pub(crate) struct LinkOrdinalOutOfRange {
pub(crate) enum AttributeParseErrorReason {
ExpectedNoArgs,
ExpectedStringLiteral { byte_string: Option<Span> },
ExpectedStringLiteral {
byte_string: Option<Span>,
},
ExpectedIntegerLiteral,
ExpectedAtLeastOneArgument,
ExpectedSingleArgument,
@@ -541,7 +543,12 @@ pub(crate) enum AttributeParseErrorReason {
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 {
@@ -615,7 +622,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() {
&[] => {}
@@ -641,6 +652,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);