Create a rustc_ast representation for parsed attributes

This commit is contained in:
Jonathan Brouwer 2025-12-26 20:54:15 +01:00
parent da476f1942
commit 03fb7eeced
No known key found for this signature in database
GPG key ID: F13E55D38C971DEF
11 changed files with 115 additions and 48 deletions

View file

@ -34,6 +34,7 @@ use rustc_span::source_map::{Spanned, respan};
use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use crate::attr::data_structures::CfgEntry;
pub use crate::format::*;
use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
@ -3390,7 +3391,7 @@ impl NormalAttr {
item: AttrItem {
unsafety: Safety::Default,
path: Path::from_ident(ident),
args: AttrArgs::Empty,
args: AttrItemKind::Unparsed(AttrArgs::Empty),
tokens: None,
},
tokens: None,
@ -3402,11 +3403,51 @@ impl NormalAttr {
pub struct AttrItem {
pub unsafety: Safety,
pub path: Path,
pub args: AttrArgs,
pub args: AttrItemKind,
// Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`.
pub tokens: Option<LazyAttrTokenStream>,
}
/// Some attributes are stored in a parsed form, for performance reasons.
/// Their arguments don't have to be reparsed everytime they're used
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum AttrItemKind {
Parsed(EarlyParsedAttribute),
Unparsed(AttrArgs),
}
impl AttrItemKind {
pub fn unparsed(self) -> Option<AttrArgs> {
match self {
AttrItemKind::Unparsed(args) => Some(args),
AttrItemKind::Parsed(_) => None,
}
}
pub fn unparsed_ref(&self) -> Option<&AttrArgs> {
match self {
AttrItemKind::Unparsed(args) => Some(args),
AttrItemKind::Parsed(_) => None,
}
}
pub fn span(&self) -> Option<Span> {
match self {
AttrItemKind::Unparsed(args) => args.span(),
AttrItemKind::Parsed(_) => None,
}
}
}
/// Some attributes are stored in parsed form in the AST.
/// This is done for performance reasons, so the attributes don't need to be reparsed on every use.
///
/// Currently all early parsed attributes are excluded from pretty printing at rustc_ast_pretty::pprust::state::print_attribute_inline.
/// When adding new early parsed attributes, consider whether they should be pretty printed.
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum EarlyParsedAttribute {
}
impl AttrItem {
pub fn is_valid_for_outer_style(&self) -> bool {
self.path == sym::cfg_attr

View file

@ -1,5 +1,8 @@
//! Functions dealing with attributes and meta items.
pub mod data_structures;
pub mod version;
use std::fmt::Debug;
use std::sync::atomic::{AtomicU32, Ordering};
@ -8,6 +11,7 @@ use rustc_span::{Ident, Span, Symbol, sym};
use smallvec::{SmallVec, smallvec};
use thin_vec::{ThinVec, thin_vec};
use crate::AttrItemKind;
use crate::ast::{
AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
@ -62,6 +66,15 @@ impl Attribute {
}
}
/// Replaces the arguments of this attribute with new arguments `AttrItemKind`.
/// This is useful for making this attribute into a trace attribute, and should otherwise be avoided.
pub fn replace_args(&mut self, new_args: AttrItemKind) {
match &mut self.kind {
AttrKind::Normal(normal) => normal.item.args = new_args,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}
pub fn unwrap_normal_item(self) -> AttrItem {
match self.kind {
AttrKind::Normal(normal) => normal.item,
@ -77,7 +90,7 @@ impl AttributeExt for Attribute {
fn value_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(normal) => match &normal.item.args {
AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? {
AttrArgs::Eq { expr, .. } => Some(expr.span),
_ => None,
},
@ -147,7 +160,7 @@ impl AttributeExt for Attribute {
fn is_word(&self) -> bool {
if let AttrKind::Normal(normal) = &self.kind {
matches!(normal.item.args, AttrArgs::Empty)
matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty))
} else {
false
}
@ -303,7 +316,7 @@ impl AttrItem {
}
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
match &self.args {
match &self.args.unparsed_ref()? {
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
MetaItemKind::list_from_tokens(args.tokens.clone())
}
@ -324,7 +337,7 @@ impl AttrItem {
/// #[attr("value")]
/// ```
fn value_str(&self) -> Option<Symbol> {
match &self.args {
match &self.args.unparsed_ref()? {
AttrArgs::Eq { expr, .. } => match expr.kind {
ExprKind::Lit(token_lit) => {
LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
@ -348,7 +361,7 @@ impl AttrItem {
/// #[attr("value")]
/// ```
fn value_span(&self) -> Option<Span> {
match &self.args {
match &self.args.unparsed_ref()? {
AttrArgs::Eq { expr, .. } => Some(expr.span),
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
}
@ -364,7 +377,7 @@ impl AttrItem {
}
pub fn meta_kind(&self) -> Option<MetaItemKind> {
MetaItemKind::from_attr_args(&self.args)
MetaItemKind::from_attr_args(self.args.unparsed_ref()?)
}
}
@ -699,7 +712,13 @@ fn mk_attr(
args: AttrArgs,
span: Span,
) -> Attribute {
mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span)
mk_attr_from_item(
g,
AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },
None,
style,
span,
)
}
pub fn mk_attr_from_item(

View file

@ -366,6 +366,7 @@ macro_rules! common_visitor_and_walkers {
crate::token::LitKind,
crate::tokenstream::LazyAttrTokenStream,
crate::tokenstream::TokenStream,
EarlyParsedAttribute,
Movability,
Mutability,
Pinnedness,
@ -457,6 +458,7 @@ macro_rules! common_visitor_and_walkers {
ModSpans,
MutTy,
NormalAttr,
AttrItemKind,
Parens,
ParenthesizedArgs,
PatFieldsRest,

View file

@ -694,7 +694,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
}
ast::Safety::Default | ast::Safety::Safe(_) => {}
}
match &item.args {
match &item.args.unparsed_ref().expect("Parsed attributes are never printed") {
AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common(
Some(MacHeader::Path(&item.path)),
false,

View file

@ -294,11 +294,9 @@ pub fn parse_cfg_attr(
sess: &Session,
features: Option<&Features>,
) -> Option<(CfgEntry, Vec<(AttrItem, Span)>)> {
match cfg_attr.get_normal_item().args {
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
if !tokens.is_empty() =>
{
check_cfg_attr_bad_delim(&sess.psess, dspan, delim);
match cfg_attr.get_normal_item().args.unparsed_ref().unwrap() {
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => {
check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim);
match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| {
parse_cfg_attr_internal(p, sess, features, cfg_attr)
}) {
@ -322,7 +320,7 @@ pub fn parse_cfg_attr(
}
_ => {
let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) =
cfg_attr.get_normal_item().args
cfg_attr.get_normal_item().args.unparsed_ref()?
{
(dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument)
} else {

View file

@ -122,10 +122,10 @@ impl ArgParser {
}
if args.delim != Delimiter::Parenthesis {
psess.dcx().emit_err(MetaBadDelim {
should_emit.emit_err(psess.dcx().create_err(MetaBadDelim {
span: args.dspan.entire(),
sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close },
});
}));
return None;
}

View file

@ -48,7 +48,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
}
_ => {
let attr_item = attr.get_normal_item();
if let AttrArgs::Eq { .. } = attr_item.args {
if let AttrArgs::Eq { .. } = attr_item.args.unparsed_ref().unwrap() {
// All key-value attributes are restricted to meta-item syntax.
match parse_meta(psess, attr) {
Ok(_) => {}
@ -67,7 +67,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met
unsafety: item.unsafety,
span: attr.span,
path: item.path.clone(),
kind: match &item.args {
kind: match &item.args.unparsed_ref().unwrap() {
AttrArgs::Empty => MetaItemKind::Word,
AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => {
check_meta_bad_delim(psess, *dspan, *delim);

View file

@ -358,7 +358,7 @@ mod llvm_enzyme {
let inline_item = ast::AttrItem {
unsafety: ast::Safety::Default,
path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)),
args: ast::AttrArgs::Delimited(never_arg),
args: rustc_ast::ast::AttrItemKind::Unparsed(ast::AttrArgs::Delimited(never_arg)),
tokens: None,
};
let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None });
@ -421,11 +421,13 @@ mod llvm_enzyme {
}
};
// Now update for d_fn
rustc_ad_attr.item.args = rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs {
dspan: DelimSpan::dummy(),
delim: rustc_ast::token::Delimiter::Parenthesis,
tokens: ts,
});
rustc_ad_attr.item.args = rustc_ast::ast::AttrItemKind::Unparsed(
rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs {
dspan: DelimSpan::dummy(),
delim: rustc_ast::token::Delimiter::Parenthesis,
tokens: ts,
}),
);
let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
let d_attr = outer_normal_attr(&rustc_ad_attr, new_id, span);

View file

@ -807,24 +807,29 @@ impl<'a> TraitDef<'a> {
rustc_ast::AttrItem {
unsafety: Safety::Default,
path: rustc_const_unstable,
args: AttrArgs::Delimited(DelimArgs {
dspan: DelimSpan::from_single(self.span),
delim: rustc_ast::token::Delimiter::Parenthesis,
tokens: [
TokenKind::Ident(sym::feature, IdentIsRaw::No),
TokenKind::Eq,
TokenKind::lit(LitKind::Str, sym::derive_const, None),
TokenKind::Comma,
TokenKind::Ident(sym::issue, IdentIsRaw::No),
TokenKind::Eq,
TokenKind::lit(LitKind::Str, sym::derive_const_issue, None),
]
.into_iter()
.map(|kind| {
TokenTree::Token(Token { kind, span: self.span }, Spacing::Alone)
})
.collect(),
}),
args: rustc_ast::ast::AttrItemKind::Unparsed(AttrArgs::Delimited(
DelimArgs {
dspan: DelimSpan::from_single(self.span),
delim: rustc_ast::token::Delimiter::Parenthesis,
tokens: [
TokenKind::Ident(sym::feature, IdentIsRaw::No),
TokenKind::Eq,
TokenKind::lit(LitKind::Str, sym::derive_const, None),
TokenKind::Comma,
TokenKind::Ident(sym::issue, IdentIsRaw::No),
TokenKind::Eq,
TokenKind::lit(LitKind::Str, sym::derive_const_issue, None),
]
.into_iter()
.map(|kind| {
TokenTree::Token(
Token { kind, span: self.span },
Spacing::Alone,
)
})
.collect(),
},
)),
tokens: None,
},
self.span,

View file

@ -813,10 +813,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
};
let attr_item = attr.get_normal_item();
let safety = attr_item.unsafety;
if let AttrArgs::Eq { .. } = attr_item.args {
if let AttrArgs::Eq { .. } = attr_item.args.unparsed_ref().unwrap() {
self.cx.dcx().emit_err(UnsupportedKeyValue { span });
}
let inner_tokens = attr_item.args.inner_tokens();
let inner_tokens = attr_item.args.unparsed_ref().unwrap().inner_tokens();
match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) {
Ok(tok_result) => {
let fragment = self.parse_ast_fragment(

View file

@ -1,7 +1,7 @@
use rustc_ast as ast;
use rustc_ast::token::{self, MetaVarKind};
use rustc_ast::tokenstream::ParserRange;
use rustc_ast::{Attribute, attr};
use rustc_ast::{AttrItemKind, Attribute, attr};
use rustc_errors::codes::*;
use rustc_errors::{Diag, PResult};
use rustc_span::{BytePos, Span};
@ -313,7 +313,7 @@ impl<'a> Parser<'a> {
this.expect(exp!(CloseParen))?;
}
Ok((
ast::AttrItem { unsafety, path, args, tokens: None },
ast::AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },
Trailing::No,
UsePreAttrPos::No,
))