From e8c660bad1b4246dd531d674f6839106833170e2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 12 Jul 2026 19:53:19 +1000 Subject: [PATCH 1/7] Remove unused `unwrap_normal_item` methods. --- compiler/rustc_ast/src/attr/mod.rs | 11 ++--------- compiler/rustc_hir/src/hir.rs | 7 ------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 9774173f24780..b5d9591382758 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -67,21 +67,14 @@ 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. + /// 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::DocComment(..) => panic!("unexpected doc comment"), - } - } } impl AttributeExt for Attribute { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 92928fb03b644..2e272fa3af9d0 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1380,13 +1380,6 @@ impl Attribute { } } - pub fn unwrap_normal_item(self) -> AttrItem { - match self { - Attribute::Unparsed(normal) => *normal, - _ => panic!("unexpected parsed attribute"), - } - } - pub fn value_lit(&self) -> Option<&MetaItemLit> { match &self { Attribute::Unparsed(n) => match n.as_ref() { From 0e297899348c6b9d672af434d24aafe7c57e106d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Jul 2026 17:58:36 +1000 Subject: [PATCH 2/7] Improve an attribute check Once `sym::ignore` has been matched, the attribute can't be a `DocComment` and trying to match one is pointless. --- src/tools/clippy/clippy_lints/src/attrs/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 78701b3368226..9b481952c98ee 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -614,12 +614,8 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { } if attr.has_name(sym::ignore) - && match &attr.kind { - AttrKind::Normal(normal_attr) => { - !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })) - }, - AttrKind::DocComment(..) => true, - } + && let AttrKind::Normal(normal_attr) = &attr.kind + && !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })) { span_lint_and_help( cx, From d0042c3c7c55e382ad0ceeb768d95da0501ef3e9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Jul 2026 19:19:22 +1000 Subject: [PATCH 3/7] Expand some comments about `cfg_trace`/`cfg_attr_trace` To explain some things that weren't obvious to me. --- compiler/rustc_ast/src/ast.rs | 2 ++ .../rustc_attr_parsing/src/early_parsed.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 20 ++++++++++++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5cc9b9652ab16..7308b3726e091 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3490,6 +3490,8 @@ impl AttrItemKind { /// /// 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. +/// +/// See also `EARLY_PARSED_ATTRIBUTES`. #[derive(Clone, Encodable, Decodable, Debug, StableHash)] pub enum EarlyParsedAttribute { CfgTrace(CfgEntry), diff --git a/compiler/rustc_attr_parsing/src/early_parsed.rs b/compiler/rustc_attr_parsing/src/early_parsed.rs index 6a33cb38edf98..57607102ed8c4 100644 --- a/compiler/rustc_attr_parsing/src/early_parsed.rs +++ b/compiler/rustc_attr_parsing/src/early_parsed.rs @@ -5,6 +5,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_span::{Span, Symbol, sym}; use thin_vec::ThinVec; +/// See also `EarlyParsedAttribute`. pub(crate) const EARLY_PARSED_ATTRIBUTES: &[&[Symbol]] = &[&[sym::cfg_trace], &[sym::cfg_attr_trace]]; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ed09a2eb958e2..d1483e4e6016f 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -291,10 +291,24 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::rustc_macro_transparency, sym::rustc_autodiff, sym::rustc_offload_kernel, - // Traces that are left when `cfg` and `cfg_attr` attributes are expanded. + // These special attributes are added by the compiler when `cfg` and `cfg_attr` attributes are + // expanded so that subsequent code can tell that conditional compilation occurred. + // + // - A `#[cfg(pred)]` with a true predicate is replaced by a `#[cfg_trace]` that records the + // parsed predicate. A `#[cfg(pred)]` with a false predicate leaves no trace because there + // is no node left to annotate. + // + // - A `#[cfg_attr(pred, attrs)]` is replaced by a `#[cfg_attr_trace]` attribute whether the + // predicate evaluated true or not (or even failed to parse). The `pred` and `attrs` are not + // recorded because they are not needed. + // + // The attributes are used for some diagnostics, by rustdoc (for detecting feature usage), and + // by some clippy lints. + // // The attributes are not gated, to avoid stability errors, but they cannot be used in stable - // or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers, they - // can only be generated by the compiler. + // or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers. They + // are treated specially in various places because they must not be observable in any way that + // could change behaviour. For example, they are hidden from proc macros. sym::cfg_trace, sym::cfg_attr_trace, From b357c7c0ff254414b243730afa27ccc83b716e3f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Jul 2026 10:46:23 +1000 Subject: [PATCH 4/7] Remove `AttrItemKind` `AttrItem` currently contains an `AttrItemKind` which is either `Parsed` or `Unparsed`. In the `Parsed` case the surrounding `AttrItem` is basically fake, with a meaningless `unsafety` field, a synthetic (non-ident) symbol, and an empty `tokens`. This commit moves `Parsed` up one level to become a new variant of `AttrKind`. It no longer needs to pretend to be an `AttrKind::Normal`, and makes a number of things simpler: - No `unparsed_ref` calls needed. - Replaces the `attr_into_trace` and `Attribute::replace_args` mess with the much simpler `convert_normal_to_parsed`. The commit converts a numberof non-exhaustive `AttrKind` matches (those that don't involve matching a single attribute) to exhaustive, for future-proofing. It uses `unreachable!` on all the `AttrKind::Parsed` cases that aren't hit by the full test suite. --- compiler/rustc_ast/src/ast.rs | 45 ++------- compiler/rustc_ast/src/ast_traits.rs | 8 +- compiler/rustc_ast/src/attr/mod.rs | 99 +++++++++---------- compiler/rustc_ast/src/visit.rs | 1 - compiler/rustc_ast_pretty/src/pprust/state.rs | 3 +- .../rustc_attr_parsing/src/attributes/cfg.rs | 4 +- compiler/rustc_attr_parsing/src/interface.rs | 36 +++---- .../rustc_attr_parsing/src/validate_attr.rs | 4 +- compiler/rustc_builtin_macros/src/autodiff.rs | 14 ++- .../src/deriving/generic/mod.rs | 41 ++++---- compiler/rustc_builtin_macros/src/offload.rs | 16 ++- compiler/rustc_expand/src/config.rs | 20 +--- compiler/rustc_expand/src/expand.rs | 16 +-- compiler/rustc_feature/src/builtin_attrs.rs | 1 - compiler/rustc_lint/src/builtin.rs | 1 + compiler/rustc_parse/src/parser/attr.rs | 8 +- compiler/rustc_parse/src/parser/cfg_select.rs | 1 + compiler/rustc_passes/src/input_stats.rs | 2 +- compiler/rustc_resolve/src/def_collector.rs | 20 +++- .../src/attrs/mixed_attributes_style.rs | 10 +- .../clippy/clippy_lints/src/attrs/mod.rs | 4 +- .../src/attrs/should_panic_without_expect.rs | 6 +- .../clippy/clippy_lints/src/cfg_not_test.rs | 33 +++---- .../src/doc/include_in_doc_without_cfg.rs | 6 +- .../clippy_lints/src/empty_line_after.rs | 2 +- .../clippy_lints/src/large_include_file.rs | 6 +- .../clippy/clippy_utils/src/ast_utils/mod.rs | 15 +-- .../clippy_utils/src/check_proc_macro.rs | 1 + tests/ui/stats/input-stats.stderr | 7 +- 29 files changed, 185 insertions(+), 245 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7308b3726e091..8573550af767f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3418,9 +3418,14 @@ pub struct Attribute { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub enum AttrKind { - /// A normal attribute. + /// A normal (non-doc comment) attribute, with attributes in unparsed form. Normal(Box), + /// A normal (non-doc comment) attribute, with attributes in parsed form, so they don't have to + /// be reparsed every time they're used, for performance. Only used for a small number of + /// attribute kinds. + Parsed(Box), + /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`). /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal` /// variant (which is much less compact and thus more expensive). @@ -3430,7 +3435,8 @@ pub enum AttrKind { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct NormalAttr { pub item: AttrItem, - // Tokens for the full attribute, e.g. `#[foo]`, `#![bar]`. + // 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, } @@ -3440,7 +3446,7 @@ impl NormalAttr { item: AttrItem { unsafety: Safety::Default, path: Path::from_ident(ident), - args: AttrItemKind::Unparsed(AttrArgs::Empty), + args: AttrArgs::Empty, }, tokens: None, } @@ -3451,38 +3457,7 @@ impl NormalAttr { 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)] -pub enum AttrItemKind { - Parsed(EarlyParsedAttribute), - Unparsed(AttrArgs), -} - -impl AttrItemKind { - pub fn unparsed(self) -> Option { - 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 { - match self { - AttrItemKind::Unparsed(args) => args.span(), - AttrItemKind::Parsed(_) => None, - } - } + pub args: AttrArgs, } /// Some attributes are stored in parsed form in the AST. diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 14b317199996f..10e3512ca2b1e 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -170,17 +170,13 @@ impl HasTokens for Attribute { fn tokens(&self) -> Option<&LazyAttrTokenStream> { match &self.kind { AttrKind::Normal(normal) => normal.tokens.as_ref(), - kind @ AttrKind::DocComment(..) => { - panic!("Called tokens on doc comment attr {kind:?}") - } + AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), } } fn tokens_mut(&mut self) -> Option<&mut Option> { Some(match &mut self.kind { AttrKind::Normal(normal) => &mut normal.tokens, - kind @ AttrKind::DocComment(..) => { - panic!("Called tokens_mut on doc comment attr {kind:?}") - } + AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), }) } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index b5d9591382758..cca9656927640 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -11,11 +11,10 @@ use rustc_span::{Ident, Span, Symbol, kw, 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, - PathSegment, Safety, + EarlyParsedAttribute, Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, + MetaItemLit, NormalAttr, Path, PathSegment, Safety, }; use crate::token::{ self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token, @@ -63,16 +62,16 @@ impl Attribute { pub fn get_normal_item(&self) -> &AttrItem { match &self.kind { AttrKind::Normal(normal) => &normal.item, - AttrKind::DocComment(..) => panic!("unexpected doc comment"), + AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), } } - /// 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 convert_normal_to_parsed(&mut self, early_parsed_attribute: EarlyParsedAttribute) { + match self.kind { + AttrKind::Normal(..) => { + self.kind = AttrKind::Parsed(Box::new(early_parsed_attribute)); + } + AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), } } } @@ -84,11 +83,11 @@ impl AttributeExt for Attribute { fn value_span(&self) -> Option { match &self.kind { - AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? { + AttrKind::Normal(normal) => match &normal.item.args { AttrArgs::Eq { expr, .. } => Some(expr.span), _ => None, }, - AttrKind::DocComment(..) => None, + AttrKind::Parsed(..) | AttrKind::DocComment(..) => None, } } @@ -97,7 +96,7 @@ impl AttributeExt for Attribute { /// a doc comment) will return `false`. fn is_doc_comment(&self) -> Option { match self.kind { - AttrKind::Normal(..) => None, + AttrKind::Normal(..) | AttrKind::Parsed(..) => None, AttrKind::DocComment(..) => Some(self.span), } } @@ -112,14 +111,20 @@ impl AttributeExt for Attribute { None } } + AttrKind::Parsed(EarlyParsedAttribute::CfgTrace(_)) => Some(sym::cfg_trace), + AttrKind::Parsed(EarlyParsedAttribute::CfgAttrTrace) => Some(sym::cfg_attr_trace), AttrKind::DocComment(..) => None, } } fn symbol_path(&self) -> Option> { match &self.kind { - AttrKind::Normal(p) => { - Some(p.item.path.segments.iter().map(|i| i.ident.name).collect()) + AttrKind::Normal(normal) => { + Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect()) + } + AttrKind::Parsed(EarlyParsedAttribute::CfgTrace(_)) => Some(smallvec![sym::cfg_trace]), + AttrKind::Parsed(EarlyParsedAttribute::CfgAttrTrace) => { + Some(smallvec![sym::cfg_attr_trace]) } AttrKind::DocComment(_, _) => None, } @@ -128,6 +133,7 @@ impl AttributeExt for Attribute { fn path_span(&self) -> Option { match &self.kind { AttrKind::Normal(attr) => Some(attr.item.path.span), + AttrKind::Parsed(..) => unreachable!(), AttrKind::DocComment(_, _) => None, } } @@ -144,6 +150,8 @@ impl AttributeExt for Attribute { .zip(name) .all(|(s, n)| s.args.is_none() && s.ident.name == *n) } + AttrKind::Parsed(EarlyParsedAttribute::CfgTrace(_)) => name == &[sym::cfg_trace], + AttrKind::Parsed(EarlyParsedAttribute::CfgAttrTrace) => name == &[sym::cfg_attr_trace], AttrKind::DocComment(..) => false, } } @@ -153,10 +161,10 @@ impl AttributeExt for Attribute { } fn is_word(&self) -> bool { - if let AttrKind::Normal(normal) = &self.kind { - matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty)) - } else { - false + match &self.kind { + AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty), + AttrKind::Parsed(..) => unreachable!(), + AttrKind::DocComment(..) => false, } } @@ -170,6 +178,7 @@ impl AttributeExt for Attribute { fn meta_item_list(&self) -> Option> { match &self.kind { AttrKind::Normal(normal) => normal.item.meta_item_list(), + AttrKind::Parsed(..) => None, AttrKind::DocComment(..) => None, } } @@ -192,6 +201,7 @@ impl AttributeExt for Attribute { fn value_str(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.value_str(), + AttrKind::Parsed(..) => unreachable!(), AttrKind::DocComment(..) => None, } } @@ -204,16 +214,14 @@ 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() - { - Some((value, DocFragmentKind::Raw(value_span))) - } else { - None - } + AttrKind::Normal(normal) + if normal.item.path == sym::doc + && let Some(value) = normal.item.value_str() + && let Some(value_span) = normal.item.value_span() => + { + Some((value, DocFragmentKind::Raw(value_span))) } - _ => None, + AttrKind::Normal(..) | AttrKind::Parsed(..) => None, } } @@ -282,6 +290,7 @@ impl Attribute { pub fn meta(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.meta(self.span), + AttrKind::Parsed(..) => None, AttrKind::DocComment(..) => None, } } @@ -289,6 +298,7 @@ impl Attribute { pub fn meta_kind(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.meta_kind(), + AttrKind::Parsed(..) => unreachable!(), AttrKind::DocComment(..) => None, } } @@ -301,6 +311,7 @@ impl Attribute { .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}")) .to_attr_token_stream() .to_token_trees(), + AttrKind::Parsed(..) => vec![], AttrKind::DocComment(comment_kind, data) => vec![TokenTree::token_alone( token::DocComment(comment_kind, self.style, data), self.span, @@ -343,7 +354,7 @@ impl AttrItem { } pub fn meta_item_list(&self) -> Option> { - match &self.args.unparsed_ref()? { + match &self.args { AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { MetaItemKind::list_from_tokens(args.tokens.clone()) } @@ -364,7 +375,7 @@ impl AttrItem { /// #[attr("value")] /// ``` fn value_str(&self) -> Option { - match &self.args.unparsed_ref()? { + match &self.args { AttrArgs::Eq { expr, .. } => match expr.kind { ExprKind::Lit(token_lit) => { LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str()) @@ -388,7 +399,7 @@ impl AttrItem { /// #[attr("value")] /// ``` fn value_span(&self) -> Option { - match &self.args.unparsed_ref()? { + match &self.args { AttrArgs::Eq { expr, .. } => Some(expr.span), AttrArgs::Delimited(_) | AttrArgs::Empty => None, } @@ -404,7 +415,7 @@ impl AttrItem { } pub fn meta_kind(&self) -> Option { - MetaItemKind::from_attr_args(self.args.unparsed_ref()?) + MetaItemKind::from_attr_args(&self.args) } } @@ -810,13 +821,7 @@ pub fn mk_attr_word( span, )); - mk_attr_from_item( - g, - AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args) }, - tokens, - style, - span, - ) + mk_attr_from_item(g, AttrItem { unsafety, path, args }, tokens, style, span) } pub fn mk_attr_nested_word( @@ -857,13 +862,7 @@ pub fn mk_attr_nested_word( span, )); - mk_attr_from_item( - g, - AttrItem { unsafety, path, args: AttrItemKind::Unparsed(attr_args) }, - tokens, - style, - span, - ) + mk_attr_from_item(g, AttrItem { unsafety, path, args: attr_args }, tokens, style, span) } pub fn mk_attr_name_value_str( @@ -899,13 +898,7 @@ pub fn mk_attr_name_value_str( span, )); - mk_attr_from_item( - g, - AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args) }, - tokens, - style, - span, - ) + mk_attr_from_item(g, AttrItem { unsafety, path, args }, tokens, style, span) } pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator { diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 22a77f98917d7..b89c45fb1766a 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -461,7 +461,6 @@ macro_rules! common_visitor_and_walkers { ModSpans, MutTy, NormalAttr, - AttrItemKind, Parens, ParenthesizedArgs, PatFieldsRest, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index bfca18a42635e..d9e9ba2b46745 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -688,6 +688,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.print_attr_item(&normal.item, attr.span); self.word("]"); } + ast::AttrKind::Parsed(..) => panic!("parsed attrs are never printed"), ast::AttrKind::DocComment(comment_kind, data) => { self.word(doc_comment_to_string( DocFragmentKind::Sugared(*comment_kind), @@ -709,7 +710,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } ast::Safety::Default | ast::Safety::Safe(_) => {} } - match &item.args.unparsed_ref().expect("Parsed attributes are never printed") { + match &item.args { AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common( Some(MacHeader::Path(&item.path)), false, diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index da37e51286d53..fc7f7731199e9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -320,7 +320,7 @@ pub fn parse_cfg_attr( features: Option<&Features>, lint_node_id: ast::NodeId, ) -> Option<(CfgEntry, Vec<(WithTokens, Span)>)> { - match cfg_attr.get_normal_item().args.unparsed_ref().unwrap() { + match &cfg_attr.get_normal_item().args { 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| { @@ -349,7 +349,7 @@ pub fn parse_cfg_attr( } _ => { let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = - cfg_attr.get_normal_item().args.unparsed_ref()? + cfg_attr.get_normal_item().args { (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) } else { diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index e8bef70be550a..9bf035a298e51 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_ast as ast; use rustc_ast::token::DocFragmentKind; -use rustc_ast::{AttrItemKind, AttrStyle, CRATE_NODE_ID, NodeId, Safety}; +use rustc_ast::{AttrStyle, CRATE_NODE_ID, NodeId, Safety}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_feature::{BUILTIN_ATTRIBUTE_MAP, Features}; @@ -158,15 +158,12 @@ impl<'sess> AttributeParser<'sess> { allow_expr_metavar: AllowExprMetavar, expected_safety: AttributeSafety, ) -> Option { - let ast::AttrKind::Normal(normal_attr) = &attr.kind else { - panic!("parse_single called on a doc attr") - }; - let parts = - normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); + let attr_item = attr.get_normal_item(); + let parts = attr_item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); - let path = AttrPath::from_ast(&normal_attr.item.path, identity); + let path = AttrPath::from_ast(&attr_item.path, identity); let args = ArgParser::from_attr_args( - &normal_attr.item.args.unparsed_ref().unwrap(), + &attr_item.args, &parts, &sess.psess, emit_errors, @@ -175,10 +172,10 @@ impl<'sess> AttributeParser<'sess> { Self::parse_single_args( sess, attr.span, - normal_attr.item.span(), + attr_item.span(), attr.style, path, - Some(normal_attr.item.unsafety), + Some(attr_item.unsafety), expected_safety, ParsedDescription::Attribute, target_span, @@ -329,19 +326,13 @@ impl<'sess> AttributeParser<'sess> { comment: *symbol, })); } + ast::AttrKind::Parsed(parsed) => { + early_parsed_state.accept_early_parsed_attribute(attr_span, lower_span, parsed); + continue; + } ast::AttrKind::Normal(n) => { attr_paths.push(PathParser(&n.item.path)); let attr_path = AttrPath::from_ast(&n.item.path, lower_span); - - let args = match &n.item.args { - AttrItemKind::Unparsed(args) => args, - AttrItemKind::Parsed(parsed) => { - early_parsed_state - .accept_early_parsed_attribute(attr_span, lower_span, parsed); - continue; - } - }; - let parts = n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); let inner_span = lower_span(n.item.span()); @@ -360,7 +351,7 @@ impl<'sess> AttributeParser<'sess> { } let Some(args) = ArgParser::from_attr_args( - args, + &n.item.args, &parts, &self.sess.psess, self.should_emit, @@ -431,8 +422,7 @@ impl<'sess> AttributeParser<'sess> { } else { let attr = AttrItem { path: attr_path.clone(), - args: self - .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span), + args: self.lower_attr_args(&n.item.args, lower_span), id: HashIgnoredAttrId { attr_id: attr.id }, style: attr.style, span: attr_span, diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 748e452a24cac..fb7e7da76b627 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -53,7 +53,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { } _ => { let attr_item = attr.get_normal_item(); - if let AttrArgs::Eq { .. } = attr_item.args.unparsed_ref().unwrap() { + if let AttrArgs::Eq { .. } = attr_item.args { // All key-value attributes are restricted to meta-item syntax. match parse_meta(psess, attr) { Ok(_) => {} @@ -72,7 +72,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.unparsed_ref().unwrap() { + kind: match &item.args { AttrArgs::Empty => MetaItemKind::Word, AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => { check_meta_bad_delim(psess, *dspan, *delim); diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index d682afad25966..29013d7c8b94d 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -361,7 +361,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: rustc_ast::ast::AttrItemKind::Unparsed(ast::AttrArgs::Delimited(never_arg)), + args: ast::AttrArgs::Delimited(never_arg), }; let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None }); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); @@ -423,13 +423,11 @@ mod llvm_enzyme { } }; // Now update for d_fn - 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, - }), - ); + rustc_ad_attr.item.args = 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); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index f9ec93a1115dd..fcba0b6be4ee0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -807,29 +807,24 @@ impl<'a> TraitDef<'a> { rustc_ast::AttrItem { unsafety: Safety::Default, path: rustc_const_unstable, - 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(), - }, - )), + 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(), + }), }, self.span, ), diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index 598c012309489..6218674917098 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -12,14 +12,10 @@ fn compile_for_device(ecx: &mut ExtCtxt<'_>) -> bool { ecx.sess.opts.unstable_opts.offload.contains(&Offload::Device) } -fn outer_normal_attr( - kind: &Box, - id: rustc_ast::AttrId, - span: Span, -) -> rustc_ast::Attribute { - let style = rustc_ast::AttrStyle::Outer; - let kind = rustc_ast::AttrKind::Normal(kind.clone()); - rustc_ast::Attribute { kind, id, style, span } +fn outer_normal_attr(normal: &Box, id: ast::AttrId, span: Span) -> ast::Attribute { + let style = ast::AttrStyle::Outer; + let kind = ast::AttrKind::Normal(normal.clone()); + ast::Attribute { kind, id, style, span } } fn extract_fn( @@ -118,7 +114,7 @@ pub(crate) fn expand_kernel( let unsafe_item = AttrItem { unsafety: ast::Safety::Unsafe(span), path: ast::Path::from_ident(Ident::new(sym::no_mangle, span)), - args: ast::AttrItemKind::Unparsed(ast::AttrArgs::Empty), + args: ast::AttrArgs::Empty, }; let no_mangle_attr = Box::new(ast::NormalAttr { item: unsafe_item, tokens: None }); @@ -182,7 +178,7 @@ pub(crate) fn expand_kernel( let inline_item = ast::AttrItem { unsafety: ast::Safety::Default, path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)), - args: rustc_ast::ast::AttrItemKind::Unparsed(ast::AttrArgs::Delimited(never_arg)), + args: ast::AttrArgs::Delimited(never_arg), }; let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None }); diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index b03f294c4af13..457cd88e360c0 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -7,8 +7,8 @@ use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, WithTokens, }; use rustc_ast::{ - self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, EarlyParsedAttribute, HasAttrs, - HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr, + self as ast, AttrStyle, Attribute, EarlyParsedAttribute, HasAttrs, HasTokens, MetaItem, + MetaItemInner, NodeId, }; use rustc_attr_parsing::parser::AllowExprMetavar; use rustc_attr_parsing::{ @@ -143,19 +143,6 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec .collect() } -pub(crate) fn attr_into_trace(mut attr: Attribute, trace_name: Symbol) -> Attribute { - match &mut attr.kind { - AttrKind::Normal(normal) => { - let NormalAttr { item, tokens } = &mut **normal; - item.path.segments[0].ident.name = trace_name; - // This makes the trace attributes unobservable to token-based proc macros. - *tokens = Some(LazyAttrTokenStream::new_direct(AttrTokenStream::default())); - } - AttrKind::DocComment(..) => unreachable!(), - } - attr -} - #[macro_export] macro_rules! configure { ($this:ident, $node:ident) => { @@ -264,8 +251,7 @@ impl<'a> StripUnconfigured<'a> { // A trace attribute left in AST in place of the original `cfg_attr` attribute. // It can later be used by lints or other diagnostics. let mut trace_attr = cfg_attr.clone(); - trace_attr.replace_args(AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace)); - let trace_attr = attr_into_trace(trace_attr, sym::cfg_attr_trace); + trace_attr.convert_normal_to_parsed(EarlyParsedAttribute::CfgAttrTrace); let Some((cfg_predicate, expanded_attrs)) = rustc_attr_parsing::parse_cfg_attr( cfg_attr, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index b35f6dcb0af6f..f7de42ca7a4af 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -7,7 +7,7 @@ use rustc_ast::mut_visit::*; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ - self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrItemKind, AttrStyle, AttrVec, + self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrKind, AttrStyle, AttrVec, DUMMY_NODE_ID, DelegationSource, DelegationSuffixes, EarlyParsedAttribute, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, @@ -36,7 +36,7 @@ use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sy use smallvec::SmallVec; use crate::base::*; -use crate::config::{StripUnconfigured, attr_into_trace}; +use crate::config::StripUnconfigured; use crate::diagnostics::{ EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, @@ -818,10 +818,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.unparsed_ref().unwrap() { + if let AttrArgs::Eq { .. } = attr_item.args { self.cx.dcx().emit_err(UnsupportedKeyValue { span }); } - let inner_tokens = attr_item.args.unparsed_ref().unwrap().inner_tokens(); + let inner_tokens = attr_item.args.inner_tokens(); match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) { Ok(tok_result) => { let fragment = self.parse_ast_fragment( @@ -2163,7 +2163,9 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let mut cfg_pos = None; let mut attr_pos = None; for (pos, attr) in item.attrs().iter().enumerate() { - if !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) { + if let AttrKind::Normal(..) = attr.kind + && !self.cx.expanded_inert_attrs.is_marked(attr) + { let name = attr.name(); if name == Some(sym::cfg) || name == Some(sym::cfg_attr) { cfg_pos = Some(pos); // a cfg attr found, no need to search anymore @@ -2285,8 +2287,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { if res.as_bool() { // A trace attribute left in AST in place of the original `cfg` attribute. // It can later be used by lints or other diagnostics. - let mut trace_attr = attr_into_trace(attr, sym::cfg_trace); - trace_attr.replace_args(AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg))); + let mut trace_attr = attr; + trace_attr.convert_normal_to_parsed(EarlyParsedAttribute::CfgTrace(cfg)); node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d1483e4e6016f..a90869f14771b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -246,7 +246,6 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::rustc_deprecated_safe_2024, sym::rustc_pub_transparent, - // ========================================================================== // Internal attributes: Type system related: // ========================================================================== diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c34005d2ea7f5..9ac94dd17706e 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -776,6 +776,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & AttrKind::DocComment(CommentKind::Block, _) => { BuiltinUnusedDocCommentSub::BlockHelp } + AttrKind::Parsed(..) => unreachable!(), }; cx.emit_span_lint( UNUSED_DOC_COMMENTS, diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 5b0d99242f440..c0e3116211d8a 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_ast::token::{self, MetaVarKind}; use rustc_ast::tokenstream::{ParserRange, WithTokens}; -use rustc_ast::{AttrItemKind, Attribute, attr}; +use rustc_ast::{Attribute, attr}; use rustc_errors::codes::*; use rustc_errors::{Diag, PResult, msg}; use rustc_span::{BytePos, Span}; @@ -350,11 +350,7 @@ impl<'a> Parser<'a> { this.expect(exp!(CloseParen))?; } Ok(( - WithTokens::new(ast::AttrItem { - unsafety, - path, - args: AttrItemKind::Unparsed(args), - }), + WithTokens::new(ast::AttrItem { unsafety, path, args }), Trailing::No, UsePreAttrPos::No, )) diff --git a/compiler/rustc_parse/src/parser/cfg_select.rs b/compiler/rustc_parse/src/parser/cfg_select.rs index a4aeb919b106a..6d4121776d940 100644 --- a/compiler/rustc_parse/src/parser/cfg_select.rs +++ b/compiler/rustc_parse/src/parser/cfg_select.rs @@ -57,6 +57,7 @@ impl<'a> Parser<'a> { for attr in attrs.take_for_recovery(self.psess) { match attr.kind { AttrKind::Normal(..) => spans.attrs.push(attr.span), + AttrKind::Parsed(..) => unreachable!(), // `parse_outer_attributes` already emitted E0753 for inner doc comments before // recovering them as outer doc-comment attributes. AttrKind::DocComment(comment_kind, _) diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index f9b6367d4a9d4..622861ff2673a 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -773,7 +773,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_attribute(&mut self, attr: &'v ast::Attribute) { record_variants!( (self, attr, attr.kind, None, ast, Attribute, AttrKind), - [Normal, DocComment] + [Normal, Parsed, DocComment] ); ast_visit::walk_attribute(self, attr) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 3dabae553204c..0bcf6da707b15 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -558,10 +558,22 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_attribute(&mut self, attr: &'a Attribute) { let orig_in_attr = mem::replace(&mut self.invocation_parent.in_attr, true); - if !attr.is_doc_comment() && attr::is_builtin_attr(attr) { - self.r - .builtin_attrs - .push((attr.get_normal_item().path.segments[0].ident, self.parent_scope)); + match &attr.kind { + AttrKind::Normal(_) => { + if attr::is_builtin_attr(attr) { + self.r + .builtin_attrs + .push((attr.get_normal_item().path.segments[0].ident, self.parent_scope)); + } + } + AttrKind::Parsed( + EarlyParsedAttribute::CfgTrace(_) | EarlyParsedAttribute::CfgAttrTrace, + ) => { + // No action needed because `sym::cfg_trace` and `sym::cfg_attr_trace` are + // synthetic, invalid identifiers that can't overlap with any real identifier in + // the code. + } + AttrKind::DocComment(..) => {} } visit::walk_attribute(self, attr); self.invocation_parent.in_attr = orig_in_attr; diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index d71c8e9894bf7..6dc3ec13cf2c3 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -1,6 +1,6 @@ use super::MIXED_ATTRIBUTES_STYLE; use clippy_utils::diagnostics::span_lint; -use rustc_ast::{AttrKind, AttrStyle, Attribute}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, EarlyParsedAttribute}; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{EarlyContext, LintContext}; use rustc_span::source_map::SourceMap; @@ -12,6 +12,8 @@ enum SimpleAttrKind { Doc, /// A normal attribute, with its name symbols. Normal(Vec), + CfgTrace, + CfgAttrTrace, } impl From<&AttrKind> for SimpleAttrKind { @@ -27,6 +29,12 @@ impl From<&AttrKind> for SimpleAttrKind { .collect::>(); Self::Normal(path_symbols) }, + AttrKind::Parsed(parsed) => { + match &**parsed { + EarlyParsedAttribute::CfgTrace(_) => Self::CfgTrace, + EarlyParsedAttribute::CfgAttrTrace => Self::CfgAttrTrace, + } + } AttrKind::DocComment(..) => Self::Doc, } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 9b481952c98ee..2833619814cc3 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -17,7 +17,7 @@ use clippy_config::Conf; use clippy_utils::check_clippy_attr; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; -use rustc_ast::{self as ast, AttrArgs, AttrItemKind, AttrKind, Attribute, MetaItemInner, MetaItemKind}; +use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind}; use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, TraitFn, TraitItem, TraitItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -615,7 +615,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if attr.has_name(sym::ignore) && let AttrKind::Normal(normal_attr) = &attr.kind - && !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })) + && !matches!(normal_attr.item.args, AttrArgs::Eq { .. }) { span_lint_and_help( cx, diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs index 1a9abd88a46ae..fd27e30a67f3b 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -2,19 +2,19 @@ use super::{Attribute, SHOULD_PANIC_WITHOUT_EXPECT}; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; -use rustc_ast::{AttrArgs, AttrItemKind, AttrKind}; +use rustc_ast::{AttrArgs, AttrKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; use rustc_span::sym; pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { if let AttrKind::Normal(normal_attr) = &attr.kind { - if let AttrItemKind::Unparsed(AttrArgs::Eq { .. }) = &normal_attr.item.args { + if let AttrArgs::Eq { .. } = &normal_attr.item.args { // `#[should_panic = ".."]` found, good return; } - if let AttrItemKind::Unparsed(AttrArgs::Delimited(args)) = &normal_attr.item.args + if let AttrArgs::Delimited(args) = &normal_attr.item.args && let mut tt_iter = args.tokens.iter() && let Some(TokenTree::Token( Token { diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs index 88d07be0d4d46..a769954696c8a 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::attr::data_structures::CfgEntry; -use rustc_ast::{AttrItemKind, EarlyParsedAttribute}; +use rustc_ast::{AttrKind, EarlyParsedAttribute}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -34,23 +34,20 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]); impl EarlyLintPass for CfgNotTest { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) { - if attr.has_name(sym::cfg_trace) { - let AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg)) = &attr.get_normal_item().args else { - unreachable!() - }; - - if contains_not_test(cfg, false) { - span_lint_and_then( - cx, - CFG_NOT_TEST, - attr.span, - "code is excluded from test builds", - |diag| { - diag.help("consider not excluding any code from test builds"); - diag.note_once("this could increase code coverage despite not actually being tested"); - }, - ); - } + if let AttrKind::Parsed(parsed) = &attr.kind + && let EarlyParsedAttribute::CfgTrace(cfg) = &**parsed + && contains_not_test(cfg, false) + { + span_lint_and_then( + cx, + CFG_NOT_TEST, + attr.span, + "code is excluded from test builds", + |diag| { + diag.help("consider not excluding any code from test builds"); + diag.note_once("this could increase code coverage despite not actually being tested"); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs index 6c800f47b68a9..0a4ef50f14bb3 100644 --- a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::{AttrArgs, AttrItemKind, AttrKind, AttrStyle, Attribute}; +use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; @@ -9,9 +9,9 @@ use super::DOC_INCLUDE_WITHOUT_CFG; pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { for attr in attrs { if !attr.span.from_expansion() - && let AttrKind::Normal(ref item) = attr.kind + && let AttrKind::Normal(ref normal) = attr.kind && attr.doc_str().is_some() - && let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.item.args + && let AttrArgs::Eq { expr: meta, .. } = &normal.item.args && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. diff --git a/src/tools/clippy/clippy_lints/src/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/empty_line_after.rs index f076c6d29138d..6eb5db1f7304a 100644 --- a/src/tools/clippy/clippy_lints/src/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/empty_line_after.rs @@ -251,7 +251,7 @@ impl Stop { Some(Self { span: attr.span, kind: match attr.kind { - AttrKind::Normal(_) => StopKind::Attr, + AttrKind::Normal(_) | AttrKind::Parsed(_) => StopKind::Attr, AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind), }, first: file.lookup_line(file.relative_position(lo))?, diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 39d252f4f3b6b..3c35c2fc9c82d 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_opt; use clippy_utils::sym; -use rustc_ast::{AttrArgs, AttrItemKind, AttrKind, Attribute, LitKind}; +use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -89,10 +89,10 @@ impl EarlyLintPass for LargeIncludeFile { if !attr.span.from_expansion() // Currently, rustc limits the usage of macro at the top-level of attributes, // so we don't need to recurse into each level. - && let AttrKind::Normal(ref item) = attr.kind + && let AttrKind::Normal(ref normal) = attr.kind && let Some(doc) = attr.doc_str() && doc.as_str().len() as u64 > self.max_file_size - && let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.item.args + && let AttrArgs::Eq { expr: meta, .. } = &normal.item.args && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index fe51f841bc8b6..ea9dfe32ab533 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -1004,20 +1004,13 @@ fn eq_attr(l: &Attribute, r: &Attribute) -> bool { && match (&l.kind, &r.kind) { (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2, (Normal(l), Normal(r)) => { - eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args) + eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args) }, + (Parsed(..), _) | (_, Parsed(..)) => unreachable!(), _ => false, } } -fn eq_attr_item_kind(l: &AttrItemKind, r: &AttrItemKind) -> bool { - match (l, r) { - (AttrItemKind::Unparsed(l), AttrItemKind::Unparsed(r)) => eq_attr_args(l, r), - (AttrItemKind::Parsed(_l), AttrItemKind::Parsed(_r)) => todo!(), - _ => false, - } -} - fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool { use AttrArgs::*; match (l, r) { @@ -1042,8 +1035,8 @@ pub fn is_cfg_test(item: &impl HasAttrs) -> bool { && item_list.iter().any(|item| item.has_name(sym::test)) { true - } else if attr.has_name(sym::cfg_trace) - && let AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg)) = &attr.get_normal_item().args + } else if let AttrKind::Parsed(parsed) = &attr.kind + && let EarlyParsedAttribute::CfgTrace(cfg) = &**parsed { requires_test_cfg(cfg) } else { diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 7b47141c9fb8a..b82240bf36d1e 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -369,6 +369,7 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { (Pat::Str("#"), Pat::Str("]")) } }, + AttrKind::Parsed(..) => unreachable!(), AttrKind::DocComment(_kind @ CommentKind::Line, ..) => { if attr.style == AttrStyle::Outer { (Pat::Str("///"), Pat::Str("")) diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index baabd678fefd0..a886e1327170b 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -10,7 +10,7 @@ ast-stats - Impl 144 (NN.N%) 1 ast-stats - Trait 144 (NN.N%) 1 ast-stats - Fn 288 (NN.N%) 2 ast-stats - Use 576 (NN.N%) 4 -ast-stats PathSegment 864 (NN.N%) 36 24 +ast-stats PathSegment 840 (NN.N%) 35 24 ast-stats Ty 784 (NN.N%) 14 56 ast-stats - Ptr 56 (NN.N%) 1 ast-stats - Ref 56 (NN.N%) 1 @@ -43,7 +43,8 @@ ast-stats Param 160 (NN.N%) 4 40 ast-stats Block 144 (NN.N%) 6 24 ast-stats Attribute 128 (NN.N%) 4 32 ast-stats - DocComment 32 (NN.N%) 1 -ast-stats - Normal 96 (NN.N%) 3 +ast-stats - Parsed 32 (NN.N%) 1 +ast-stats - Normal 64 (NN.N%) 2 ast-stats InlineAsm 120 (NN.N%) 1 120 ast-stats FnDecl 120 (NN.N%) 5 24 ast-stats Local 96 (NN.N%) 1 96 @@ -57,7 +58,7 @@ ast-stats GenericArgs 40 (NN.N%) 1 40 ast-stats - AngleBracketed 40 (NN.N%) 1 ast-stats Crate 40 (NN.N%) 1 40 ast-stats ---------------------------------------------------------------- -ast-stats Total 6_976 127 +ast-stats Total 6_952 126 ast-stats ================================================================ hir-stats ================================================================ hir-stats HIR STATS: input_stats From 1a8ecb0b5b98250a03971a5da7095547054430e6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Jul 2026 21:01:59 +1000 Subject: [PATCH 5/7] Remove `sym::cfg_trace` and `sym::cfg_attr_trace` This commit pushes further than the previous one, relying more heavily on looking directly at `AttrKind::Parsed` to handle various cases. Note that the removed clippy code was actually dead. --- compiler/rustc_ast/src/ast.rs | 23 ++++++++--- compiler/rustc_ast/src/attr/mod.rs | 25 +++++------- .../rustc_ast_passes/src/ast_validation.rs | 21 +++++----- compiler/rustc_ast_pretty/src/pprust/state.rs | 20 ++++++---- .../src/attributes/codegen_attrs.rs | 3 -- .../rustc_attr_parsing/src/attributes/util.rs | 4 +- .../rustc_attr_parsing/src/early_parsed.rs | 6 +-- compiler/rustc_attr_parsing/src/interface.rs | 6 +-- compiler/rustc_attr_parsing/src/lib.rs | 1 + .../rustc_attr_parsing/src/validate_attr.rs | 11 +++-- compiler/rustc_expand/src/expand.rs | 40 ++++++++++++------- compiler/rustc_expand/src/lib.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 20 ---------- .../rustc_parse/src/parser/attr_wrapper.rs | 26 +++++++----- compiler/rustc_resolve/src/def_collector.rs | 15 +++---- .../rustc_resolve/src/diagnostics/impls.rs | 5 --- compiler/rustc_span/src/symbol.rs | 2 - .../src/attrs/duplicated_attributes.rs | 11 +---- 18 files changed, 110 insertions(+), 130 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8573550af767f..d689c01f28972 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3462,14 +3462,27 @@ pub struct AttrItem { /// 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. -/// -/// See also `EARLY_PARSED_ATTRIBUTES`. #[derive(Clone, Encodable, Decodable, Debug, StableHash)] pub enum EarlyParsedAttribute { + /// This special attribute is added by the compiler when a `cfg` attribute is expanded so that + /// subsequent code can tell that conditional compilation occurred. A `#[cfg(pred)]` with a + /// true predicate is replaced by a synthetic `CfgTrace` attribute that records the parsed + /// predicate. A `#[cfg(pred)]` with a false predicate leaves no trace because there is no node + /// left to annotate. + /// + /// The attribute is used for some diagnostics, by rustdoc (for detecting feature usage), and + /// by some clippy lints. It is treated specially in various places because it must not be + /// observable in any way that could change behaviour. For example, it is never pretty-printed + /// and it is hidden from proc macros. CfgTrace(CfgEntry), + + /// This special attribute is added by the compiler when a `cfg_attr` attribute is expanded so + /// that subsequent code can tell that conditional compilation occurred. A `#[cfg_attr(pred, + /// attrs)]` is replaced by a synthetic `CfgAttrTrace` attribute whether the predicate + /// evaluated true or not (or even failed to parse). The `pred` and `attrs` are not recorded + /// because they are not needed. + /// + /// In all other respects, it is the same as `CfgTrace`. CfgAttrTrace, } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index cca9656927640..0d6603da2a7dd 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -103,29 +103,21 @@ impl AttributeExt for Attribute { /// For a single-segment attribute, returns its name; otherwise, returns `None`. fn name(&self) -> Option { + use EarlyParsedAttribute::*; match &self.kind { - AttrKind::Normal(normal) => { - if let [ident] = &*normal.item.path.segments { - Some(ident.ident.name) - } else { - None - } - } - AttrKind::Parsed(EarlyParsedAttribute::CfgTrace(_)) => Some(sym::cfg_trace), - AttrKind::Parsed(EarlyParsedAttribute::CfgAttrTrace) => Some(sym::cfg_attr_trace), + AttrKind::Normal(normal) => normal.item.name(), + AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => None, AttrKind::DocComment(..) => None, } } fn symbol_path(&self) -> Option> { + use EarlyParsedAttribute::*; match &self.kind { AttrKind::Normal(normal) => { Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect()) } - AttrKind::Parsed(EarlyParsedAttribute::CfgTrace(_)) => Some(smallvec![sym::cfg_trace]), - AttrKind::Parsed(EarlyParsedAttribute::CfgAttrTrace) => { - Some(smallvec![sym::cfg_attr_trace]) - } + AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => None, AttrKind::DocComment(_, _) => None, } } @@ -150,8 +142,7 @@ impl AttributeExt for Attribute { .zip(name) .all(|(s, n)| s.args.is_none() && s.ident.name == *n) } - AttrKind::Parsed(EarlyParsedAttribute::CfgTrace(_)) => name == &[sym::cfg_trace], - AttrKind::Parsed(EarlyParsedAttribute::CfgAttrTrace) => name == &[sym::cfg_attr_trace], + AttrKind::Parsed(..) => false, AttrKind::DocComment(..) => false, } } @@ -349,6 +340,10 @@ impl Attribute { } impl AttrItem { + pub fn name(&self) -> Option { + if let [seg] = &*self.path.segments { Some(seg.ident.name) } else { None } + } + pub fn span(&self) -> Span { self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span)) } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 8b776edad4d6e..bb31f32df9b2d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -514,22 +514,19 @@ impl<'a> AstValidator<'a> { } fn check_decl_attrs(&self, fn_decl: &FnDecl) { + use EarlyParsedAttribute::*; fn_decl .inputs .iter() .flat_map(|i| i.attrs.as_ref()) - .filter(|attr| { - let arr = [ - sym::allow, - sym::cfg_trace, - sym::cfg_attr_trace, - sym::deny, - sym::expect, - sym::forbid, - sym::splat, - sym::warn, - ]; - !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(*attr) + .filter(|attr| match &attr.kind { + AttrKind::Normal(normal) => { + let arr = + [sym::allow, sym::deny, sym::expect, sym::forbid, sym::splat, sym::warn]; + !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(&normal.item) + } + AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => false, + AttrKind::DocComment(..) => true, }) .for_each(|attr| { if attr.is_doc_comment() { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index d9e9ba2b46745..0b4463bb71fd8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -15,9 +15,9 @@ use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; use rustc_ast::{ - self as ast, AttrArgs, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, GenericBound, - InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind, - RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, + self as ast, AttrArgs, AttrKind, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, + GenericBound, InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, + InlineAsmTemplatePiece, PatKind, RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, }; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; @@ -670,10 +670,14 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool { - if attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) { - // It's not a valid identifier, so avoid printing it - // to keep the printed code reasonably parse-able. - return false; + use ast::EarlyParsedAttribute::*; + match attr.kind { + AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => { + // These are internal synthetic attributes with no syntax, so avoid printing them + // to keep the printed code reasonably parse-able. + return false; + } + AttrKind::Normal(_) | AttrKind::DocComment(..) => {} } if !is_inline { self.hardbreak_if_not_bol(); @@ -688,7 +692,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.print_attr_item(&normal.item, attr.span); self.word("]"); } - ast::AttrKind::Parsed(..) => panic!("parsed attrs are never printed"), + ast::AttrKind::Parsed(..) => unreachable!(), // due to early return above ast::AttrKind::DocComment(comment_kind, data) => { self.word(doc_comment_to_string( DocFragmentKind::Sugared(*comment_kind), diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 3f9313b19ccd0..1de0bba71ffd2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -248,9 +248,6 @@ impl AttributeParser for NakedParser { // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains // accurate. const ALLOW_LIST: &[rustc_span::Symbol] = &[ - // conditional compilation - sym::cfg_trace, - sym::cfg_attr_trace, // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`) sym::test, sym::ignore, diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 82f515aff3999..22ae0da908233 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -26,8 +26,8 @@ pub fn parse_version(s: Symbol) -> Option { Some(RustcVersion { major, minor, patch }) } -pub fn is_builtin_attr(attr: &ast::Attribute) -> bool { - attr.is_doc_comment() || attr.name().is_some_and(|name| is_builtin_attr_name(name)) +pub fn is_builtin_attr(attr: &ast::AttrItem) -> bool { + attr.name().is_some_and(|name| is_builtin_attr_name(name)) } /// Parse a single integer. diff --git a/compiler/rustc_attr_parsing/src/early_parsed.rs b/compiler/rustc_attr_parsing/src/early_parsed.rs index 57607102ed8c4..b7b84bdd1fbe2 100644 --- a/compiler/rustc_attr_parsing/src/early_parsed.rs +++ b/compiler/rustc_attr_parsing/src/early_parsed.rs @@ -2,13 +2,9 @@ use rustc_ast::EarlyParsedAttribute; use rustc_ast::attr::data_structures::CfgEntry; use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::Span; use thin_vec::ThinVec; -/// See also `EarlyParsedAttribute`. -pub(crate) const EARLY_PARSED_ATTRIBUTES: &[&[Symbol]] = - &[&[sym::cfg_trace], &[sym::cfg_attr_trace]]; - /// This struct contains the state necessary to convert early parsed attributes to hir attributes /// The only conversion that really happens here is that multiple early parsed attributes are /// merged into a single hir attribute, representing their combined state. diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 9bf035a298e51..0e9704e9a83b6 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -20,7 +20,7 @@ use crate::attributes::AttributeSafety; use crate::context::{ ATTRIBUTE_PARSERS, AcceptContext, FinalizeContext, FinalizeFn, SharedContext, }; -use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState}; +use crate::early_parsed::EarlyParsedState; use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; use crate::{AttributeTemplate, OmitDoc, ShouldEmit}; @@ -502,9 +502,7 @@ impl<'sess> AttributeParser<'sess> { &[sym::cfg_attr], ]; - ATTRIBUTE_PARSERS.accepters.contains_key(path) - || EARLY_PARSED_ATTRIBUTES.contains(&path) - || SPECIAL_ATTRIBUTES.contains(&path) + ATTRIBUTE_PARSERS.accepters.contains_key(path) || SPECIAL_ATTRIBUTES.contains(&path) } fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 3d6148f69537c..4c78b754dd29b 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -88,6 +88,7 @@ // tidy-alphabetical-start #![feature(decl_macro)] +#![feature(deref_patterns)] #![feature(iter_intersperse)] #![feature(try_blocks)] #![recursion_limit = "256"] diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index fb7e7da76b627..0ed57766d9236 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -6,7 +6,8 @@ use std::slice; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ - self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, + self as ast, AttrArgs, AttrKind, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, + Safety, }; use rustc_errors::{Applicability, Diagnostic, PResult}; use rustc_feature::BUILTIN_ATTRIBUTE_MAP; @@ -20,9 +21,11 @@ use rustc_span::{Span, Symbol, sym}; use crate::{AttributeParser, AttributeTemplate, session_diagnostics as errors, template}; pub fn check_attr(psess: &ParseSess, attr: &Attribute) { - if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) - { - return; + use ast::EarlyParsedAttribute::*; + match &attr.kind { + AttrKind::Normal(_) => {} + AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => return, + AttrKind::DocComment(..) => return, } let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index f7de42ca7a4af..a8c9a4ecd1297 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2209,6 +2209,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Detect use of feature-gated or invalid attributes on macro invocations // since they will not be detected after macro expansion. fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { + use EarlyParsedAttribute::*; let features = self.cx.ecfg.features; let mut attrs = attrs.iter().peekable(); let mut span: Option = None; @@ -2241,21 +2242,30 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.current_expansion.lint_node_id, crate::diagnostics::MacroCallUnusedDocComment { span: attr.span }, ); - } else if rustc_attr_parsing::is_builtin_attr(attr) - && !AttributeParser::is_parsed_attribute(&attr.path()) - { - let attr_name = attr.name().unwrap(); - self.cx.sess.psess.buffer_lint( - UNUSED_ATTRIBUTES, - attr.span, - self.cx.current_expansion.lint_node_id, - crate::diagnostics::UnusedBuiltinAttribute { - attr_name, - macro_name: pprust::path_to_string(&call.path), - invoc_span: call.path.span, - attr_span: attr.span, - }, - ); + continue; + } + + match &attr.kind { + AttrKind::Normal(normal) + if rustc_attr_parsing::is_builtin_attr(&normal.item) + && !AttributeParser::is_parsed_attribute(&attr.path()) => + { + let attr_name = attr.name().unwrap(); + self.cx.sess.psess.buffer_lint( + UNUSED_ATTRIBUTES, + attr.span, + self.cx.current_expansion.lint_node_id, + crate::diagnostics::UnusedBuiltinAttribute { + attr_name, + macro_name: pprust::path_to_string(&call.path), + invoc_span: call.path.span, + attr_span: attr.span, + }, + ); + } + AttrKind::Normal(_) => {} + AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => {} + AttrKind::DocComment(..) => unreachable!(), // handled above } } } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index ec55f40b6c1e0..8bbca0ddf0f90 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -2,6 +2,7 @@ #![allow(internal_features)] #![feature(associated_type_defaults)] #![feature(default_field_values)] +#![feature(deref_patterns)] #![feature(macro_metavar_expr)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index a90869f14771b..cd01c29d40ac2 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -290,26 +290,6 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::rustc_macro_transparency, sym::rustc_autodiff, sym::rustc_offload_kernel, - // These special attributes are added by the compiler when `cfg` and `cfg_attr` attributes are - // expanded so that subsequent code can tell that conditional compilation occurred. - // - // - A `#[cfg(pred)]` with a true predicate is replaced by a `#[cfg_trace]` that records the - // parsed predicate. A `#[cfg(pred)]` with a false predicate leaves no trace because there - // is no node left to annotate. - // - // - A `#[cfg_attr(pred, attrs)]` is replaced by a `#[cfg_attr_trace]` attribute whether the - // predicate evaluated true or not (or even failed to parse). The `pred` and `attrs` are not - // recorded because they are not needed. - // - // The attributes are used for some diagnostics, by rustdoc (for detecting feature usage), and - // by some clippy lints. - // - // The attributes are not gated, to avoid stability errors, but they cannot be used in stable - // or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers. They - // are treated specially in various places because they must not be observable in any way that - // could change behaviour. For example, they are hidden from proc macros. - sym::cfg_trace, - sym::cfg_attr_trace, // ========================================================================== // Internal attributes, Diagnostics related: diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 6397fc855b86c..3cbcb9b5c5e5a 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -5,7 +5,7 @@ use rustc_ast::token::Token; use rustc_ast::tokenstream::{ AttrsTarget, LazyAttrTokenStream, NodeRange, ParserRange, Spacing, TokenCursor, }; -use rustc_ast::{self as ast, AttrVec, Attribute, HasTokens}; +use rustc_ast::{self as ast, AttrKind, AttrVec, Attribute, HasTokens}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::PResult; use rustc_session::parse::ParseSess; @@ -391,15 +391,21 @@ impl<'a> Parser<'a> { } } -/// Tokens are needed if: -/// - any non-single-segment attributes (other than doc comments) are present, -/// e.g. `rustfmt::skip`; or -/// - any `cfg_attr` attributes are present; or -/// - any single-segment, non-builtin attributes are present, e.g. `derive`, -/// `test`, `global_allocator`. fn needs_tokens(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| match attr.name() { - None => !attr.is_doc_comment(), - Some(name) => name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(name), + // Tokens are needed if... + attrs.iter().any(|attr| match &attr.kind { + AttrKind::Normal(normal) => { + match normal.item.name() { + // ... a multi-segment attribute is present, e.g. `rustfmt::skip`. + None => true, + // ... `cfg_attr` or a single-segment non-builtin attribute is present, e.g. + // `derive`, `test`, `global_allocator`. + Some(name) => name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(name), + } + } + // These attributes are created only during expansion, and can't re-enter the parser + // because they have no token form. + AttrKind::Parsed(_) => unreachable!(), + AttrKind::DocComment(..) => false, }) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 0bcf6da707b15..9cdc57d2f9c31 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -557,22 +557,17 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_attribute(&mut self, attr: &'a Attribute) { + use EarlyParsedAttribute::*; let orig_in_attr = mem::replace(&mut self.invocation_parent.in_attr, true); match &attr.kind { - AttrKind::Normal(_) => { - if attr::is_builtin_attr(attr) { + AttrKind::Normal(normal) => { + if attr::is_builtin_attr(&normal.item) { self.r .builtin_attrs - .push((attr.get_normal_item().path.segments[0].ident, self.parent_scope)); + .push((normal.item.path.segments[0].ident, self.parent_scope)); } } - AttrKind::Parsed( - EarlyParsedAttribute::CfgTrace(_) | EarlyParsedAttribute::CfgAttrTrace, - ) => { - // No action needed because `sym::cfg_trace` and `sym::cfg_attr_trace` are - // synthetic, invalid identifiers that can't overlap with any real identifier in - // the code. - } + AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => {} AttrKind::DocComment(..) => {} } visit::walk_attribute(self, attr); diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index a72e65f634b16..0b6b9899f48a6 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1514,11 +1514,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestions.extend( BUILTIN_ATTRIBUTES .iter() - // These trace attributes are compiler-generated and have - // deliberately invalid names. - .filter(|attr| { - !matches!(**attr, sym::cfg_trace | sym::cfg_attr_trace) - }) .map(|attr| TypoSuggestion::typo_from_name(*attr, res)), ); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index de090531a6192..9db6b9f9af126 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -581,7 +581,6 @@ symbols! { cfg_accessible, cfg_attr, cfg_attr_multi, - cfg_attr_trace: "", // must not be a valid identifier cfg_boolean_literals, cfg_contract_checks, cfg_doctest, @@ -602,7 +601,6 @@ symbols! { cfg_target_object_format, cfg_target_thread_local, cfg_target_vendor, - cfg_trace: "", // must not be a valid identifier cfg_ub_checks, cfg_version, cfi, diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index c956738edf0eb..da9ace312f7d3 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -43,21 +43,12 @@ fn check_duplicated_attr( }; if let Some(ident) = attr.ident() { let name = ident.name; - if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason - { + if name == sym::doc || name == sym::rustc_on_unimplemented || name == sym::reason { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. return; } - if let Some(direct_parent) = parent.last() - && *direct_parent == sym::cfg_trace - && [sym::all, sym::not, sym::any].contains(&name) - { - // FIXME: We don't correctly check `cfg`s for now, so if it's more complex than just a one - // level `cfg`, we leave. - return; - } } if let Some(value) = attr.value_str() { emit_if_duplicated( From 17d526f1be4f5f42e34a05efc35e32e8a638b0df Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 14 Jul 2026 12:45:32 +1000 Subject: [PATCH 6/7] Replace "early parsed" terminology AST attributes use "early parsed"/"parsed" terminology to refer to the `CfgTrace` and `CfgAttrTrace` attributes. I think this terminology is meant to echo the terminology used for HIR attributes, i.e. `hir::Attribute::{Parsed,Unparsed}`, probably because `hir::Attribute::Parsed` is used for attributes that aren't stored in a token-based form. But this naming is misleading. "Early parsed" attributes aren't parsed at all because they are inserted by the compiler and cannot be written in source code. There are also two comments that claim that these attributes are kept in parsed form "so they don't have to be reparsed every time they're used, for performance", which is simply incorrect. This commit renames these as "synthetic" attributes, which better reflects their nature. The commit also fixes the incorrect comments. Note that `is_parsed_attribute` is unchanged, because it refers to the HIR attribute meaning. (And the removal of the synthetic attributes from it in the previous commit is now more obviously correct.) --- compiler/rustc_ast/src/ast.rs | 26 ++++++------ compiler/rustc_ast/src/ast_traits.rs | 4 +- compiler/rustc_ast/src/attr/mod.rs | 42 +++++++++---------- compiler/rustc_ast/src/visit.rs | 2 +- .../rustc_ast_passes/src/ast_validation.rs | 4 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 6 +-- compiler/rustc_attr_parsing/src/interface.rs | 13 +++--- compiler/rustc_attr_parsing/src/lib.rs | 2 +- .../src/{early_parsed.rs => synthetic.rs} | 30 ++++++------- .../rustc_attr_parsing/src/validate_attr.rs | 4 +- compiler/rustc_expand/src/config.rs | 8 ++-- compiler/rustc_expand/src/expand.rs | 14 +++---- compiler/rustc_lint/src/builtin.rs | 2 +- .../rustc_parse/src/parser/attr_wrapper.rs | 4 +- compiler/rustc_parse/src/parser/cfg_select.rs | 2 +- compiler/rustc_passes/src/input_stats.rs | 2 +- compiler/rustc_resolve/src/def_collector.rs | 4 +- .../src/attrs/mixed_attributes_style.rs | 10 ++--- .../clippy/clippy_lints/src/cfg_not_test.rs | 6 +-- .../clippy_lints/src/empty_line_after.rs | 2 +- .../clippy/clippy_utils/src/ast_utils/mod.rs | 6 +-- .../clippy_utils/src/check_proc_macro.rs | 2 +- tests/ui/stats/input-stats.stderr | 2 +- 23 files changed, 98 insertions(+), 99 deletions(-) rename compiler/rustc_attr_parsing/src/{early_parsed.rs => synthetic.rs} (61%) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index d689c01f28972..b5f9fffffc5db 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3418,13 +3418,11 @@ pub struct Attribute { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub enum AttrKind { - /// A normal (non-doc comment) attribute, with attributes in unparsed form. + /// A normal attribute. Normal(Box), - /// A normal (non-doc comment) attribute, with attributes in parsed form, so they don't have to - /// be reparsed every time they're used, for performance. Only used for a small number of - /// attribute kinds. - Parsed(Box), + /// A synthetic attribute inserted by the compiler. + Synthetic(Box), /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`). /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal` @@ -3460,29 +3458,29 @@ pub struct AttrItem { pub args: AttrArgs, } -/// 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. +/// Synthetic attributes are inserted by the compiler and cannot be written in source code. They +/// receive special treatment in various ways because they must not affect observable behaviour: +/// they are invisible to proc macros, cannot be pretty-printed, and are unable to re-enter the +/// parser. #[derive(Clone, Encodable, Decodable, Debug, StableHash)] -pub enum EarlyParsedAttribute { - /// This special attribute is added by the compiler when a `cfg` attribute is expanded so that +pub enum SyntheticAttr { + /// This synthetic attribute is added by the compiler when a `cfg` attribute is expanded so that /// subsequent code can tell that conditional compilation occurred. A `#[cfg(pred)]` with a /// true predicate is replaced by a synthetic `CfgTrace` attribute that records the parsed /// predicate. A `#[cfg(pred)]` with a false predicate leaves no trace because there is no node /// left to annotate. /// /// The attribute is used for some diagnostics, by rustdoc (for detecting feature usage), and - /// by some clippy lints. It is treated specially in various places because it must not be - /// observable in any way that could change behaviour. For example, it is never pretty-printed - /// and it is hidden from proc macros. + /// by some clippy lints. CfgTrace(CfgEntry), - /// This special attribute is added by the compiler when a `cfg_attr` attribute is expanded so + /// This synthetic attribute is added by the compiler when a `cfg_attr` attribute is expanded so /// that subsequent code can tell that conditional compilation occurred. A `#[cfg_attr(pred, /// attrs)]` is replaced by a synthetic `CfgAttrTrace` attribute whether the predicate /// evaluated true or not (or even failed to parse). The `pred` and `attrs` are not recorded /// because they are not needed. /// - /// In all other respects, it is the same as `CfgTrace`. + /// The attribute is used by some clippy lints. CfgAttrTrace, } diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 10e3512ca2b1e..3b0478e24b195 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -170,13 +170,13 @@ impl HasTokens for Attribute { fn tokens(&self) -> Option<&LazyAttrTokenStream> { match &self.kind { AttrKind::Normal(normal) => normal.tokens.as_ref(), - AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), } } fn tokens_mut(&mut self) -> Option<&mut Option> { Some(match &mut self.kind { AttrKind::Normal(normal) => &mut normal.tokens, - AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), }) } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 0d6603da2a7dd..9f7ef74edf850 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -13,8 +13,8 @@ use thin_vec::{ThinVec, thin_vec}; use crate::ast::{ AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs, - EarlyParsedAttribute, Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, - MetaItemLit, NormalAttr, Path, PathSegment, Safety, + Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path, + PathSegment, Safety, SyntheticAttr, }; use crate::token::{ self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token, @@ -62,16 +62,16 @@ impl Attribute { pub fn get_normal_item(&self) -> &AttrItem { match &self.kind { AttrKind::Normal(normal) => &normal.item, - AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), } } - pub fn convert_normal_to_parsed(&mut self, early_parsed_attribute: EarlyParsedAttribute) { + pub fn convert_normal_to_synthetic(&mut self, synthetic_attr: SyntheticAttr) { match self.kind { AttrKind::Normal(..) => { - self.kind = AttrKind::Parsed(Box::new(early_parsed_attribute)); + self.kind = AttrKind::Synthetic(Box::new(synthetic_attr)); } - AttrKind::Parsed(..) | AttrKind::DocComment(..) => unreachable!(), + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), } } } @@ -87,7 +87,7 @@ impl AttributeExt for Attribute { AttrArgs::Eq { expr, .. } => Some(expr.span), _ => None, }, - AttrKind::Parsed(..) | AttrKind::DocComment(..) => None, + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None, } } @@ -96,28 +96,28 @@ impl AttributeExt for Attribute { /// a doc comment) will return `false`. fn is_doc_comment(&self) -> Option { match self.kind { - AttrKind::Normal(..) | AttrKind::Parsed(..) => None, + AttrKind::Normal(..) | AttrKind::Synthetic(..) => None, AttrKind::DocComment(..) => Some(self.span), } } /// For a single-segment attribute, returns its name; otherwise, returns `None`. fn name(&self) -> Option { - use EarlyParsedAttribute::*; + use SyntheticAttr::*; match &self.kind { AttrKind::Normal(normal) => normal.item.name(), - AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => None, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => None, AttrKind::DocComment(..) => None, } } fn symbol_path(&self) -> Option> { - use EarlyParsedAttribute::*; + use SyntheticAttr::*; match &self.kind { AttrKind::Normal(normal) => { Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect()) } - AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => None, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => None, AttrKind::DocComment(_, _) => None, } } @@ -125,7 +125,7 @@ impl AttributeExt for Attribute { fn path_span(&self) -> Option { match &self.kind { AttrKind::Normal(attr) => Some(attr.item.path.span), - AttrKind::Parsed(..) => unreachable!(), + AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(_, _) => None, } } @@ -142,7 +142,7 @@ impl AttributeExt for Attribute { .zip(name) .all(|(s, n)| s.args.is_none() && s.ident.name == *n) } - AttrKind::Parsed(..) => false, + AttrKind::Synthetic(..) => false, AttrKind::DocComment(..) => false, } } @@ -154,7 +154,7 @@ impl AttributeExt for Attribute { fn is_word(&self) -> bool { match &self.kind { AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty), - AttrKind::Parsed(..) => unreachable!(), + AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(..) => false, } } @@ -169,7 +169,7 @@ impl AttributeExt for Attribute { fn meta_item_list(&self) -> Option> { match &self.kind { AttrKind::Normal(normal) => normal.item.meta_item_list(), - AttrKind::Parsed(..) => None, + AttrKind::Synthetic(..) => None, AttrKind::DocComment(..) => None, } } @@ -192,7 +192,7 @@ impl AttributeExt for Attribute { fn value_str(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.value_str(), - AttrKind::Parsed(..) => unreachable!(), + AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(..) => None, } } @@ -212,7 +212,7 @@ impl AttributeExt for Attribute { { Some((value, DocFragmentKind::Raw(value_span))) } - AttrKind::Normal(..) | AttrKind::Parsed(..) => None, + AttrKind::Normal(..) | AttrKind::Synthetic(..) => None, } } @@ -281,7 +281,7 @@ impl Attribute { pub fn meta(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.meta(self.span), - AttrKind::Parsed(..) => None, + AttrKind::Synthetic(..) => None, AttrKind::DocComment(..) => None, } } @@ -289,7 +289,7 @@ impl Attribute { pub fn meta_kind(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.meta_kind(), - AttrKind::Parsed(..) => unreachable!(), + AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(..) => None, } } @@ -302,7 +302,7 @@ impl Attribute { .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}")) .to_attr_token_stream() .to_token_trees(), - AttrKind::Parsed(..) => vec![], + AttrKind::Synthetic(..) => vec![], AttrKind::DocComment(comment_kind, data) => vec![TokenTree::token_alone( token::DocComment(comment_kind, self.style, data), self.span, diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index b89c45fb1766a..1c3e881bd47be 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -365,7 +365,6 @@ macro_rules! common_visitor_and_walkers { crate::token::LitKind, crate::tokenstream::LazyAttrTokenStream, crate::tokenstream::TokenStream, - EarlyParsedAttribute, Movability, Mutability, Pinnedness, @@ -374,6 +373,7 @@ macro_rules! common_visitor_and_walkers { rustc_span::ErrorGuaranteed, std::borrow::Cow<'_, str>, Symbol, + SyntheticAttr, u8, usize, ); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bb31f32df9b2d..c7077da26a48c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -514,7 +514,7 @@ impl<'a> AstValidator<'a> { } fn check_decl_attrs(&self, fn_decl: &FnDecl) { - use EarlyParsedAttribute::*; + use SyntheticAttr::*; fn_decl .inputs .iter() @@ -525,7 +525,7 @@ impl<'a> AstValidator<'a> { [sym::allow, sym::deny, sym::expect, sym::forbid, sym::splat, sym::warn]; !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(&normal.item) } - AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => false, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => false, AttrKind::DocComment(..) => true, }) .for_each(|attr| { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 0b4463bb71fd8..268ca2a7e26f8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -670,9 +670,9 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool { - use ast::EarlyParsedAttribute::*; + use ast::SyntheticAttr::*; match attr.kind { - AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => { + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => { // These are internal synthetic attributes with no syntax, so avoid printing them // to keep the printed code reasonably parse-able. return false; @@ -692,7 +692,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.print_attr_item(&normal.item, attr.span); self.word("]"); } - ast::AttrKind::Parsed(..) => unreachable!(), // due to early return above + ast::AttrKind::Synthetic(..) => unreachable!(), // due to early return above ast::AttrKind::DocComment(comment_kind, data) => { self.word(doc_comment_to_string( DocFragmentKind::Sugared(*comment_kind), diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 0e9704e9a83b6..76efec612a7e2 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -20,9 +20,9 @@ use crate::attributes::AttributeSafety; use crate::context::{ ATTRIBUTE_PARSERS, AcceptContext, FinalizeContext, FinalizeFn, SharedContext, }; -use crate::early_parsed::EarlyParsedState; use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; +use crate::synthetic::SyntheticAttrState; use crate::{AttributeTemplate, OmitDoc, ShouldEmit}; pub struct EmitAttribute( @@ -290,7 +290,7 @@ impl<'sess> AttributeParser<'sess> { ) -> Vec { let mut attributes = Vec::new(); let mut attr_paths: Vec> = Vec::new(); - let mut early_parsed_state = EarlyParsedState::default(); + let mut synthetic_attr_state = SyntheticAttrState::default(); let mut finalizers: Vec = Vec::with_capacity(attrs.len()); @@ -326,8 +326,8 @@ impl<'sess> AttributeParser<'sess> { comment: *symbol, })); } - ast::AttrKind::Parsed(parsed) => { - early_parsed_state.accept_early_parsed_attribute(attr_span, lower_span, parsed); + ast::AttrKind::Synthetic(synthetic) => { + synthetic_attr_state.accept_synthetic_attr(attr_span, lower_span, synthetic); continue; } ast::AttrKind::Normal(n) => { @@ -448,7 +448,7 @@ impl<'sess> AttributeParser<'sess> { } } - early_parsed_state.finalize_early_parsed_attributes(&mut attributes); + synthetic_attr_state.finalize_synthetic_attrs(&mut attributes); for f in &finalizers { if let Some(attr) = f(&mut FinalizeContext { shared: SharedContext { @@ -497,7 +497,8 @@ impl<'sess> AttributeParser<'sess> { /// The list of attributes that are parsed attributes, /// even though they don't have a parser in `Late::parsers()` const SPECIAL_ATTRIBUTES: &[&[Symbol]] = &[ - // Cfg attrs are removed after being early-parsed, so don't need to be in the parser list + // Cfg attrs are removed after being converted into synthetic attrs and don't need to + // be in the parser list. &[sym::cfg], &[sym::cfg_attr], ]; diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 4c78b754dd29b..475957f384b45 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -99,12 +99,12 @@ mod attributes; mod check_cfg; mod context; mod diagnostics; -mod early_parsed; mod interface; pub mod parser; mod safety; mod session_diagnostics; mod stability; +mod synthetic; mod target_checking; mod template; pub mod validate_attr; diff --git a/compiler/rustc_attr_parsing/src/early_parsed.rs b/compiler/rustc_attr_parsing/src/synthetic.rs similarity index 61% rename from compiler/rustc_attr_parsing/src/early_parsed.rs rename to compiler/rustc_attr_parsing/src/synthetic.rs index b7b84bdd1fbe2..5e633e6ee3685 100644 --- a/compiler/rustc_attr_parsing/src/early_parsed.rs +++ b/compiler/rustc_attr_parsing/src/synthetic.rs @@ -1,45 +1,45 @@ -use rustc_ast::EarlyParsedAttribute; +use rustc_ast::SyntheticAttr; use rustc_ast::attr::data_structures::CfgEntry; use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; use rustc_span::Span; use thin_vec::ThinVec; -/// This struct contains the state necessary to convert early parsed attributes to hir attributes -/// The only conversion that really happens here is that multiple early parsed attributes are +/// This struct contains the state necessary to convert synthetic attributes to hir attributes +/// The only conversion that really happens here is that multiple synthetic attributes are /// merged into a single hir attribute, representing their combined state. /// FIXME: We should make this a nice and extendable system if this is going to be used more often #[derive(Default)] -pub(crate) struct EarlyParsedState { - /// Attribute state for `#[cfg]` trace attributes +pub(crate) struct SyntheticAttrState { + /// Attribute state for `SyntheticAttr::CfgTrace` attributes. cfg_trace: ThinVec<(CfgEntry, Span)>, - /// Attribute state for `#[cfg_attr]` trace attributes - /// The arguments of these attributes is no longer relevant for any later passes, only their presence. - /// So we discard the arguments here. + /// Attribute state for `SyntheticAttr::CfgAttrTrace` attributes. + /// The arguments of these attributes is no longer relevant for any later passes, only their + /// presence. So we discard the arguments here. cfg_attr_trace: bool, } -impl EarlyParsedState { - pub(crate) fn accept_early_parsed_attribute( +impl SyntheticAttrState { + pub(crate) fn accept_synthetic_attr( &mut self, attr_span: Span, lower_span: impl Copy + Fn(Span) -> Span, - parsed: &EarlyParsedAttribute, + synthetic: &SyntheticAttr, ) { - match parsed { - EarlyParsedAttribute::CfgTrace(cfg) => { + match synthetic { + SyntheticAttr::CfgTrace(cfg) => { let mut cfg = cfg.clone(); cfg.lower_spans(lower_span); self.cfg_trace.push((cfg, attr_span)); } - EarlyParsedAttribute::CfgAttrTrace => { + SyntheticAttr::CfgAttrTrace => { self.cfg_attr_trace = true; } } } - pub(crate) fn finalize_early_parsed_attributes(self, attributes: &mut Vec) { + pub(crate) fn finalize_synthetic_attrs(self, attributes: &mut Vec) { if !self.cfg_trace.is_empty() { attributes.push(Attribute::Parsed(AttributeKind::CfgTrace(self.cfg_trace))); } diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 0ed57766d9236..daec803a27a24 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -21,10 +21,10 @@ use rustc_span::{Span, Symbol, sym}; use crate::{AttributeParser, AttributeTemplate, session_diagnostics as errors, template}; pub fn check_attr(psess: &ParseSess, attr: &Attribute) { - use ast::EarlyParsedAttribute::*; + use ast::SyntheticAttr::*; match &attr.kind { AttrKind::Normal(_) => {} - AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => return, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => return, AttrKind::DocComment(..) => return, } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 457cd88e360c0..2bac291d6c0e7 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -7,8 +7,8 @@ use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, WithTokens, }; use rustc_ast::{ - self as ast, AttrStyle, Attribute, EarlyParsedAttribute, HasAttrs, HasTokens, MetaItem, - MetaItemInner, NodeId, + self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, + SyntheticAttr, }; use rustc_attr_parsing::parser::AllowExprMetavar; use rustc_attr_parsing::{ @@ -248,10 +248,10 @@ impl<'a> StripUnconfigured<'a> { /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec { - // A trace attribute left in AST in place of the original `cfg_attr` attribute. + // A synthetic trace attribute left in AST in place of the original `cfg_attr` attribute. // It can later be used by lints or other diagnostics. let mut trace_attr = cfg_attr.clone(); - trace_attr.convert_normal_to_parsed(EarlyParsedAttribute::CfgAttrTrace); + trace_attr.convert_normal_to_synthetic(SyntheticAttr::CfgAttrTrace); let Some((cfg_predicate, expanded_attrs)) = rustc_attr_parsing::parse_cfg_attr( cfg_attr, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index a8c9a4ecd1297..b829702d8719c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -8,9 +8,9 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrKind, AttrStyle, AttrVec, - DUMMY_NODE_ID, DelegationSource, DelegationSuffixes, EarlyParsedAttribute, ExprKind, - ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, - MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, + DUMMY_NODE_ID, DelegationSource, DelegationSuffixes, ExprKind, ForeignItemKind, HasAttrs, + HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, + PatKind, StmtKind, SyntheticAttr, TyKind, token, }; use rustc_ast_pretty::pprust; use rustc_attr_parsing::parser::AllowExprMetavar; @@ -2209,7 +2209,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Detect use of feature-gated or invalid attributes on macro invocations // since they will not be detected after macro expansion. fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { - use EarlyParsedAttribute::*; + use SyntheticAttr::*; let features = self.cx.ecfg.features; let mut attrs = attrs.iter().peekable(); let mut span: Option = None; @@ -2264,7 +2264,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ); } AttrKind::Normal(_) => {} - AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => {} + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => {} AttrKind::DocComment(..) => unreachable!(), // handled above } } @@ -2295,10 +2295,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let res = eval_config_entry(self.cfg().sess, &cfg); if res.as_bool() { - // A trace attribute left in AST in place of the original `cfg` attribute. + // A synthetic trace attribute left in AST in place of the original `cfg` attribute. // It can later be used by lints or other diagnostics. let mut trace_attr = attr; - trace_attr.convert_normal_to_parsed(EarlyParsedAttribute::CfgTrace(cfg)); + trace_attr.convert_normal_to_synthetic(SyntheticAttr::CfgTrace(cfg)); node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 9ac94dd17706e..5400c601ebf57 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -776,7 +776,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & AttrKind::DocComment(CommentKind::Block, _) => { BuiltinUnusedDocCommentSub::BlockHelp } - AttrKind::Parsed(..) => unreachable!(), + AttrKind::Synthetic(..) => unreachable!(), }; cx.emit_span_lint( UNUSED_DOC_COMMENTS, diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 3cbcb9b5c5e5a..49627a6c92c7c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -403,9 +403,9 @@ fn needs_tokens(attrs: &[ast::Attribute]) -> bool { Some(name) => name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(name), } } - // These attributes are created only during expansion, and can't re-enter the parser + // Synthetic attributes are created only during expansion, and can't re-enter the parser // because they have no token form. - AttrKind::Parsed(_) => unreachable!(), + AttrKind::Synthetic(_) => unreachable!(), AttrKind::DocComment(..) => false, }) } diff --git a/compiler/rustc_parse/src/parser/cfg_select.rs b/compiler/rustc_parse/src/parser/cfg_select.rs index 6d4121776d940..f5e82b96b41eb 100644 --- a/compiler/rustc_parse/src/parser/cfg_select.rs +++ b/compiler/rustc_parse/src/parser/cfg_select.rs @@ -57,7 +57,7 @@ impl<'a> Parser<'a> { for attr in attrs.take_for_recovery(self.psess) { match attr.kind { AttrKind::Normal(..) => spans.attrs.push(attr.span), - AttrKind::Parsed(..) => unreachable!(), + AttrKind::Synthetic(..) => unreachable!(), // `parse_outer_attributes` already emitted E0753 for inner doc comments before // recovering them as outer doc-comment attributes. AttrKind::DocComment(comment_kind, _) diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 622861ff2673a..87193b73a1a95 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -773,7 +773,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_attribute(&mut self, attr: &'v ast::Attribute) { record_variants!( (self, attr, attr.kind, None, ast, Attribute, AttrKind), - [Normal, Parsed, DocComment] + [Normal, Synthetic, DocComment] ); ast_visit::walk_attribute(self, attr) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 9cdc57d2f9c31..d319cd7ae9281 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -557,7 +557,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_attribute(&mut self, attr: &'a Attribute) { - use EarlyParsedAttribute::*; + use SyntheticAttr::*; let orig_in_attr = mem::replace(&mut self.invocation_parent.in_attr, true); match &attr.kind { AttrKind::Normal(normal) => { @@ -567,7 +567,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { .push((normal.item.path.segments[0].ident, self.parent_scope)); } } - AttrKind::Parsed(CfgTrace(_) | CfgAttrTrace) => {} + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => {} AttrKind::DocComment(..) => {} } visit::walk_attribute(self, attr); diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 6dc3ec13cf2c3..3e82ff4a69545 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -1,6 +1,6 @@ use super::MIXED_ATTRIBUTES_STYLE; use clippy_utils::diagnostics::span_lint; -use rustc_ast::{AttrKind, AttrStyle, Attribute, EarlyParsedAttribute}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, SyntheticAttr}; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{EarlyContext, LintContext}; use rustc_span::source_map::SourceMap; @@ -29,10 +29,10 @@ impl From<&AttrKind> for SimpleAttrKind { .collect::>(); Self::Normal(path_symbols) }, - AttrKind::Parsed(parsed) => { - match &**parsed { - EarlyParsedAttribute::CfgTrace(_) => Self::CfgTrace, - EarlyParsedAttribute::CfgAttrTrace => Self::CfgAttrTrace, + AttrKind::Synthetic(synthetic) => { + match &**synthetic { + SyntheticAttr::CfgTrace(_) => Self::CfgTrace, + SyntheticAttr::CfgAttrTrace => Self::CfgAttrTrace, } } AttrKind::DocComment(..) => Self::Doc, diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs index a769954696c8a..0a1ad883c2977 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::attr::data_structures::CfgEntry; -use rustc_ast::{AttrKind, EarlyParsedAttribute}; +use rustc_ast::{AttrKind, SyntheticAttr}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -34,8 +34,8 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]); impl EarlyLintPass for CfgNotTest { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) { - if let AttrKind::Parsed(parsed) = &attr.kind - && let EarlyParsedAttribute::CfgTrace(cfg) = &**parsed + if let AttrKind::Synthetic(synthetic) = &attr.kind + && let SyntheticAttr::CfgTrace(cfg) = &**synthetic && contains_not_test(cfg, false) { span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/empty_line_after.rs index 6eb5db1f7304a..834981a582832 100644 --- a/src/tools/clippy/clippy_lints/src/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/empty_line_after.rs @@ -251,7 +251,7 @@ impl Stop { Some(Self { span: attr.span, kind: match attr.kind { - AttrKind::Normal(_) | AttrKind::Parsed(_) => StopKind::Attr, + AttrKind::Normal(_) | AttrKind::Synthetic(_) => StopKind::Attr, AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind), }, first: file.lookup_line(file.relative_position(lo))?, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index ea9dfe32ab533..07c3d2e2f0ffe 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -1006,7 +1006,7 @@ fn eq_attr(l: &Attribute, r: &Attribute) -> bool { (Normal(l), Normal(r)) => { eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args) }, - (Parsed(..), _) | (_, Parsed(..)) => unreachable!(), + (Synthetic(..), _) | (_, Synthetic(..)) => unreachable!(), _ => false, } } @@ -1035,8 +1035,8 @@ pub fn is_cfg_test(item: &impl HasAttrs) -> bool { && item_list.iter().any(|item| item.has_name(sym::test)) { true - } else if let AttrKind::Parsed(parsed) = &attr.kind - && let EarlyParsedAttribute::CfgTrace(cfg) = &**parsed + } else if let AttrKind::Synthetic(synthetic) = &attr.kind + && let SyntheticAttr::CfgTrace(cfg) = &**synthetic { requires_test_cfg(cfg) } else { diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index b82240bf36d1e..412cdd41d0f2c 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -369,7 +369,7 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { (Pat::Str("#"), Pat::Str("]")) } }, - AttrKind::Parsed(..) => unreachable!(), + AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(_kind @ CommentKind::Line, ..) => { if attr.style == AttrStyle::Outer { (Pat::Str("///"), Pat::Str("")) diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index a886e1327170b..79d1d633e9774 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -43,7 +43,7 @@ ast-stats Param 160 (NN.N%) 4 40 ast-stats Block 144 (NN.N%) 6 24 ast-stats Attribute 128 (NN.N%) 4 32 ast-stats - DocComment 32 (NN.N%) 1 -ast-stats - Parsed 32 (NN.N%) 1 +ast-stats - Synthetic 32 (NN.N%) 1 ast-stats - Normal 64 (NN.N%) 2 ast-stats InlineAsm 120 (NN.N%) 1 120 ast-stats FnDecl 120 (NN.N%) 5 24 From fa56a01dadbe72a12a3d5f42ec313c98f694cb9b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 15 Jul 2026 09:41:21 +1000 Subject: [PATCH 7/7] Address review comments. --- compiler/rustc_ast/src/ast.rs | 8 ++++---- compiler/rustc_ast/src/attr/mod.rs | 14 ++++++-------- compiler/rustc_expand/src/config.rs | 3 +-- compiler/rustc_expand/src/expand.rs | 3 +-- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b5f9fffffc5db..867c77d129de0 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3458,10 +3458,10 @@ pub struct AttrItem { pub args: AttrArgs, } -/// Synthetic attributes are inserted by the compiler and cannot be written in source code. They -/// receive special treatment in various ways because they must not affect observable behaviour: -/// they are invisible to proc macros, cannot be pretty-printed, and are unable to re-enter the -/// parser. +/// Synthetic attributes are inserted by the compiler. They cannot be written in source code, and +/// so cannot be pretty-printed by the AST pretty printer (because its output should be valid Rust +/// code). They receive special treatment because they must not affect observable language +/// behaviour: they are invisible to proc macros and are unable to re-enter the parser. #[derive(Clone, Encodable, Decodable, Debug, StableHash)] pub enum SyntheticAttr { /// This synthetic attribute is added by the compiler when a `cfg` attribute is expanded so that diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 9f7ef74edf850..424280e3c9be6 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -66,10 +66,10 @@ impl Attribute { } } - pub fn convert_normal_to_synthetic(&mut self, synthetic_attr: SyntheticAttr) { + pub fn convert_normal_to_synthetic(self, synthetic_attr: SyntheticAttr) -> Attribute { match self.kind { AttrKind::Normal(..) => { - self.kind = AttrKind::Synthetic(Box::new(synthetic_attr)); + Attribute { kind: AttrKind::Synthetic(Box::new(synthetic_attr)), ..self } } AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), } @@ -142,8 +142,7 @@ impl AttributeExt for Attribute { .zip(name) .all(|(s, n)| s.args.is_none() && s.ident.name == *n) } - AttrKind::Synthetic(..) => false, - AttrKind::DocComment(..) => false, + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => false, } } @@ -169,8 +168,7 @@ impl AttributeExt for Attribute { fn meta_item_list(&self) -> Option> { match &self.kind { AttrKind::Normal(normal) => normal.item.meta_item_list(), - AttrKind::Synthetic(..) => None, - AttrKind::DocComment(..) => None, + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None, } } @@ -281,8 +279,7 @@ impl Attribute { pub fn meta(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.meta(self.span), - AttrKind::Synthetic(..) => None, - AttrKind::DocComment(..) => None, + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None, } } @@ -302,6 +299,7 @@ impl Attribute { .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}")) .to_attr_token_stream() .to_token_trees(), + // Empty tokens here ensures synthetic attributes are invisible to proc macros. AttrKind::Synthetic(..) => vec![], AttrKind::DocComment(comment_kind, data) => vec![TokenTree::token_alone( token::DocComment(comment_kind, self.style, data), diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 2bac291d6c0e7..030fa96a9fa68 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -250,8 +250,7 @@ impl<'a> StripUnconfigured<'a> { pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec { // A synthetic trace attribute left in AST in place of the original `cfg_attr` attribute. // It can later be used by lints or other diagnostics. - let mut trace_attr = cfg_attr.clone(); - trace_attr.convert_normal_to_synthetic(SyntheticAttr::CfgAttrTrace); + let trace_attr = cfg_attr.clone().convert_normal_to_synthetic(SyntheticAttr::CfgAttrTrace); let Some((cfg_predicate, expanded_attrs)) = rustc_attr_parsing::parse_cfg_attr( cfg_attr, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index b829702d8719c..832a991c31780 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2297,8 +2297,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { if res.as_bool() { // A synthetic trace attribute left in AST in place of the original `cfg` attribute. // It can later be used by lints or other diagnostics. - let mut trace_attr = attr; - trace_attr.convert_normal_to_synthetic(SyntheticAttr::CfgTrace(cfg)); + let trace_attr = attr.convert_normal_to_synthetic(SyntheticAttr::CfgTrace(cfg)); node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); }