Rollup merge of #155696 - scrabsha:push-kxqstpltlwzn, r=JonathanBrouwer

Add a higher-level API for parsing attributes
This commit is contained in:
Jonathan Brouwer
2026-04-25 23:07:52 +02:00
committed by GitHub
23 changed files with 110 additions and 151 deletions
@@ -81,7 +81,7 @@ fn parse_unstable<S: Stage>(
) -> impl IntoIterator<Item = Symbol> {
let mut res = Vec::new();
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
cx.emit_err(session_diagnostics::ExpectsFeatureList {
span: cx.attr_span,
name: symbol.to_ident_string(),
@@ -44,13 +44,9 @@ pub fn parse_cfg<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> Option<CfgEntry> {
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let Some(single) = list.single() else {
let Some(single) = list.as_single() else {
let target = cx.target;
let mut adcx = cx.adcx();
if list.is_empty() {
@@ -93,7 +89,7 @@ pub fn parse_cfg_entry<S: Stage>(
MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() {
ArgParser::List(list) => match meta.path().word_sym() {
Some(sym::not) => {
let Some(single) = list.single() else {
let Some(single) = list.as_single() else {
return Err(cx.adcx().expected_single_argument(list.span, list.len()));
};
CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span)
@@ -136,7 +132,7 @@ fn parse_cfg_entry_version<S: Stage>(
meta_span: Span,
) -> Result<CfgEntry, ErrorGuaranteed> {
try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option());
let Some(version) = list.single() else {
let Some(version) = list.as_single() else {
return Err(
cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span })
);
@@ -24,7 +24,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
Some(sym::size) => OptimizeAttr::Size,
@@ -75,7 +75,7 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let arg = cx.single_element_list(args, cx.attr_span)?;
let arg = cx.expect_single_element_list(args, cx.attr_span)?;
let mut fail_incorrect_argument =
|span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);
@@ -371,8 +371,7 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
let used_by = match args {
ArgParser::NoArgs => UsedBy::Default,
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
let Some(l) = cx.expect_single(list) else {
return;
};
@@ -463,9 +462,7 @@ fn parse_tf_attribute<S: Stage>(
args: &ArgParser,
) -> impl IntoIterator<Item = (Symbol, Span)> {
let mut features = Vec::new();
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return features;
};
if list.is_empty() {
@@ -588,11 +585,7 @@ impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut on_set = SanitizerSet::empty();
let mut off_set = SanitizerSet::empty();
@@ -719,11 +712,7 @@ impl<S: Stage> SingleAttributeParser<S> for PatchableFunctionEntryParser {
const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(meta_item_list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let meta_item_list = cx.expect_list(args, cx.attr_span)?;
let mut prefix = None;
let mut entry = None;
@@ -12,11 +12,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
&[sym::rustc_confusables],
template!(List: &[r#""name1", "name2", ..."#]),
|this, cx, args| {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return;
};
let Some(list) = cx.expect_list(args, cx.attr_span) else { return };
if list.is_empty() {
cx.emit_err(EmptyConfusables { span: cx.attr_span });
@@ -314,9 +314,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return Vec::new();
};
@@ -362,9 +360,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return Vec::new();
};
@@ -20,7 +20,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(mi) = single.meta_item() else {
cx.adcx().expected_name_value(single.span(), None);
return None;
@@ -344,7 +344,7 @@ fn parse_directive_items<'p, S: Stage>(
}
(Mode::RustcOnUnimplemented, sym::on) => {
if is_root {
let items = or_malformed!(item.args().list()?);
let items = or_malformed!(item.args().as_list()?);
let mut iter = items.mixed();
let condition: &MetaItemOrLitParser = match iter.next() {
Some(c) => c,
@@ -554,7 +554,7 @@ fn parse_predicate(input: &MetaItemOrLitParser) -> Result<Predicate, InvalidOnCl
sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis)?)),
sym::all => Ok(Predicate::All(parse_predicate_sequence(mis)?)),
sym::not => {
if let Some(single) = mis.single() {
if let Some(single) = mis.as_single() {
Ok(Predicate::Not(Box::new(parse_predicate(single)?)))
} else {
Err(InvalidOnClause::ExpectedOnePredInNot { span: mis.span })
@@ -199,7 +199,7 @@ fn parse_single_test_doc_attr_item<S: Stage>(
self.attribute.no_crate_inject = Some(path.span())
}
Some(sym::attr) => {
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
// FIXME: remove this method once merged and uncomment the line below instead.
// cx.expected_list(cx.attr_span, args);
let span = cx.attr_span;
@@ -587,7 +587,7 @@ macro_rules! string_arg_and_crate_level {
}),
Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args),
Some(sym::test) => {
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
|dcx, level| DocTestTakesList.into_diag(dcx, level),
@@ -37,10 +37,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
match args {
ArgParser::NoArgs => Some(AttributeKind::Inline(InlineAttr::Hint, cx.attr_span)),
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
return None;
};
let l = cx.expect_single(list)?;
match l.meta_item().and_then(|i| i.path().word_sym()) {
Some(sym::always) => {
@@ -78,10 +75,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
let reason = match args {
ArgParser::NoArgs => None,
ArgParser::List(list) => {
let Some(l) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
return None;
};
let l = cx.expect_single(list)?;
let Some(reason) = l.lit().and_then(|i| i.kind.str()) else {
cx.adcx().expected_string_literal(l.span(), l.lit());
@@ -19,7 +19,7 @@ impl<S: Stage> SingleAttributeParser<S> for InstructionSetParser {
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
const POSSIBLE_SYMBOLS: &[Symbol] = &[sym::arm_a32, sym::arm_t32];
const POSSIBLE_ARM_SYMBOLS: &[Symbol] = &[sym::a32, sym::t32];
let maybe_meta_item = cx.single_element_list(args, cx.attr_span)?;
let maybe_meta_item = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(meta_item) = maybe_meta_item.meta_item() else {
cx.adcx().expected_specific_argument(maybe_meta_item.span(), POSSIBLE_SYMBOLS);
@@ -391,7 +391,7 @@ fn parse_link_cfg<S: Stage>(
cx.adcx().duplicate_key(item.span(), sym::cfg);
return true;
}
let Some(link_cfg) = cx.single_element_list(item.args(), item.span()) else {
let Some(link_cfg) = cx.expect_single_element_list(item.args(), item.span()) else {
return true;
};
if !features.link_cfg() {
@@ -142,7 +142,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
let local_inner_macros = match args {
ArgParser::NoArgs => false,
ArgParser::List(list) => {
let Some(l) = list.single() else {
let Some(l) = list.as_single() else {
cx.adcx().warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
return None;
};
@@ -174,7 +174,7 @@ impl<S: Stage> SingleAttributeParser<S> for CollapseDebugInfoParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(mi) = single.meta_item() else {
cx.adcx().expected_not_literal(single.span());
return None;
@@ -57,7 +57,7 @@ fn parse_derive_like<S: Stage>(
args: &ArgParser,
trait_name_mandatory: bool,
) -> Option<(Option<Symbol>, ThinVec<Symbol>)> {
let Some(list) = args.list() else {
let Some(list) = args.as_list() else {
// For #[rustc_builtin_macro], it is permitted to leave out the trait name
if args.no_args().is_ok() && !trait_name_mandatory {
return Some((None, ThinVec::new()));
@@ -101,10 +101,7 @@ fn parse_derive_like<S: Stage>(
cx.adcx().expected_specific_argument(attrs.span(), &[sym::attributes]);
return None;
}
let Some(attr_list) = attr_list.args().list() else {
cx.adcx().expected_list(attrs.span(), attr_list.args());
return None;
};
let attr_list = cx.expect_list(attr_list.args(), attrs.span())?;
// Parse item in `attributes(...)` argument
for attr in attr_list.mixed() {
@@ -22,11 +22,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut dialect = None;
let mut phase = None;
@@ -32,9 +32,7 @@ fn extend(
) -> impl IntoIterator<Item = Self::Item> {
let mut reprs = Vec::new();
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return reprs;
};
@@ -197,7 +195,7 @@ fn parse_repr_align<S: Stage>(
) -> Option<ReprAttr> {
use AlignKind::*;
let Some(align) = list.single() else {
let Some(align) = list.as_single() else {
match align_kind {
Packed => {
cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
@@ -296,8 +294,7 @@ fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParse
cx.adcx().expected_list(attr_span, args);
}
ArgParser::List(list) => {
let Some(align) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
let Some(align) = cx.expect_single(list) else {
return;
};
@@ -96,9 +96,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::List(items) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(items) = cx.expect_list(args, cx.attr_span) else {
return vec![];
};
@@ -30,11 +30,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcMustImplementOneOfParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const TEMPLATE: AttributeTemplate = template!(List: &["function1, function2, ..."]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let span = cx.attr_span;
cx.adcx().expected_list(span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut fn_names = ThinVec::new();
@@ -130,11 +126,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLegacyConstGenericsParser {
const TEMPLATE: AttributeTemplate = template!(List: &["N"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let ArgParser::List(meta_items) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let meta_items = cx.expect_list(args, cx.attr_span)?;
let mut parsed_indexes = ThinVec::new();
let mut errored = false;
@@ -185,7 +177,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLintOptDenyFieldAccessParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Field)]);
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let arg = cx.single_element_list(args, cx.attr_span)?;
let arg = cx.expect_single_element_list(args, cx.attr_span)?;
let MetaItemOrLitParser::Lit(MetaItemLit { kind: LitKind::Str(lint_message, _), .. }) = arg
else {
@@ -210,11 +202,7 @@ fn parse_cgu_fields<S: Stage>(
args: &ArgParser,
accepts_kind: bool,
) -> Option<(Symbol, Symbol, Option<CguKind>)> {
let Some(args) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let args = cx.expect_list(args, cx.attr_span)?;
let mut cfg = None::<(Symbol, Span)>;
let mut module = None::<(Symbol, Span)>;
@@ -359,7 +347,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcDeprecatedSafe2024Parser {
const TEMPLATE: AttributeTemplate = template!(List: &[r#"audit_that = "...""#]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(arg) = single.meta_item() else {
cx.adcx().expected_name_value(single.span(), None);
@@ -418,11 +406,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcNeverTypeOptionsParser {
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut fallback = None::<Ident>;
let mut diverging_block_default = None::<Ident>;
@@ -703,9 +687,7 @@ fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
let Some(list) = cx.expect_list(args, cx.attr_span) else {
return ThinVec::new();
};
@@ -825,11 +807,8 @@ fn extend(
if !cx.cx.sess.opts.unstable_opts.query_dep_graph {
cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
}
let Some(list) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
let mut except = None;
let mut loaded_from_disk = None;
let mut cfg = None;
@@ -926,11 +905,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
match args {
ArgParser::NoArgs => Some(AttributeKind::RustcIfThisChanged(cx.attr_span, None)),
ArgParser::List(list) => {
let Some(item) = list.single() else {
let attr_span = cx.attr_span;
cx.adcx().expected_single_argument(attr_span, list.len());
return None;
};
let item = cx.expect_single(list)?;
let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
cx.adcx().expected_identifier(item.span());
return None;
@@ -988,7 +963,7 @@ fn extend(
if !cx.cx.sess.opts.unstable_opts.query_dep_graph {
cx.emit_err(AttributeRequiresOpt { span: cx.attr_span, opt: "-Z query-dep-graph" });
}
let item = cx.single_element_list(args, cx.attr_span)?;
let item = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(ident) = item.meta_item().and_then(|item| item.ident()) else {
cx.adcx().expected_identifier(item.span());
return None;
@@ -311,11 +311,7 @@ pub(crate) fn parse_stability<S: Stage>(
let mut feature = None;
let mut since = None;
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
for param in list.mixed() {
let param_span = param.span();
@@ -383,11 +379,7 @@ pub(crate) fn parse_unstability<S: Stage>(
let mut implied_by = None;
let mut old_name = None;
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
for param in list.mixed() {
let Some(param) = param.meta_item() else {
@@ -503,11 +495,7 @@ fn extend(
return None;
}
let ArgParser::List(list) = args else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let list = cx.expect_list(args, cx.attr_span)?;
for param in list.mixed() {
let Some(param) = param.meta_item() else {
@@ -28,10 +28,11 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
Some(str_value)
}
ArgParser::List(list) => {
let help = list.single().and_then(|item| item.meta_item()).and_then(|item| {
item.args().no_args().ok()?;
Some(item.path().to_string())
});
let help =
list.as_single().and_then(|item| item.meta_item()).and_then(|item| {
item.args().no_args().ok()?;
Some(item.path().to_string())
});
cx.adcx().warn_ill_formed_attribute_input_with_help(
ILL_FORMED_ATTRIBUTE_INPUT,
help,
@@ -71,10 +72,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
Some(str_value)
}
ArgParser::List(list) => {
let Some(single) = list.single() else {
cx.adcx().expected_single_argument(list.span, list.len());
return None;
};
let single = cx.expect_single(list)?;
let Some(single) = single.meta_item() else {
cx.adcx().expected_name_value(single.span(), Some(sym::expected));
return None;
@@ -140,17 +138,13 @@ impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(args) = args.list() else {
let Some(args) = args.as_list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_specific_argument_and_list(attr_span, &[sym::assert_eq, sym::debug]);
return None;
};
let Some(arg) = args.single() else {
let attr_span = cx.attr_span;
cx.adcx().expected_single_argument(attr_span, args.len());
return None;
};
let arg = cx.expect_single(args)?;
let mut fail_incorrect_argument =
|span| cx.adcx().expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
@@ -203,7 +197,7 @@ impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
const TEMPLATE: AttributeTemplate = template!(List: &["path"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(meta) = single.meta_item() else {
cx.adcx().expected_not_literal(single.span());
@@ -17,11 +17,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcSkipDuringMethodDispatchParser
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let mut array = false;
let mut boxed_slice = false;
let Some(args) = args.list() else {
let attr_span = cx.attr_span;
cx.adcx().expected_list(attr_span, args);
return None;
};
let args = cx.expect_list(args, cx.attr_span)?;
if args.is_empty() {
cx.adcx().expected_at_least_one_argument(args.span);
return None;
@@ -41,7 +41,7 @@ pub(crate) fn parse_single_integer<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> Option<u128> {
let single = cx.single_element_list(args, cx.attr_span)?;
let single = cx.expect_single_element_list(args, cx.attr_span)?;
let Some(lit) = single.lit() else {
cx.adcx().expected_integer_literal(single.span());
return None;
+49 -3
View File
@@ -61,7 +61,7 @@
use crate::attributes::traits::*;
use crate::attributes::transparency::*;
use crate::attributes::{AttributeParser as _, AttributeSafety, Combine, Single, WithoutArgs};
use crate::parser::{ArgParser, MetaItemOrLitParser, RefPathParser};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, RefPathParser};
use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, AttributeParseErrorSuggestions,
ParsedDescription,
@@ -554,7 +554,7 @@ pub(crate) fn adcx(&mut self) -> AttributeDiagnosticContext<'_, 'f, 'sess, S> {
///
/// The provided span is used as a fallback for diagnostic generation in case `arg` does not
/// contain any. It should be the span of the node that contains `arg`.
pub(crate) fn single_element_list<'arg>(
pub(crate) fn expect_single_element_list<'arg>(
&mut self,
arg: &'arg ArgParser,
span: Span,
@@ -564,13 +564,59 @@ pub(crate) fn single_element_list<'arg>(
return None;
};
let Some(single) = l.single() else {
let Some(single) = l.as_single() else {
self.adcx().expected_single_argument(l.span, l.len());
return None;
};
Some(single)
}
/// Asserts that an [`ArgParser`] is a list and returns it, or emits an error and returns
/// `None`.
///
/// Some examples:
///
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
///
/// This is a higher-level (and harder to misuse) wrapper over [`ArgParser::as_list`]. That
/// allows using `?` when the attribute parsing function allows it. You may still want to use
/// [`ArgParser::as_list`] for the following reasons:
///
/// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]).
/// - The attribute can be parsed in multiple ways and it does not make sense to emit an error.
pub(crate) fn expect_list<'arg>(
&mut self,
args: &'arg ArgParser,
span: Span,
) -> Option<&'arg MetaItemListParser> {
let list = args.as_list();
if list.is_none() {
self.adcx().expected_list(span, args);
}
list
}
/// Asserts that a [`MetaItemListParser`] contains a single element and returns it, or emits an
/// error and returns `None`.
///
/// This is a higher-level (and harder to misuse) wrapper over [`MetaItemListParser::as_single`].
/// That allows using `?` to early return. You may still want to use
/// [`MetaItemListParser::as_single`] for the following reasons:
///
/// - You want to emit your own diagnostics (for instance, with [`SharedContext::emit_err`]).
/// - The attribute can be parsed in multiple ways and it does not make sense to emit an error.
pub(crate) fn expect_single<'arg>(
&mut self,
list: &'arg MetaItemListParser,
) -> Option<&'arg MetaItemOrLitParser> {
let single = list.as_single();
if single.is_none() {
self.adcx().expected_single_argument(list.span, list.len());
}
single
}
}
impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
+4 -3
View File
@@ -176,7 +176,7 @@ pub fn from_attr_args<'sess>(
///
/// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
/// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
pub fn list(&self) -> Option<&MetaItemListParser> {
pub fn as_list(&self) -> Option<&MetaItemListParser> {
match self {
Self::List(l) => Some(l),
Self::NameValue(_) | Self::NoArgs => None,
@@ -255,6 +255,7 @@ pub fn meta_item(&self) -> Option<&MetaItemParser> {
}
}
// FIXME(scrabsha): once #155696 is merged, update this and mention the higher-level APIs.
/// Utility that deconstructs a MetaItem into usable parts.
///
/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
@@ -263,7 +264,7 @@ pub fn meta_item(&self) -> Option<&MetaItemParser> {
/// MetaItems consist of some path, and some args. The args could be empty. In other words:
///
/// - `name` -> args are empty
/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses
/// - `name(...)` -> args are a [`list`](ArgParser::as_list), which is the bit between the parentheses
/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
/// `= value` part
///
@@ -694,7 +695,7 @@ pub fn is_empty(&self) -> bool {
/// Returns Some if the list contains only a single element.
///
/// Inside the Some is the parser to parse this single element.
pub fn single(&self) -> Option<&MetaItemOrLitParser> {
pub fn as_single(&self) -> Option<&MetaItemOrLitParser> {
let mut iter = self.mixed();
iter.next().filter(|_| iter.next().is_none())
}