Rollup merge of #141271 - nnethercote:attr-streamline, r=jdonszelmann

Streamline some attr parsing APIs

r? ``@jdonszelmann``
This commit is contained in:
Matthias Krüger
2025-06-04 16:24:06 +02:00
committed by GitHub
6 changed files with 20 additions and 65 deletions

View File

@@ -53,7 +53,7 @@ fn parse_unstable<'a>(
for param in list.mixed() {
let param_span = param.span();
if let Some(ident) = param.meta_item().and_then(|i| i.path_without_args().word()) {
if let Some(ident) = param.meta_item().and_then(|i| i.path().word()) {
res.push(ident.name);
} else {
cx.emit_err(session_diagnostics::ExpectsFeatures {

View File

@@ -79,7 +79,7 @@ impl SingleAttributeParser for DeprecationParser {
return None;
};
let ident_name = param.path_without_args().word_sym();
let ident_name = param.path().word_sym();
match ident_name {
Some(name @ sym::since) => {
@@ -102,7 +102,7 @@ impl SingleAttributeParser for DeprecationParser {
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path_without_args().to_string(),
item: param.path().to_string(),
expected: if features.deprecated_suggestion() {
&["since", "note", "suggestion"]
} else {

View File

@@ -96,7 +96,7 @@ fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<Repr
// FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
// structure.
let (name, ident_span) = if let Some(ident) = param.path_without_args().word() {
let (name, ident_span) = if let Some(ident) = param.path().word() {
(Some(ident.name), ident.span)
} else {
(None, DUMMY_SP)

View File

@@ -204,7 +204,7 @@ fn insert_value_into_option_or_error(
if item.is_some() {
cx.emit_err(session_diagnostics::MultipleItem {
span: param.span(),
item: param.path_without_args().to_string(),
item: param.path().to_string(),
});
None
} else if let Some(v) = param.args().name_value()
@@ -242,13 +242,13 @@ pub(crate) fn parse_stability(
return None;
};
match param.path_without_args().word_sym() {
match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::since) => insert_value_into_option_or_error(cx, &param, &mut since)?,
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param_span,
item: param.path_without_args().to_string(),
item: param.path().to_string(),
expected: &["feature", "since"],
});
return None;
@@ -310,7 +310,7 @@ pub(crate) fn parse_unstability(
return None;
};
match param.path_without_args().word_sym() {
match param.path().word_sym() {
Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
Some(sym::reason) => insert_value_into_option_or_error(cx, &param, &mut reason)?,
Some(sym::issue) => {
@@ -349,7 +349,7 @@ pub(crate) fn parse_unstability(
_ => {
cx.emit_err(session_diagnostics::UnknownMetaItem {
span: param.span(),
item: param.path_without_args().to_string(),
item: param.path().to_string(),
expected: &["feature", "reason", "issue", "soft", "implied_by"],
});
return None;

View File

@@ -264,7 +264,8 @@ impl<'sess> AttributeParser<'sess> {
// }
ast::AttrKind::Normal(n) => {
let parser = MetaItemParser::from_attr(n, self.dcx());
let (path, args) = parser.deconstruct();
let path = parser.path();
let args = parser.args();
let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
if let Some(accept) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {

View File

@@ -252,9 +252,13 @@ impl<'a> MetaItemParser<'a> {
}
}
/// Gets just the path, without the args.
pub fn path_without_args(&self) -> PathParser<'a> {
self.path.clone()
/// Gets just the path, without the args. Some examples:
///
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
/// - `#[inline]`: `inline` is a single segment path
pub fn path(&self) -> &PathParser<'a> {
&self.path
}
/// Gets just the args parser, without caring about the path.
@@ -262,50 +266,14 @@ impl<'a> MetaItemParser<'a> {
&self.args
}
pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
(self.path_without_args(), self.args())
}
/// Asserts that this MetaItem starts with a path. Some examples:
///
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
/// - `#[inline]`: `inline` is a single segment path
pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
self.deconstruct()
}
/// Asserts that this MetaItem starts with a word, or single segment path.
/// Doesn't return the args parser.
///
/// For examples. see [`Self::word`]
pub fn word_without_args(&self) -> Option<Ident> {
Some(self.word()?.0)
}
/// Asserts that this MetaItem starts with a word, or single segment path.
///
/// Some examples:
/// - `#[inline]`: `inline` is a word
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
/// and not a word and should instead be parsed using [`path`](Self::path)
pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
let (path, args) = self.deconstruct();
Some((path.word()?, args))
}
/// Asserts that this MetaItem starts with some specific word.
///
/// See [`word`](Self::word) for examples of what a word is.
pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
self.path_without_args().word_is(sym).then(|| self.args())
}
/// Asserts that this MetaItem starts with some specific path.
///
/// See [`word`](Self::path) for examples of what a word is.
pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
self.path_without_args().segments_is(segments).then(|| self.args())
self.path().word_is(sym).then(|| self.args())
}
}
@@ -548,7 +516,7 @@ impl<'a> MetaItemListParser<'a> {
}
/// Lets you pick and choose as what you want to parse each element in the list
pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
self.sub_parsers.iter()
}
@@ -560,20 +528,6 @@ impl<'a> MetaItemListParser<'a> {
self.len() == 0
}
/// Asserts that every item in the list is another list starting with a word.
///
/// See [`MetaItemParser::word`] for examples of words.
pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
self.mixed().map(|i| i.meta_item()?.word()).collect()
}
/// Asserts that every item in the list is another list starting with a full path.
///
/// See [`MetaItemParser::path`] for examples of paths.
pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
}
/// Returns Some if the list contains only a single element.
///
/// Inside the Some is the parser to parse this single element.