Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3416,7 +3416,7 @@ pub struct Attribute {
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum AttrKind {
/// A normal attribute.
Normal(Box<NormalAttr>),
Normal(Box<AttrItem>),

/// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
/// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
Expand All @@ -3425,32 +3425,26 @@ pub enum AttrKind {
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct NormalAttr {
pub item: AttrItem,
// Tokens for the full attribute, e.g. `#[foo]`, `#![bar]`.
pub struct AttrItem {
pub unsafety: Safety,
pub path: Path,
pub args: AttrItemKind,
// Tokens for the full attribute, e.g. `#[foo]`, `#![bar]`. (Compare this with
// `ParseNtResult::Meta`; `expand_cfg_attr_item` is where the two cases interact.)
pub tokens: Option<LazyAttrTokenStream>,

@petrochenkov petrochenkov Jul 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think much changed here since #158942, these tokens still do not belong to the AttrItem.
Putting them here removes one type, but instead introduces confusion, IMO.

Semantically the tokens belong to the Attribute level, they were moved to the AttrKind::Normal level only as an optimization for doc comments. Moving them into AttrItem goes even further from the right mental model.

  • Normal(Box<AttrItem>, Option<LazyAttrTokenStream>) would be fine semantically, but too large (?).
  • Normal(Box<(AttrItem, Option<LazyAttrTokenStream>)>) would be fine semantically too, and avoids the size issue, but a NormalAttr struct with named fields is probably still better than a tuple.

View changes since the review

}

impl NormalAttr {
impl AttrItem {
pub fn from_ident(ident: Ident) -> Self {
Self {
item: AttrItem {
unsafety: Safety::Default,
path: Path::from_ident(ident),
args: AttrItemKind::Unparsed(AttrArgs::Empty),
},
unsafety: Safety::Default,
path: Path::from_ident(ident),
args: AttrItemKind::Unparsed(AttrArgs::Empty),
tokens: None,
}
}
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct AttrItem {
pub unsafety: Safety,
pub path: Path,
pub args: AttrItemKind,
}

/// 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)]
Expand Down Expand Up @@ -4382,6 +4376,7 @@ mod size_asserts {
// tidy-alphabetical-start
static_assert_size!(AssocItem, 72);
static_assert_size!(AssocItemKind, 16);
static_assert_size!(AttrItem, 72);
static_assert_size!(AttrKind, 16);
static_assert_size!(Attribute, 32);
static_assert_size!(Block, 24);
Expand All @@ -4407,7 +4402,6 @@ mod size_asserts {
static_assert_size!(MetaItem, 80);
static_assert_size!(MetaItemKind, 40);
static_assert_size!(MetaItemLit, 40);
static_assert_size!(NormalAttr, 72);
static_assert_size!(Param, 40);
static_assert_size!(Pat, 64);
static_assert_size!(PatKind, 48);
Expand Down
79 changes: 28 additions & 51 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ 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,
PathSegment, Safety,
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, Path, PathSegment,
Safety,
};
use crate::token::{
self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,
};
use crate::tokenstream::{
DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
};
use crate::tokenstream::{DelimSpan, Spacing, TokenStream, TokenStreamIter, TokenTree};
use crate::util::comments;
use crate::util::literal::escape_string_symbol;

Expand Down Expand Up @@ -61,23 +59,16 @@ impl AttrIdGenerator {
impl Attribute {
pub fn get_normal_item(&self) -> &AttrItem {
match &self.kind {
AttrKind::Normal(normal) => &normal.item,
AttrKind::Normal(normal) => normal,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}

/// 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.
/// 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,
AttrKind::Normal(normal) => normal.args = new_args,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}
Expand All @@ -90,7 +81,7 @@ impl AttributeExt for Attribute {

fn value_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? {
AttrKind::Normal(normal) => match &normal.args.unparsed_ref()? {
AttrArgs::Eq { expr, .. } => Some(expr.span),
_ => None,
},
Expand All @@ -112,7 +103,7 @@ impl AttributeExt for Attribute {
fn name(&self) -> Option<Symbol> {
match &self.kind {
AttrKind::Normal(normal) => {
if let [ident] = &*normal.item.path.segments {
if let [ident] = &*normal.path.segments {
Some(ident.ident.name)
} else {
None
Expand All @@ -124,26 +115,23 @@ impl AttributeExt for Attribute {

fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {
match &self.kind {
AttrKind::Normal(p) => {
Some(p.item.path.segments.iter().map(|i| i.ident.name).collect())
}
AttrKind::Normal(p) => Some(p.path.segments.iter().map(|i| i.ident.name).collect()),
AttrKind::DocComment(_, _) => None,
}
}

fn path_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(attr) => Some(attr.item.path.span),
AttrKind::Normal(attr) => Some(attr.path.span),
AttrKind::DocComment(_, _) => None,
}
}

fn path_matches(&self, name: &[Symbol]) -> bool {
match &self.kind {
AttrKind::Normal(normal) => {
normal.item.path.segments.len() == name.len()
normal.path.segments.len() == name.len()
&& normal
.item
.path
.segments
.iter()
Expand All @@ -160,7 +148,7 @@ impl AttributeExt for Attribute {

fn is_word(&self) -> bool {
if let AttrKind::Normal(normal) = &self.kind {
matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty))
matches!(normal.args, AttrItemKind::Unparsed(AttrArgs::Empty))
} else {
false
}
Expand All @@ -175,7 +163,7 @@ impl AttributeExt for Attribute {
/// ```
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.meta_item_list(),
AttrKind::Normal(normal) => normal.meta_item_list(),
AttrKind::DocComment(..) => None,
}
}
Expand All @@ -197,7 +185,7 @@ impl AttributeExt for Attribute {
/// ```
fn value_str(&self) -> Option<Symbol> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.value_str(),
AttrKind::Normal(normal) => normal.value_str(),
AttrKind::DocComment(..) => None,
}
}
Expand All @@ -210,9 +198,9 @@ impl AttributeExt for Attribute {
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
match &self.kind {
AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))),
AttrKind::Normal(normal) if normal.item.path == sym::doc => {
if let Some(value) = normal.item.value_str()
&& let Some(value_span) = normal.item.value_span()
AttrKind::Normal(normal) if normal.path == sym::doc => {
if let Some(value) = normal.value_str()
&& let Some(value_span) = normal.value_span()
{
Some((value, DocFragmentKind::Raw(value_span)))
} else {
Expand All @@ -230,17 +218,15 @@ impl AttributeExt for Attribute {
fn doc_str(&self) -> Option<Symbol> {
match &self.kind {
AttrKind::DocComment(.., data) => Some(*data),
AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
AttrKind::Normal(normal) if normal.path == sym::doc => normal.value_str(),
_ => None,
}
}

fn doc_resolution_scope(&self) -> Option<AttrStyle> {
match &self.kind {
AttrKind::DocComment(..) => Some(self.style),
AttrKind::Normal(normal)
if normal.item.path == sym::doc && normal.item.value_str().is_some() =>
{
AttrKind::Normal(normal) if normal.path == sym::doc && normal.value_str().is_some() => {
Some(self.style)
}
_ => None,
Expand Down Expand Up @@ -287,14 +273,14 @@ impl Attribute {
/// Extracts the MetaItem from inside this Attribute.
pub fn meta(&self) -> Option<MetaItem> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.meta(self.span),
AttrKind::Normal(normal) => normal.meta(self.span),
AttrKind::DocComment(..) => None,
}
}

pub fn meta_kind(&self) -> Option<MetaItemKind> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.meta_kind(),
AttrKind::Normal(normal) => normal.meta_kind(),
AttrKind::DocComment(..) => None,
}
}
Expand All @@ -316,16 +302,14 @@ impl Attribute {

pub fn deprecation_note(&self) -> Option<Ident> {
match &self.kind {
AttrKind::Normal(normal) if normal.item.path == sym::deprecated => {
let meta = &normal.item;

AttrKind::Normal(normal) if normal.path == sym::deprecated => {
// #[deprecated = "..."]
if let Some(s) = meta.value_str() {
return Some(Ident { name: s, span: meta.span() });
if let Some(s) = normal.value_str() {
return Some(Ident { name: s, span: normal.span() });
}

// #[deprecated(note = "...")]
if let Some(list) = meta.meta_item_list() {
if let Some(list) = normal.meta_item_list() {
for nested in list {
if let Some(mi) = nested.meta_item()
&& mi.path == sym::note
Expand Down Expand Up @@ -747,8 +731,7 @@ fn mk_attr(
) -> Attribute {
mk_attr_from_item(
g,
AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args) },
None,
AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },
style,
span,
)
Expand All @@ -757,16 +740,10 @@ fn mk_attr(
pub fn mk_attr_from_item(
g: &AttrIdGenerator,
item: AttrItem,
tokens: Option<LazyAttrTokenStream>,
style: AttrStyle,
span: Span,
) -> Attribute {
Attribute {
kind: AttrKind::Normal(Box::new(NormalAttr { item, tokens })),
id: g.mk_attr_id(),
style,
span,
}
Attribute { kind: AttrKind::Normal(Box::new(item)), id: g.mk_attr_id(), style, span }
}

pub fn mk_attr_word(
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,6 @@ macro_rules! common_visitor_and_walkers {
ModKind,
ModSpans,
MutTy,
NormalAttr,
AttrItemKind,
Parens,
ParenthesizedArgs,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_attrs(
inner_hir_id,
&[Attribute {
kind: AttrKind::Normal(Box::new(NormalAttr::from_ident(Ident::new(
kind: AttrKind::Normal(Box::new(AttrItem::from_ident(Ident::new(
sym::track_caller,
span,
)))),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
ast::AttrStyle::Inner => self.word("#!["),
ast::AttrStyle::Outer => self.word("#["),
}
self.print_attr_item(&normal.item, attr.span);
self.print_attr_item(normal, attr.span);
self.word("]");
}
ast::AttrKind::DocComment(comment_kind, data) => {
Expand Down
Loading
Loading