2025-02-09 22:49:31 +01:00
|
|
|
//! This is in essence an (improved) duplicate of `rustc_ast/attr/mod.rs`.
|
|
|
|
|
//! That module is intended to be deleted in its entirety.
|
|
|
|
|
//!
|
|
|
|
|
//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
use std::borrow::Cow;
|
2025-02-09 22:49:31 +01:00
|
|
|
use std::fmt::{Debug, Display};
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
use rustc_ast::token::{self, Delimiter, MetaVarKind};
|
|
|
|
|
use rustc_ast::tokenstream::TokenStream;
|
2025-02-09 22:49:31 +01:00
|
|
|
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
|
|
|
|
|
use rustc_ast_pretty::pprust;
|
2025-08-28 20:03:58 +02:00
|
|
|
use rustc_errors::{Diag, PResult};
|
2025-02-09 22:49:31 +01:00
|
|
|
use rustc_hir::{self as hir, AttrPath};
|
2025-07-31 15:37:43 +02:00
|
|
|
use rustc_parse::exp;
|
|
|
|
|
use rustc_parse::parser::{Parser, PathStyle, token_descr};
|
2025-08-28 20:03:58 +02:00
|
|
|
use rustc_session::errors::{create_lit_error, report_lit_error};
|
2025-07-31 15:37:43 +02:00
|
|
|
use rustc_session::parse::ParseSess;
|
|
|
|
|
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
|
|
|
|
|
use thin_vec::ThinVec;
|
|
|
|
|
|
|
|
|
|
use crate::ShouldEmit;
|
|
|
|
|
use crate::session_diagnostics::{
|
|
|
|
|
InvalidMetaItem, InvalidMetaItemQuoteIdentSugg, InvalidMetaItemRemoveNegSugg, MetaBadDelim,
|
|
|
|
|
MetaBadDelimSugg, SuffixedLiteralInAttribute,
|
|
|
|
|
};
|
2025-02-09 22:49:31 +01:00
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
2025-07-31 15:37:43 +02:00
|
|
|
pub struct PathParser<'a>(pub Cow<'a, Path>);
|
2025-02-09 22:49:31 +01:00
|
|
|
|
|
|
|
|
impl<'a> PathParser<'a> {
|
|
|
|
|
pub fn get_attribute_path(&self) -> hir::AttrPath {
|
|
|
|
|
AttrPath {
|
|
|
|
|
segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
|
|
|
|
|
span: self.span(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
|
2025-07-31 15:37:43 +02:00
|
|
|
self.0.segments.iter().map(|seg| &seg.ident)
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn span(&self) -> Span {
|
2025-07-31 15:37:43 +02:00
|
|
|
self.0.span
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
2025-07-31 15:37:43 +02:00
|
|
|
self.0.segments.len()
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn segments_is(&self, segments: &[Symbol]) -> bool {
|
2025-09-27 20:57:50 +03:00
|
|
|
self.segments().map(|segment| &segment.name).eq(segments)
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn word(&self) -> Option<Ident> {
|
|
|
|
|
(self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-21 07:52:30 +10:00
|
|
|
pub fn word_sym(&self) -> Option<Symbol> {
|
|
|
|
|
self.word().map(|ident| ident.name)
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Asserts that this MetaItem is some specific word.
|
|
|
|
|
///
|
|
|
|
|
/// See [`word`](Self::word) for examples of what a word is.
|
|
|
|
|
pub fn word_is(&self, sym: Symbol) -> bool {
|
|
|
|
|
self.word().map(|i| i.name == sym).unwrap_or(false)
|
|
|
|
|
}
|
2025-06-13 02:28:33 +02:00
|
|
|
|
|
|
|
|
/// Checks whether the first segments match the givens.
|
|
|
|
|
///
|
|
|
|
|
/// Unlike [`segments_is`](Self::segments_is),
|
|
|
|
|
/// `self` may contain more segments than the number matched against.
|
|
|
|
|
pub fn starts_with(&self, segments: &[Symbol]) -> bool {
|
|
|
|
|
segments.len() < self.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
|
|
|
|
|
}
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for PathParser<'_> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
2025-07-31 15:37:43 +02:00
|
|
|
write!(f, "{}", pprust::path_to_string(&self.0))
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub enum ArgParser<'a> {
|
|
|
|
|
NoArgs,
|
|
|
|
|
List(MetaItemListParser<'a>),
|
|
|
|
|
NameValue(NameValueParser),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> ArgParser<'a> {
|
|
|
|
|
pub fn span(&self) -> Option<Span> {
|
|
|
|
|
match self {
|
|
|
|
|
Self::NoArgs => None,
|
|
|
|
|
Self::List(l) => Some(l.span),
|
|
|
|
|
Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
pub fn from_attr_args<'sess>(
|
|
|
|
|
value: &'a AttrArgs,
|
|
|
|
|
parts: &[Symbol],
|
|
|
|
|
psess: &'sess ParseSess,
|
|
|
|
|
should_emit: ShouldEmit,
|
|
|
|
|
) -> Option<Self> {
|
|
|
|
|
Some(match value {
|
2025-02-09 22:49:31 +01:00
|
|
|
AttrArgs::Empty => Self::NoArgs,
|
|
|
|
|
AttrArgs::Delimited(args) => {
|
2025-07-31 15:37:43 +02:00
|
|
|
// The arguments of rustc_dummy are not validated if the arguments are delimited
|
|
|
|
|
if parts == &[sym::rustc_dummy] {
|
|
|
|
|
return Some(ArgParser::List(MetaItemListParser {
|
|
|
|
|
sub_parsers: ThinVec::new(),
|
|
|
|
|
span: args.dspan.entire(),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if args.delim != Delimiter::Parenthesis {
|
|
|
|
|
psess.dcx().emit_err(MetaBadDelim {
|
|
|
|
|
span: args.dspan.entire(),
|
|
|
|
|
sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
|
|
|
|
|
});
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Self::List(MetaItemListParser::new(args, psess, should_emit)?)
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
|
|
|
|
|
eq_span: *eq_span,
|
2025-07-31 15:37:43 +02:00
|
|
|
value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
|
2025-02-09 22:49:31 +01:00
|
|
|
value_span: expr.span,
|
|
|
|
|
}),
|
2025-07-31 15:37:43 +02:00
|
|
|
})
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Asserts that this MetaItem is a list
|
|
|
|
|
///
|
|
|
|
|
/// Some examples:
|
|
|
|
|
///
|
|
|
|
|
/// - `#[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<'a>> {
|
|
|
|
|
match self {
|
|
|
|
|
Self::List(l) => Some(l),
|
|
|
|
|
Self::NameValue(_) | Self::NoArgs => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Asserts that this MetaItem is a name-value pair.
|
|
|
|
|
///
|
|
|
|
|
/// Some examples:
|
|
|
|
|
///
|
|
|
|
|
/// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair,
|
|
|
|
|
/// where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path
|
|
|
|
|
/// to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is
|
|
|
|
|
/// there
|
|
|
|
|
/// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair
|
|
|
|
|
pub fn name_value(&self) -> Option<&NameValueParser> {
|
|
|
|
|
match self {
|
|
|
|
|
Self::NameValue(n) => Some(n),
|
|
|
|
|
Self::List(_) | Self::NoArgs => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-18 22:27:35 +03:00
|
|
|
/// Assert that there were no args.
|
|
|
|
|
/// If there were, get a span to the arguments
|
|
|
|
|
/// (to pass to [`AcceptContext::expected_no_args`](crate::context::AcceptContext::expected_no_args)).
|
|
|
|
|
pub fn no_args(&self) -> Result<(), Span> {
|
|
|
|
|
match self {
|
|
|
|
|
Self::NoArgs => Ok(()),
|
|
|
|
|
Self::List(args) => Err(args.span),
|
|
|
|
|
Self::NameValue(args) => Err(args.eq_span.to(args.value_span)),
|
|
|
|
|
}
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Inside lists, values could be either literals, or more deeply nested meta items.
|
|
|
|
|
/// This enum represents that.
|
|
|
|
|
///
|
|
|
|
|
/// Choose which one you want using the provided methods.
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum MetaItemOrLitParser<'a> {
|
|
|
|
|
MetaItemParser(MetaItemParser<'a>),
|
|
|
|
|
Lit(MetaItemLit),
|
|
|
|
|
Err(Span, ErrorGuaranteed),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> MetaItemOrLitParser<'a> {
|
|
|
|
|
pub fn span(&self) -> Span {
|
|
|
|
|
match self {
|
|
|
|
|
MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
|
|
|
|
|
generic_meta_item_parser.span()
|
|
|
|
|
}
|
|
|
|
|
MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
|
|
|
|
|
MetaItemOrLitParser::Err(span, _) => *span,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn lit(&self) -> Option<&MetaItemLit> {
|
|
|
|
|
match self {
|
|
|
|
|
MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
|
|
|
|
|
match self {
|
|
|
|
|
MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Utility that deconstructs a MetaItem into usable parts.
|
|
|
|
|
///
|
|
|
|
|
/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
|
|
|
|
|
/// them in custom, more restricted ways. This can be done using this struct.
|
|
|
|
|
///
|
|
|
|
|
/// 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 = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
|
|
|
|
|
/// `= value` part
|
|
|
|
|
///
|
|
|
|
|
/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct MetaItemParser<'a> {
|
|
|
|
|
path: PathParser<'a>,
|
|
|
|
|
args: ArgParser<'a>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Debug for MetaItemParser<'a> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
f.debug_struct("MetaItemParser")
|
|
|
|
|
.field("path", &self.path)
|
|
|
|
|
.field("args", &self.args)
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> MetaItemParser<'a> {
|
|
|
|
|
/// Create a new parser from a [`NormalAttr`], which is stored inside of any
|
|
|
|
|
/// [`ast::Attribute`](rustc_ast::Attribute)
|
2025-07-31 15:37:43 +02:00
|
|
|
pub fn from_attr<'sess>(
|
|
|
|
|
attr: &'a NormalAttr,
|
|
|
|
|
parts: &[Symbol],
|
|
|
|
|
psess: &'sess ParseSess,
|
|
|
|
|
should_emit: ShouldEmit,
|
|
|
|
|
) -> Option<Self> {
|
|
|
|
|
Some(Self {
|
|
|
|
|
path: PathParser(Cow::Borrowed(&attr.item.path)),
|
|
|
|
|
args: ArgParser::from_attr_args(&attr.item.args, parts, psess, should_emit)?,
|
|
|
|
|
})
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> MetaItemParser<'a> {
|
|
|
|
|
pub fn span(&self) -> Span {
|
|
|
|
|
if let Some(other) = self.args.span() {
|
|
|
|
|
self.path.span().with_hi(other.hi())
|
|
|
|
|
} else {
|
|
|
|
|
self.path.span()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-20 11:57:21 +10:00
|
|
|
/// Gets just the path, without the args. Some examples:
|
2025-02-09 22:49:31 +01:00
|
|
|
///
|
|
|
|
|
/// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
|
|
|
|
|
/// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
|
|
|
|
|
/// - `#[inline]`: `inline` is a single segment path
|
2025-05-20 12:41:56 +10:00
|
|
|
pub fn path(&self) -> &PathParser<'a> {
|
|
|
|
|
&self.path
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets just the args parser, without caring about the path.
|
|
|
|
|
pub fn args(&self) -> &ArgParser<'a> {
|
|
|
|
|
&self.args
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
|
2025-05-20 12:41:56 +10:00
|
|
|
self.path().word_is(sym).then(|| self.args())
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct NameValueParser {
|
|
|
|
|
pub eq_span: Span,
|
|
|
|
|
value: MetaItemLit,
|
|
|
|
|
pub value_span: Span,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Debug for NameValueParser {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
f.debug_struct("NameValueParser")
|
|
|
|
|
.field("eq_span", &self.eq_span)
|
|
|
|
|
.field("value", &self.value)
|
|
|
|
|
.field("value_span", &self.value_span)
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl NameValueParser {
|
|
|
|
|
pub fn value_as_lit(&self) -> &MetaItemLit {
|
|
|
|
|
&self.value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn value_as_str(&self) -> Option<Symbol> {
|
|
|
|
|
self.value_as_lit().kind.str()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
fn expr_to_lit(
|
|
|
|
|
psess: &ParseSess,
|
|
|
|
|
expr: &Expr,
|
|
|
|
|
span: Span,
|
|
|
|
|
should_emit: ShouldEmit,
|
|
|
|
|
) -> Option<MetaItemLit> {
|
|
|
|
|
if let ExprKind::Lit(token_lit) = expr.kind {
|
|
|
|
|
let res = MetaItemLit::from_token_lit(token_lit, expr.span);
|
|
|
|
|
match res {
|
|
|
|
|
Ok(lit) => {
|
|
|
|
|
if token_lit.suffix.is_some() {
|
2025-08-23 19:42:14 +02:00
|
|
|
should_emit.emit_err(
|
2025-08-11 11:46:30 +02:00
|
|
|
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
|
|
|
|
);
|
2025-07-31 15:37:43 +02:00
|
|
|
None
|
|
|
|
|
} else {
|
2025-08-11 11:46:30 +02:00
|
|
|
if !lit.kind.is_unsuffixed() {
|
2025-07-31 15:37:43 +02:00
|
|
|
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
|
2025-08-23 19:42:14 +02:00
|
|
|
should_emit.emit_err(
|
2025-08-11 11:46:30 +02:00
|
|
|
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
|
|
|
|
);
|
2025-07-31 15:37:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(lit)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(err) => {
|
|
|
|
|
let guar = report_lit_error(psess, err, token_lit, expr.span);
|
|
|
|
|
let lit = MetaItemLit {
|
|
|
|
|
symbol: token_lit.symbol,
|
|
|
|
|
suffix: token_lit.suffix,
|
|
|
|
|
kind: LitKind::Err(guar),
|
|
|
|
|
span: expr.span,
|
|
|
|
|
};
|
|
|
|
|
Some(lit)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-09 22:49:31 +01:00
|
|
|
} else {
|
2025-08-23 19:42:14 +02:00
|
|
|
if matches!(should_emit, ShouldEmit::Nothing) {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
// Example cases:
|
|
|
|
|
// - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
|
|
|
|
|
// - `#[foo = include_str!("nonexistent-file.rs")]`:
|
|
|
|
|
// results in `ast::ExprKind::Err`. In that case we delay
|
|
|
|
|
// the error because an earlier error will have already
|
|
|
|
|
// been reported.
|
|
|
|
|
let msg = "attribute value must be a literal";
|
2025-08-23 19:42:14 +02:00
|
|
|
let err = psess.dcx().struct_span_err(span, msg);
|
|
|
|
|
should_emit.emit_err(err);
|
2025-07-31 15:37:43 +02:00
|
|
|
None
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 13:59:08 +01:00
|
|
|
struct MetaItemListParserContext<'a, 'sess> {
|
2025-07-31 15:37:43 +02:00
|
|
|
parser: &'a mut Parser<'sess>,
|
|
|
|
|
should_emit: ShouldEmit,
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-12 13:59:08 +01:00
|
|
|
impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
2025-07-31 15:37:43 +02:00
|
|
|
fn parse_unsuffixed_meta_item_lit(&mut self) -> PResult<'sess, MetaItemLit> {
|
2025-08-28 20:03:58 +02:00
|
|
|
let Some(token_lit) = self.parser.eat_token_lit() else { return Err(self.expected_lit()) };
|
|
|
|
|
self.unsuffixed_meta_item_from_lit(token_lit)
|
|
|
|
|
}
|
2025-02-09 22:49:31 +01:00
|
|
|
|
2025-08-28 20:03:58 +02:00
|
|
|
fn unsuffixed_meta_item_from_lit(
|
|
|
|
|
&mut self,
|
|
|
|
|
token_lit: token::Lit,
|
|
|
|
|
) -> PResult<'sess, MetaItemLit> {
|
2025-07-31 15:37:43 +02:00
|
|
|
let lit = match MetaItemLit::from_token_lit(token_lit, self.parser.prev_token.span) {
|
|
|
|
|
Ok(lit) => lit,
|
|
|
|
|
Err(err) => {
|
2025-08-28 20:03:58 +02:00
|
|
|
return Err(create_lit_error(
|
|
|
|
|
&self.parser.psess,
|
|
|
|
|
err,
|
|
|
|
|
token_lit,
|
|
|
|
|
self.parser.prev_token_uninterpolated_span(),
|
|
|
|
|
));
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
2025-07-31 15:37:43 +02:00
|
|
|
};
|
|
|
|
|
|
2025-08-11 11:46:30 +02:00
|
|
|
if !lit.kind.is_unsuffixed() {
|
2025-07-31 15:37:43 +02:00
|
|
|
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
|
2025-08-23 19:42:14 +02:00
|
|
|
self.should_emit.emit_err(
|
2025-08-11 11:46:30 +02:00
|
|
|
self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
|
|
|
|
|
);
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
Ok(lit)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser<'static>> {
|
|
|
|
|
if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() {
|
|
|
|
|
return if has_meta_form {
|
|
|
|
|
let attr_item = self
|
|
|
|
|
.parser
|
|
|
|
|
.eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| {
|
|
|
|
|
MetaItemListParserContext { parser: this, should_emit: self.should_emit }
|
|
|
|
|
.parse_attr_item()
|
|
|
|
|
})
|
|
|
|
|
.unwrap();
|
|
|
|
|
Ok(attr_item)
|
|
|
|
|
} else {
|
|
|
|
|
self.parser.unexpected_any()
|
|
|
|
|
};
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
2025-07-31 15:37:43 +02:00
|
|
|
|
|
|
|
|
let path = self.parser.parse_path(PathStyle::Mod)?;
|
|
|
|
|
|
|
|
|
|
// Check style of arguments that this meta item has
|
|
|
|
|
let args = if self.parser.check(exp!(OpenParen)) {
|
|
|
|
|
let start = self.parser.token.span;
|
|
|
|
|
let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| {
|
|
|
|
|
MetaItemListParserContext { parser, should_emit: self.should_emit }
|
|
|
|
|
.parse_meta_item_inner()
|
|
|
|
|
})?;
|
|
|
|
|
let end = self.parser.prev_token.span;
|
|
|
|
|
ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) })
|
|
|
|
|
} else if self.parser.eat(exp!(Eq)) {
|
|
|
|
|
let eq_span = self.parser.prev_token.span;
|
|
|
|
|
let value = self.parse_unsuffixed_meta_item_lit()?;
|
|
|
|
|
|
|
|
|
|
ArgParser::NameValue(NameValueParser { eq_span, value, value_span: value.span })
|
|
|
|
|
} else {
|
|
|
|
|
ArgParser::NoArgs
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(MetaItemParser { path: PathParser(Cow::Owned(path)), args })
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser<'static>> {
|
2025-08-28 20:03:58 +02:00
|
|
|
if let Some(token_lit) = self.parser.eat_token_lit() {
|
|
|
|
|
// If a literal token is parsed, we commit to parsing a MetaItemLit for better errors
|
|
|
|
|
Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?))
|
|
|
|
|
} else {
|
|
|
|
|
let prev_pros = self.parser.approx_token_stream_pos();
|
|
|
|
|
match self.parse_attr_item() {
|
|
|
|
|
Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)),
|
|
|
|
|
Err(err) => {
|
|
|
|
|
// If `parse_attr_item` made any progress, it likely has a more precise error we should prefer
|
|
|
|
|
// If it didn't make progress we use the `expected_lit` from below
|
|
|
|
|
if self.parser.approx_token_stream_pos() != prev_pros {
|
|
|
|
|
Err(err)
|
|
|
|
|
} else {
|
|
|
|
|
err.cancel();
|
|
|
|
|
Err(self.expected_lit())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-31 15:37:43 +02:00
|
|
|
}
|
2025-08-28 20:03:58 +02:00
|
|
|
}
|
2025-07-31 15:37:43 +02:00
|
|
|
|
2025-08-28 20:03:58 +02:00
|
|
|
fn expected_lit(&mut self) -> Diag<'sess> {
|
2025-07-31 15:37:43 +02:00
|
|
|
let mut err = InvalidMetaItem {
|
|
|
|
|
span: self.parser.token.span,
|
|
|
|
|
descr: token_descr(&self.parser.token),
|
|
|
|
|
quote_ident_sugg: None,
|
|
|
|
|
remove_neg_sugg: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
|
|
|
|
|
// don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
|
|
|
|
|
// when macro metavariables are involved.
|
|
|
|
|
if self.parser.prev_token == token::Eq
|
|
|
|
|
&& let token::Ident(..) = self.parser.token.kind
|
2025-02-26 14:29:19 +01:00
|
|
|
{
|
2025-07-31 15:37:43 +02:00
|
|
|
let before = self.parser.token.span.shrink_to_lo();
|
|
|
|
|
while let token::Ident(..) = self.parser.token.kind {
|
|
|
|
|
self.parser.bump();
|
2025-02-26 14:29:19 +01:00
|
|
|
}
|
2025-07-31 15:37:43 +02:00
|
|
|
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
|
|
|
|
|
before,
|
|
|
|
|
after: self.parser.prev_token.span.shrink_to_hi(),
|
|
|
|
|
});
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
if self.parser.token == token::Minus
|
|
|
|
|
&& self
|
|
|
|
|
.parser
|
|
|
|
|
.look_ahead(1, |t| matches!(t.kind, rustc_ast::token::TokenKind::Literal { .. }))
|
|
|
|
|
{
|
|
|
|
|
err.remove_neg_sugg =
|
|
|
|
|
Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
|
|
|
|
|
self.parser.bump();
|
|
|
|
|
self.parser.bump();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 20:03:58 +02:00
|
|
|
self.parser.dcx().create_err(err)
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
fn parse(
|
|
|
|
|
tokens: TokenStream,
|
|
|
|
|
psess: &'sess ParseSess,
|
|
|
|
|
span: Span,
|
|
|
|
|
should_emit: ShouldEmit,
|
|
|
|
|
) -> PResult<'sess, MetaItemListParser<'static>> {
|
|
|
|
|
let mut parser = Parser::new(psess, tokens, None);
|
|
|
|
|
let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
|
2025-02-09 22:49:31 +01:00
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
// Presumably, the majority of the time there will only be one attr.
|
|
|
|
|
let mut sub_parsers = ThinVec::with_capacity(1);
|
|
|
|
|
while this.parser.token != token::Eof {
|
|
|
|
|
sub_parsers.push(this.parse_meta_item_inner()?);
|
2025-02-09 22:49:31 +01:00
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
if !this.parser.eat(exp!(Comma)) {
|
|
|
|
|
break;
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-31 15:37:43 +02:00
|
|
|
if parser.token != token::Eof {
|
|
|
|
|
parser.unexpected()?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(MetaItemListParser { sub_parsers, span })
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct MetaItemListParser<'a> {
|
2025-07-31 15:37:43 +02:00
|
|
|
sub_parsers: ThinVec<MetaItemOrLitParser<'a>>,
|
2025-02-09 22:49:31 +01:00
|
|
|
pub span: Span,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> MetaItemListParser<'a> {
|
2025-07-31 15:37:43 +02:00
|
|
|
fn new<'sess>(
|
|
|
|
|
delim: &'a DelimArgs,
|
|
|
|
|
psess: &'sess ParseSess,
|
|
|
|
|
should_emit: ShouldEmit,
|
|
|
|
|
) -> Option<Self> {
|
|
|
|
|
match MetaItemListParserContext::parse(
|
|
|
|
|
delim.tokens.clone(),
|
|
|
|
|
psess,
|
|
|
|
|
delim.dspan.entire(),
|
|
|
|
|
should_emit,
|
|
|
|
|
) {
|
|
|
|
|
Ok(s) => Some(s),
|
|
|
|
|
Err(e) => {
|
2025-08-23 19:42:14 +02:00
|
|
|
should_emit.emit_err(e);
|
2025-07-31 15:37:43 +02:00
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-09 22:49:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Lets you pick and choose as what you want to parse each element in the list
|
2025-05-20 12:01:03 +10:00
|
|
|
pub fn mixed(&self) -> impl Iterator<Item = &MetaItemOrLitParser<'a>> {
|
2025-02-09 22:49:31 +01:00
|
|
|
self.sub_parsers.iter()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
|
self.sub_parsers.len()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
|
self.len() == 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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<'a>> {
|
|
|
|
|
let mut iter = self.mixed();
|
|
|
|
|
iter.next().filter(|_| iter.next().is_none())
|
|
|
|
|
}
|
|
|
|
|
}
|