From 76b59870e29347599d34f01c85335fa8898f14a8 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 16 Dec 2025 23:29:01 +0100 Subject: [PATCH 1/5] Wip --- compiler/rustc_ast/src/ast.rs | 40 +++++- .../rustc_ast/src/attr/data_structures.rs | 84 +++++++++++ compiler/rustc_ast/src/attr/mod.rs | 31 +++- .../src => rustc_ast/src/attr}/version.rs | 16 +-- compiler/rustc_ast/src/visit.rs | 2 + compiler/rustc_ast_lowering/src/lib.rs | 3 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 2 +- .../rustc_attr_parsing/src/attributes/cfg.rs | 26 ++-- .../src/attributes/cfg_trace.rs | 33 +++++ .../rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 49 +------ compiler/rustc_attr_parsing/src/interface.rs | 72 +++++++--- compiler/rustc_attr_parsing/src/parser.rs | 4 +- compiler/rustc_attr_parsing/src/safety.rs | 41 ++---- .../rustc_attr_parsing/src/target_checking.rs | 6 +- .../rustc_attr_parsing/src/validate_attr.rs | 4 +- compiler/rustc_builtin_macros/src/autodiff.rs | 14 +- .../src/deriving/generic/mod.rs | 41 +++--- compiler/rustc_expand/src/config.rs | 10 +- compiler/rustc_expand/src/expand.rs | 62 +++++--- .../rustc_hir/src/attrs/data_structures.rs | 85 +---------- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 + .../rustc_hir/src/attrs/pretty_printing.rs | 4 + compiler/rustc_hir/src/hir.rs | 1 + compiler/rustc_hir/src/lib.rs | 3 +- compiler/rustc_parse/src/parser/attr.rs | 4 +- compiler/rustc_passes/src/check_attr.rs | 6 +- compiler/rustc_resolve/src/def_collector.rs | 3 +- src/librustdoc/clean/cfg.rs | 129 +---------------- src/librustdoc/clean/cfg/tests.rs | 133 ------------------ src/librustdoc/clean/mod.rs | 14 +- src/librustdoc/passes/propagate_doc_cfg.rs | 6 +- .../clippy_lints/src/incompatible_msrv.rs | 9 +- .../clippy_lints/src/methods/is_empty.rs | 5 +- .../clippy_lints/src/new_without_default.rs | 11 +- src/tools/clippy/clippy_utils/src/lib.rs | 25 ++-- 36 files changed, 415 insertions(+), 566 deletions(-) create mode 100644 compiler/rustc_ast/src/attr/data_structures.rs rename compiler/{rustc_hir/src => rustc_ast/src/attr}/version.rs (75%) create mode 100644 compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7c922417ee29f..912e091b0dd82 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -34,6 +34,7 @@ use rustc_span::source_map::{Spanned, respan}; use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; +use crate::attr::data_structures::CfgEntry; pub use crate::format::*; use crate::token::{self, CommentKind, Delimiter}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; @@ -3390,7 +3391,7 @@ impl NormalAttr { item: AttrItem { unsafety: Safety::Default, path: Path::from_ident(ident), - args: AttrArgs::Empty, + args: AttrItemKind::Unparsed(AttrArgs::Empty), tokens: None, }, tokens: None, @@ -3402,11 +3403,46 @@ impl NormalAttr { pub struct AttrItem { pub unsafety: Safety, pub path: Path, - pub args: AttrArgs, + pub args: AttrItemKind, // Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`. pub tokens: Option, } +#[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, + } + } +} + +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +pub enum EarlyParsedAttribute { + CfgTrace(CfgEntry), + CfgAttrTrace, +} + impl AttrItem { pub fn is_valid_for_outer_style(&self) -> bool { self.path == sym::cfg_attr diff --git a/compiler/rustc_ast/src/attr/data_structures.rs b/compiler/rustc_ast/src/attr/data_structures.rs new file mode 100644 index 0000000000000..d2c5af5f9f8c6 --- /dev/null +++ b/compiler/rustc_ast/src/attr/data_structures.rs @@ -0,0 +1,84 @@ +use std::fmt; + +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_span::{Span, Symbol}; +use thin_vec::ThinVec; + +use crate::attr::version::RustcVersion; + +#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum CfgEntry { + All(ThinVec, Span), + Any(ThinVec, Span), + Not(Box, Span), + Bool(bool, Span), + NameValue { name: Symbol, value: Option, span: Span }, + Version(Option, Span), +} + +impl CfgEntry { + pub fn span(&self) -> Span { + let (Self::All(_, span) + | Self::Any(_, span) + | Self::Not(_, span) + | Self::Bool(_, span) + | Self::NameValue { span, .. } + | Self::Version(_, span)) = self; + *span + } + + /// Same as `PartialEq` but doesn't check spans and ignore order of cfgs. + pub fn is_equivalent_to(&self, other: &Self) -> bool { + match (self, other) { + (Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => { + a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b))) + } + (Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b), + (Self::Bool(a, _), Self::Bool(b, _)) => a == b, + ( + Self::NameValue { name: name1, value: value1, .. }, + Self::NameValue { name: name2, value: value2, .. }, + ) => name1 == name2 && value1 == value2, + (Self::Version(a, _), Self::Version(b, _)) => a == b, + _ => false, + } + } +} + +impl fmt::Display for CfgEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn write_entries( + name: &str, + entries: &[CfgEntry], + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(f, "{name}(")?; + for (nb, entry) in entries.iter().enumerate() { + if nb != 0 { + f.write_str(", ")?; + } + entry.fmt(f)?; + } + f.write_str(")") + } + match self { + Self::All(entries, _) => write_entries("all", entries, f), + Self::Any(entries, _) => write_entries("any", entries, f), + Self::Not(entry, _) => write!(f, "not({entry})"), + Self::Bool(value, _) => write!(f, "{value}"), + Self::NameValue { name, value, .. } => { + match value { + // We use `as_str` and debug display to have characters escaped and `"` + // characters surrounding the string. + Some(value) => write!(f, "{name} = {:?}", value.as_str()), + None => write!(f, "{name}"), + } + } + Self::Version(version, _) => match version { + Some(version) => write!(f, "{version}"), + None => Ok(()), + }, + } + } +} diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index c53188a22aedd..0cebf093144d8 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,5 +1,8 @@ //! Functions dealing with attributes and meta items. +pub mod data_structures; +pub mod version; + use std::fmt::Debug; use std::sync::atomic::{AtomicU32, Ordering}; @@ -8,6 +11,7 @@ use rustc_span::{Ident, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::{ThinVec, thin_vec}; +use crate::AttrItemKind; use crate::ast::{ AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path, @@ -62,6 +66,13 @@ impl Attribute { } } + pub fn get_mut_normal_item(&mut self) -> &mut AttrItem { + match &mut self.kind { + AttrKind::Normal(normal) => &mut normal.item, + AttrKind::DocComment(..) => panic!("unexpected doc comment"), + } + } + pub fn unwrap_normal_item(self) -> AttrItem { match self.kind { AttrKind::Normal(normal) => normal.item, @@ -77,7 +88,7 @@ impl AttributeExt for Attribute { fn value_span(&self) -> Option { match &self.kind { - AttrKind::Normal(normal) => match &normal.item.args { + AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? { AttrArgs::Eq { expr, .. } => Some(expr.span), _ => None, }, @@ -138,7 +149,7 @@ impl AttributeExt for Attribute { fn is_word(&self) -> bool { if let AttrKind::Normal(normal) = &self.kind { - matches!(normal.item.args, AttrArgs::Empty) + matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty)) } else { false } @@ -294,7 +305,7 @@ impl AttrItem { } pub fn meta_item_list(&self) -> Option> { - match &self.args { + match &self.args.unparsed_ref()? { AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { MetaItemKind::list_from_tokens(args.tokens.clone()) } @@ -315,7 +326,7 @@ impl AttrItem { /// #[attr("value")] /// ``` fn value_str(&self) -> Option { - match &self.args { + match &self.args.unparsed_ref()? { AttrArgs::Eq { expr, .. } => match expr.kind { ExprKind::Lit(token_lit) => { LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str()) @@ -339,7 +350,7 @@ impl AttrItem { /// #[attr("value")] /// ``` fn value_span(&self) -> Option { - match &self.args { + match &self.args.unparsed_ref()? { AttrArgs::Eq { expr, .. } => Some(expr.span), AttrArgs::Delimited(_) | AttrArgs::Empty => None, } @@ -355,7 +366,7 @@ impl AttrItem { } pub fn meta_kind(&self) -> Option { - MetaItemKind::from_attr_args(&self.args) + MetaItemKind::from_attr_args(self.args.unparsed_ref()?) } } @@ -690,7 +701,13 @@ fn mk_attr( args: AttrArgs, span: Span, ) -> Attribute { - mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span) + mk_attr_from_item( + g, + AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None }, + None, + style, + span, + ) } pub fn mk_attr_from_item( diff --git a/compiler/rustc_hir/src/version.rs b/compiler/rustc_ast/src/attr/version.rs similarity index 75% rename from compiler/rustc_hir/src/version.rs rename to compiler/rustc_ast/src/attr/version.rs index 03182088d4c05..59deee40ae28d 100644 --- a/compiler/rustc_hir/src/version.rs +++ b/compiler/rustc_ast/src/attr/version.rs @@ -1,16 +1,10 @@ -use std::borrow::Cow; use std::fmt::{self, Display}; use std::sync::OnceLock; -use rustc_error_messages::{DiagArgValue, IntoDiagArg}; -use rustc_macros::{ - BlobDecodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version, -}; - -use crate::attrs::PrintAttribute; +use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic, current_rustc_version}; #[derive(Encodable, BlobDecodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[derive(HashStable_Generic, PrintAttribute)] +#[derive(HashStable_Generic)] pub struct RustcVersion { pub major: u16, pub minor: u16, @@ -47,9 +41,3 @@ impl Display for RustcVersion { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) } } - -impl IntoDiagArg for RustcVersion { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(self.to_string())) - } -} diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d122adfdf9661..83b751fbde902 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -366,6 +366,7 @@ macro_rules! common_visitor_and_walkers { crate::token::LitKind, crate::tokenstream::LazyAttrTokenStream, crate::tokenstream::TokenStream, + EarlyParsedAttribute, Movability, Mutability, Pinnedness, @@ -457,6 +458,7 @@ macro_rules! common_visitor_and_walkers { ModSpans, MutTy, NormalAttr, + AttrItemKind, Parens, ParenthesizedArgs, PatFieldsRest, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 1bc8d7c25bb51..2a0952a4c7c87 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -40,7 +40,7 @@ use std::sync::Arc; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, *}; -use rustc_attr_parsing::{AttributeParser, Late, OmitDoc}; +use rustc_attr_parsing::{AttributeParser, Late, OmitDoc, ShouldEmit}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -209,6 +209,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { tcx.features(), registered_tools, Late, + ShouldEmit::ErrorsAndLints, ), delayed_lints: Vec::new(), } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 1aa08dfd3d5ee..b6e2405ec9517 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -694,7 +694,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } ast::Safety::Default | ast::Safety::Safe(_) => {} } - match &item.args { + match &item.args.unparsed_ref().unwrap() { 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 798cc10765415..631ade1b713c7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -103,7 +103,7 @@ fn parse_cfg_entry_version( list: &MetaItemListParser, meta_span: Span, ) -> Result { - try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option()); + try_gate_cfg(sym::version, meta_span, cx); let Some(version) = list.single() else { return Err( cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span }) @@ -135,7 +135,8 @@ fn parse_cfg_entry_target( list: &MetaItemListParser, meta_span: Span, ) -> Result { - if let Some(features) = cx.features_option() + if let ShouldEmit::ErrorsAndLints = cx.should_emit + && let Some(features) = cx.features_option() && !features.cfg_target_compact() { feature_err( @@ -180,7 +181,7 @@ pub(crate) fn parse_name_value( span: Span, cx: &mut AcceptContext<'_, '_, S>, ) -> Result { - try_gate_cfg(name, span, cx.sess(), cx.features_option()); + try_gate_cfg(name, span, cx); let value = match value { None => None, @@ -294,11 +295,9 @@ pub fn parse_cfg_attr( sess: &Session, features: Option<&Features>, ) -> Option<(CfgEntry, Vec<(AttrItem, Span)>)> { - match cfg_attr.get_normal_item().args { - ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens }) - if !tokens.is_empty() => - { - check_cfg_attr_bad_delim(&sess.psess, dspan, delim); + match cfg_attr.get_normal_item().args.unparsed_ref().unwrap() { + ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => { + check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim); match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| { parse_cfg_attr_internal(p, sess, features, cfg_attr) }) { @@ -322,7 +321,7 @@ pub fn parse_cfg_attr( } _ => { let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = - cfg_attr.get_normal_item().args + cfg_attr.get_normal_item().args.unparsed_ref()? { (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) } else { @@ -413,10 +412,13 @@ fn parse_cfg_attr_internal<'a>( Ok((cfg_predicate, expanded_attrs)) } -fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { +fn try_gate_cfg(name: Symbol, span: Span, cx: &mut AcceptContext<'_, '_, S>) { + if let ShouldEmit::Nothing = cx.should_emit { + return; + } let gate = find_gated_cfg(|sym| sym == name); - if let (Some(feats), Some(gated_cfg)) = (features, gate) { - gate_cfg(gated_cfg, span, sess, feats); + if let (Some(feats), Some(gated_cfg)) = (cx.features, gate) { + gate_cfg(gated_cfg, span, cx.sess, feats); } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs new file mode 100644 index 0000000000000..7c4f0916e6d69 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs @@ -0,0 +1,33 @@ +use rustc_feature::AttributeTemplate; +use rustc_hir::attrs::{AttributeKind, CfgEntry}; +use rustc_span::{Span, Symbol, sym}; + +use crate::attributes::{CombineAttributeParser, ConvertFn}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; +use crate::target_checking::{ALL_TARGETS, AllowedTargets}; +use crate::{CFG_TEMPLATE, parse_cfg_entry}; + +pub(crate) struct CfgTraceParser; + +impl CombineAttributeParser for CfgTraceParser { + const PATH: &[Symbol] = &[sym::cfg_trace]; + type Item = (CfgEntry, Span); + const CONVERT: ConvertFn = |c, _| AttributeKind::CfgTrace(c); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const TEMPLATE: AttributeTemplate = CFG_TEMPLATE; + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let Some(list) = args.list() else { + return None; + }; + let Some(entry) = list.single() else { + return None; + }; + + Some((parse_cfg_entry(cx, entry).ok()?, cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index c39c60ea7e391..2ea53205d401b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -33,6 +33,7 @@ pub(crate) mod allow_unstable; pub(crate) mod body; pub(crate) mod cfg; pub(crate) mod cfg_select; +pub(crate) mod cfg_trace; pub(crate) mod cfi_encoding; pub(crate) mod codegen_attrs; pub(crate) mod confusables; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 9a443dfbb842a..aecdc5f2b9ebd 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -5,12 +5,11 @@ use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId}; -use rustc_errors::{Diag, Diagnostic, Level}; +use rustc_errors::{Diag, Level}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; -use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; @@ -19,6 +18,7 @@ use crate::attributes::allow_unstable::{ AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, }; use crate::attributes::body::CoroutineParser; +use crate::attributes::cfg_trace::CfgTraceParser; use crate::attributes::cfi_encoding::CfiEncodingParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser, @@ -179,6 +179,7 @@ attribute_parsers!( // tidy-alphabetical-start Combine, Combine, + Combine, Combine, Combine, Combine, @@ -292,14 +293,6 @@ pub trait Stage: Sized + 'static + Sealed { fn parsers() -> &'static GroupType; - fn emit_err<'sess>( - &self, - sess: &'sess Session, - diag: impl for<'x> Diagnostic<'x>, - ) -> ErrorGuaranteed; - - fn should_emit(&self) -> ShouldEmit; - fn id_is_crate_root(id: Self::Id) -> bool; } @@ -311,17 +304,6 @@ impl Stage for Early { fn parsers() -> &'static GroupType { &early::ATTRIBUTE_PARSERS } - fn emit_err<'sess>( - &self, - sess: &'sess Session, - diag: impl for<'x> Diagnostic<'x>, - ) -> ErrorGuaranteed { - self.should_emit().emit_err(sess.dcx().create_err(diag)) - } - - fn should_emit(&self) -> ShouldEmit { - self.emit_errors - } fn id_is_crate_root(id: Self::Id) -> bool { id == CRATE_NODE_ID @@ -336,17 +318,6 @@ impl Stage for Late { fn parsers() -> &'static GroupType { &late::ATTRIBUTE_PARSERS } - fn emit_err<'sess>( - &self, - tcx: &'sess Session, - diag: impl for<'x> Diagnostic<'x>, - ) -> ErrorGuaranteed { - tcx.dcx().emit_err(diag) - } - - fn should_emit(&self) -> ShouldEmit { - ShouldEmit::ErrorsAndLints - } fn id_is_crate_root(id: Self::Id) -> bool { id == CRATE_HIR_ID @@ -354,12 +325,8 @@ impl Stage for Late { } /// used when parsing attributes for miscellaneous things *before* ast lowering -pub struct Early { - /// Whether to emit errors or delay them as a bug - /// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed - /// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted - pub emit_errors: ShouldEmit, -} +pub struct Early; + /// used when parsing attributes during ast lowering pub struct Late; @@ -396,16 +363,12 @@ pub struct AcceptContext<'f, 'sess, S: Stage> { } impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { - pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { - self.stage.emit_err(&self.sess, diag) - } - /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing /// must be delayed until after HIR is built. This method will take care of the details of /// that. pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) { if !matches!( - self.stage.should_emit(), + self.should_emit, ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true } ) { return; diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 91596ff0de600..01590d88e2720 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -2,15 +2,15 @@ use std::convert::identity; use rustc_ast as ast; use rustc_ast::token::DocFragmentKind; -use rustc_ast::{AttrStyle, NodeId, Safety}; -use rustc_errors::DiagCtxtHandle; +use rustc_ast::{AttrItemKind, AttrStyle, EarlyParsedAttribute, NodeId, Safety}; +use rustc_errors::{DiagCtxtHandle, Diagnostic}; use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLint; use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target}; use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; -use rustc_span::{DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage}; use crate::parser::{ArgParser, PathParser, RefPathParser}; @@ -23,7 +23,8 @@ pub struct AttributeParser<'sess, S: Stage = Late> { pub(crate) tools: Vec, pub(crate) features: Option<&'sess Features>, pub(crate) sess: &'sess Session, - pub(crate) stage: S, + pub(crate) _stage: S, + pub(crate) should_emit: ShouldEmit, /// *Only* parse attributes with this symbol. /// @@ -105,10 +106,10 @@ impl<'sess> AttributeParser<'sess, Early> { target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, - emit_errors: ShouldEmit, + should_emit: ShouldEmit, ) -> Vec { let mut p = - Self { features, tools: Vec::new(), parse_only, sess, stage: Early { emit_errors } }; + Self { features, tools: Vec::new(), parse_only, sess, _stage: Early, should_emit }; p.parse_attribute_list( attrs, target_span, @@ -146,8 +147,12 @@ impl<'sess> AttributeParser<'sess, Early> { normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); let path = AttrPath::from_ast(&normal_attr.item.path, identity); - let args = - ArgParser::from_attr_args(&normal_attr.item.args, &parts, &sess.psess, emit_errors)?; + let args = ArgParser::from_attr_args( + &normal_attr.item.args.unparsed_ref().unwrap(), + &parts, + &sess.psess, + emit_errors, + )?; Self::parse_single_args( sess, attr.span, @@ -179,7 +184,7 @@ impl<'sess> AttributeParser<'sess, Early> { target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, - emit_errors: ShouldEmit, + should_emit: ShouldEmit, args: &I, parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &I) -> T, template: &AttributeTemplate, @@ -189,7 +194,8 @@ impl<'sess> AttributeParser<'sess, Early> { tools: Vec::new(), parse_only: None, sess, - stage: Early { emit_errors }, + _stage: Early, + should_emit, }; let mut emit_lint = |lint: AttributeLint| { sess.psess.buffer_lint( @@ -232,8 +238,9 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { features: &'sess Features, tools: Vec, stage: S, + should_emit: ShouldEmit, ) -> Self { - Self { features: Some(features), tools, parse_only: None, sess, stage } + Self { features: Some(features), tools, parse_only: None, sess, _stage: stage, should_emit } } pub(crate) fn sess(&self) -> &'sess Session { @@ -252,6 +259,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { self.sess().dcx() } + pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + self.should_emit.emit_err(self.dcx().create_err(diag)) + } + /// Parse a list of attributes. /// /// `target_span` is the span of the thing this list of attributes is applied to, @@ -263,14 +274,16 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target_id: S::Id, target: Target, omit_doc: OmitDoc, - lower_span: impl Copy + Fn(Span) -> Span, mut emit_lint: impl FnMut(AttributeLint), ) -> Vec { let mut attributes = Vec::new(); let mut attr_paths: Vec> = Vec::new(); + let old_should_emit = self.should_emit; for attr in attrs { + self.should_emit = old_should_emit; //FIXME ugly solution + // If we're only looking for a single attribute, skip all the ones we don't care about. if let Some(expected) = self.parse_only { if !attr.has_name(expected) { @@ -305,6 +318,28 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { 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) => { + match parsed { + EarlyParsedAttribute::CfgTrace(cfg) => { + attributes.push(Attribute::Parsed(AttributeKind::CfgTrace( + [(cfg.clone(), n.item.span())].into(), + ))); + } + EarlyParsedAttribute::CfgAttrTrace => { + attributes.push(Attribute::Parsed(AttributeKind::CfgAttrTrace)); + } + } + continue; + } + }; + + // Don't emit anything for trace attributes + if attr.has_any_name(&[sym::cfg_trace, sym::cfg_attr_trace]) { + self.should_emit = ShouldEmit::Nothing; + } + self.check_attribute_safety( &attr_path, lower_span(n.item.span()), @@ -318,10 +353,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) { let Some(args) = ArgParser::from_attr_args( - &n.item.args, + args, &parts, &self.sess.psess, - self.stage.should_emit(), + self.should_emit, ) else { continue; }; @@ -374,9 +409,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { }; (accept.accept_fn)(&mut cx, &args); - if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { - Self::check_target(&accept.allowed_targets, target, &mut cx); - } + Self::check_target(&accept.allowed_targets, target, &mut cx); } } else { // If we're here, we must be compiling a tool attribute... Or someone @@ -396,7 +429,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { attributes.push(Attribute::Unparsed(Box::new(AttrItem { path: attr_path.clone(), - args: self.lower_attr_args(&n.item.args, lower_span), + args: self + .lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span), id: HashIgnoredAttrId { attr_id: attr.id }, style: attr.style, span: lower_span(attr.span), @@ -428,7 +462,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { /// Returns whether there is a parser for an attribute with this name pub fn is_parsed_attribute(path: &[Symbol]) -> bool { - Late::parsers().accepters.contains_key(path) + Late::parsers().accepters.contains_key(path) || path == &[sym::cfg_attr_trace] // FIXME } fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 9551744d5ec53..faecde1227198 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -120,10 +120,10 @@ impl ArgParser { } if args.delim != Delimiter::Parenthesis { - psess.dcx().emit_err(MetaBadDelim { + should_emit.emit_err(psess.dcx().create_err(MetaBadDelim { span: args.dspan.entire(), sugg: MetaBadDelimSugg { open: args.dspan.open, close: args.dspan.close }, - }); + })); return None; } diff --git a/compiler/rustc_attr_parsing/src/safety.rs b/compiler/rustc_attr_parsing/src/safety.rs index 817785108a1ed..6199bd4234806 100644 --- a/compiler/rustc_attr_parsing/src/safety.rs +++ b/compiler/rustc_attr_parsing/src/safety.rs @@ -4,7 +4,7 @@ use rustc_hir::AttrPath; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_session::lint::LintId; use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE; -use rustc_span::{Span, sym}; +use rustc_span::Span; use crate::context::Stage; use crate::{AttributeParser, ShouldEmit}; @@ -18,16 +18,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { emit_lint: &mut impl FnMut(AttributeLint), target_id: S::Id, ) { - if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { + if matches!(self.should_emit, ShouldEmit::Nothing) { return; } let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0].name); - if let Some(name) = name - && [sym::cfg_trace, sym::cfg_attr_trace].contains(&name) - { - return; - } // FIXME: We should retrieve this information from the attribute parsers instead of from `BUILTIN_ATTRIBUTE_MAP` let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)); @@ -74,18 +69,15 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } if emit_error { - self.stage.emit_err( - self.sess, - crate::session_diagnostics::UnsafeAttrOutsideUnsafe { - span: path_span, - suggestion: not_from_proc_macro.then(|| { - crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { - left: diag_span.shrink_to_lo(), - right: diag_span.shrink_to_hi(), - } - }), - }, - ); + self.emit_err(crate::session_diagnostics::UnsafeAttrOutsideUnsafe { + span: path_span, + suggestion: not_from_proc_macro.then(|| { + crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { + left: diag_span.shrink_to_lo(), + right: diag_span.shrink_to_hi(), + } + }), + }); } else { emit_lint(AttributeLint { lint_id: LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE), @@ -103,13 +95,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { // - Normal builtin attribute // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes (None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => { - self.stage.emit_err( - self.sess, - crate::session_diagnostics::InvalidAttrUnsafe { - span: unsafe_span, - name: attr_path.clone(), - }, - ); + self.emit_err(crate::session_diagnostics::InvalidAttrUnsafe { + span: unsafe_span, + name: attr_path.clone(), + }); } // - Normal builtin attribute diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 88efb910c1601..e251326f5b15c 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -7,9 +7,9 @@ use rustc_hir::lints::AttributeLintKind; use rustc_hir::{MethodKind, Target}; use rustc_span::sym; -use crate::AttributeParser; use crate::context::{AcceptContext, Stage}; use crate::session_diagnostics::InvalidTarget; +use crate::{AttributeParser, ShouldEmit}; #[derive(Debug)] pub(crate) enum AllowedTargets { @@ -95,6 +95,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { + if matches!(cx.should_emit, ShouldEmit::Nothing) { + return; + } + Self::check_type(matches!(allowed_targets, AllowedTargets::CrateLevel), target, cx); match allowed_targets.is_allowed(target) { diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index e69ed0eea6b01..276fa5bedfd15 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -48,7 +48,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { } _ => { let attr_item = attr.get_normal_item(); - if let AttrArgs::Eq { .. } = attr_item.args { + if let AttrArgs::Eq { .. } = attr_item.args.unparsed_ref().unwrap() { // All key-value attributes are restricted to meta-item syntax. match parse_meta(psess, attr) { Ok(_) => {} @@ -67,7 +67,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met unsafety: item.unsafety, span: attr.span, path: item.path.clone(), - kind: match &item.args { + kind: match &item.args.unparsed_ref().unwrap() { AttrArgs::Empty => MetaItemKind::Word, AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => { check_meta_bad_delim(psess, *dspan, *delim); diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 39abb66df30c6..95191b82ff3ff 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -358,7 +358,7 @@ mod llvm_enzyme { let inline_item = ast::AttrItem { unsafety: ast::Safety::Default, path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)), - args: ast::AttrArgs::Delimited(never_arg), + args: rustc_ast::ast::AttrItemKind::Unparsed(ast::AttrArgs::Delimited(never_arg)), tokens: None, }; let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None }); @@ -421,11 +421,13 @@ mod llvm_enzyme { } }; // Now update for d_fn - rustc_ad_attr.item.args = rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs { - dspan: DelimSpan::dummy(), - delim: rustc_ast::token::Delimiter::Parenthesis, - tokens: ts, - }); + rustc_ad_attr.item.args = rustc_ast::ast::AttrItemKind::Unparsed( + rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs { + dspan: DelimSpan::dummy(), + delim: rustc_ast::token::Delimiter::Parenthesis, + tokens: ts, + }), + ); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); let d_attr = outer_normal_attr(&rustc_ad_attr, new_id, span); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index bb6557802ece8..7c84530abbee2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -807,24 +807,29 @@ impl<'a> TraitDef<'a> { rustc_ast::AttrItem { unsafety: Safety::Default, path: rustc_const_unstable, - args: AttrArgs::Delimited(DelimArgs { - dspan: DelimSpan::from_single(self.span), - delim: rustc_ast::token::Delimiter::Parenthesis, - tokens: [ - TokenKind::Ident(sym::feature, IdentIsRaw::No), - TokenKind::Eq, - TokenKind::lit(LitKind::Str, sym::derive_const, None), - TokenKind::Comma, - TokenKind::Ident(sym::issue, IdentIsRaw::No), - TokenKind::Eq, - TokenKind::lit(LitKind::Str, sym::derive_const_issue, None), - ] - .into_iter() - .map(|kind| { - TokenTree::Token(Token { kind, span: self.span }, Spacing::Alone) - }) - .collect(), - }), + args: rustc_ast::ast::AttrItemKind::Unparsed(AttrArgs::Delimited( + DelimArgs { + dspan: DelimSpan::from_single(self.span), + delim: rustc_ast::token::Delimiter::Parenthesis, + tokens: [ + TokenKind::Ident(sym::feature, IdentIsRaw::No), + TokenKind::Eq, + TokenKind::lit(LitKind::Str, sym::derive_const, None), + TokenKind::Comma, + TokenKind::Ident(sym::issue, IdentIsRaw::No), + TokenKind::Eq, + TokenKind::lit(LitKind::Str, sym::derive_const_issue, None), + ] + .into_iter() + .map(|kind| { + TokenTree::Token( + Token { kind, span: self.span }, + Spacing::Alone, + ) + }) + .collect(), + }, + )), tokens: None, }, self.span, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 492c845df1710..a0434066c310c 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, }; use rustc_ast::{ - self as ast, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, - NodeId, NormalAttr, + self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, EarlyParsedAttribute, HasAttrs, + HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr, }; use rustc_attr_parsing as attr; use rustc_attr_parsing::{ @@ -288,7 +288,11 @@ impl<'a> StripUnconfigured<'a> { 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. // It can later be used by lints or other diagnostics. - let trace_attr = attr_into_trace(cfg_attr.clone(), sym::cfg_attr_trace); + // Remove the arguments, because they won't be used later and may trip up the attribute parser + let mut trace_attr = cfg_attr.clone(); + trace_attr.get_mut_normal_item().args = + AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace); + let trace_attr = attr_into_trace(trace_attr, sym::cfg_attr_trace); let Some((cfg_predicate, expanded_attrs)) = rustc_attr_parsing::parse_cfg_attr(cfg_attr, &self.sess, self.features) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 6422779e13c9b..45c0f4c37b9af 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -7,12 +7,16 @@ use rustc_ast::mut_visit::*; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ - self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, DUMMY_NODE_ID, - ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, - MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, + self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrItemKind, AttrStyle, AttrVec, + DUMMY_NODE_ID, EarlyParsedAttribute, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, + ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, PatKind, StmtKind, + TyKind, token, }; use rustc_ast_pretty::pprust; -use rustc_attr_parsing::{AttributeParser, Early, EvalConfigResult, ShouldEmit, validate_attr}; +use rustc_attr_parsing::{ + AttributeParser, CFG_TEMPLATE, Early, EvalConfigResult, ShouldEmit, eval_config_entry, + parse_cfg, validate_attr, +}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::PResult; @@ -813,10 +817,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; let attr_item = attr.get_normal_item(); let safety = attr_item.unsafety; - if let AttrArgs::Eq { .. } = attr_item.args { + if let AttrArgs::Eq { .. } = attr_item.args.unparsed_ref().unwrap() { self.cx.dcx().emit_err(UnsupportedKeyValue { span }); } - let inner_tokens = attr_item.args.inner_tokens(); + let inner_tokens = attr_item.args.unparsed_ref().unwrap().inner_tokens(); match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) { Ok(tok_result) => { let fragment = self.parse_ast_fragment( @@ -2188,21 +2192,17 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { && !AttributeParser::::is_parsed_attribute(&attr.path()) { let attr_name = attr.ident().unwrap().name; - // `#[cfg]` and `#[cfg_attr]` are special - they are - // eagerly evaluated. - if attr_name != sym::cfg_trace && attr_name != sym::cfg_attr_trace { - self.cx.sess.psess.buffer_lint( - UNUSED_ATTRIBUTES, - attr.span, - self.cx.current_expansion.lint_node_id, - crate::errors::UnusedBuiltinAttribute { - attr_name, - macro_name: pprust::path_to_string(&call.path), - invoc_span: call.path.span, - attr_span: attr.span, - }, - ); - } + self.cx.sess.psess.buffer_lint( + UNUSED_ATTRIBUTES, + attr.span, + self.cx.current_expansion.lint_node_id, + crate::errors::UnusedBuiltinAttribute { + attr_name, + macro_name: pprust::path_to_string(&call.path), + invoc_span: call.path.span, + attr_span: attr.span, + }, + ); } } } @@ -2213,11 +2213,27 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { attr: ast::Attribute, pos: usize, ) -> EvalConfigResult { - let res = self.cfg().cfg_true(&attr, ShouldEmit::ErrorsAndLints); + let Some(cfg) = AttributeParser::parse_single( + self.cfg().sess, + &attr, + attr.span, + self.cfg().lint_node_id, + self.cfg().features, + ShouldEmit::ErrorsAndLints, + parse_cfg, + &CFG_TEMPLATE, + ) else { + // Cfg attribute was not parsable, give up + return EvalConfigResult::True; + }; + + 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. // It can later be used by lints or other diagnostics. - let trace_attr = attr_into_trace(attr, sym::cfg_trace); + let mut trace_attr = attr_into_trace(attr, sym::cfg_trace); + trace_attr.get_mut_normal_item().args = + AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg)); node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 39530f5c3f8b0..ef07daa64b34a 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; -use std::fmt; use std::path::PathBuf; pub use ReprAttr::*; use rustc_abi::Align; +pub use rustc_ast::attr::data_structures::*; use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrStyle, ast}; use rustc_data_structures::fx::FxIndexMap; @@ -202,83 +202,6 @@ impl StrippedCfgItem { } } -#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic, PrintAttribute)] -pub enum CfgEntry { - All(ThinVec, Span), - Any(ThinVec, Span), - Not(Box, Span), - Bool(bool, Span), - NameValue { name: Symbol, value: Option, span: Span }, - Version(Option, Span), -} - -impl CfgEntry { - pub fn span(&self) -> Span { - let (Self::All(_, span) - | Self::Any(_, span) - | Self::Not(_, span) - | Self::Bool(_, span) - | Self::NameValue { span, .. } - | Self::Version(_, span)) = self; - *span - } - - /// Same as `PartialEq` but doesn't check spans and ignore order of cfgs. - pub fn is_equivalent_to(&self, other: &Self) -> bool { - match (self, other) { - (Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => { - a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b))) - } - (Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b), - (Self::Bool(a, _), Self::Bool(b, _)) => a == b, - ( - Self::NameValue { name: name1, value: value1, .. }, - Self::NameValue { name: name2, value: value2, .. }, - ) => name1 == name2 && value1 == value2, - (Self::Version(a, _), Self::Version(b, _)) => a == b, - _ => false, - } - } -} - -impl fmt::Display for CfgEntry { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn write_entries( - name: &str, - entries: &[CfgEntry], - f: &mut fmt::Formatter<'_>, - ) -> fmt::Result { - write!(f, "{name}(")?; - for (nb, entry) in entries.iter().enumerate() { - if nb != 0 { - f.write_str(", ")?; - } - entry.fmt(f)?; - } - f.write_str(")") - } - match self { - Self::All(entries, _) => write_entries("all", entries, f), - Self::Any(entries, _) => write_entries("any", entries, f), - Self::Not(entry, _) => write!(f, "not({entry})"), - Self::Bool(value, _) => write!(f, "{value}"), - Self::NameValue { name, value, .. } => { - match value { - // We use `as_str` and debug display to have characters escaped and `"` - // characters surrounding the string. - Some(value) => write!(f, "{name} = {:?}", value.as_str()), - None => write!(f, "{name}"), - } - } - Self::Version(version, _) => match version { - Some(version) => write!(f, "{version}"), - None => Ok(()), - }, - } - } -} - /// Possible values for the `#[linkage]` attribute, allowing to specify the /// linkage type for a `MonoItem`. /// @@ -703,6 +626,12 @@ pub enum AttributeKind { span: Span, }, + /// Represents the trace attribute of `#[cfg_attr]` + CfgAttrTrace, + + /// Represents the trace attribute of `#[cfg]` + CfgTrace(ThinVec<(CfgEntry, Span)>), + /// Represents `#[cfi_encoding]` CfiEncoding { encoding: Symbol }, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 6d159a6ee68a7..beace1c796ff6 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -26,6 +26,8 @@ impl AttributeKind { AsPtr(..) => Yes, AutomaticallyDerived(..) => Yes, BodyStability { .. } => No, + CfgAttrTrace => Yes, + CfgTrace(..) => Yes, CfiEncoding { .. } => Yes, Coinductive(..) => No, Cold(..) => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index f8ac2a547ca8f..806f5c4d3ed91 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -2,6 +2,8 @@ use std::num::NonZero; use std::ops::Deref; use rustc_abi::Align; +use rustc_ast::attr::data_structures::CfgEntry; +use rustc_ast::attr::version::RustcVersion; use rustc_ast::token::{CommentKind, DocFragmentKind}; use rustc_ast::{AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; @@ -182,4 +184,6 @@ print_debug!( Transparency, SanitizerSet, DefId, + RustcVersion, + CfgEntry, ); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index c60471848c892..28092b65fae6c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1350,6 +1350,7 @@ impl AttributeExt for Attribute { // FIXME: should not be needed anymore when all attrs are parsed Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span, Attribute::Parsed(AttributeKind::Deprecation { span, .. }) => *span, + Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) => cfgs[0].1, a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"), } } diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 7c9c15c16df46..fa46c0f085a27 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -33,7 +33,6 @@ pub mod pat_util; mod stability; mod stable_hash_impls; mod target; -mod version; pub mod weak_lang_items; #[cfg(test)] @@ -42,9 +41,9 @@ mod tests; #[doc(no_inline)] pub use hir::*; pub use lang_items::{LangItem, LanguageItems}; +pub use rustc_ast::attr::version::*; pub use stability::*; pub use stable_hash_impls::HashStableContext; pub use target::{MethodKind, Target}; -pub use version::*; arena_types!(rustc_arena::declare_arena); diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 63109c7ba5cbf..1cb9a1b65d650 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; -use rustc_ast::{Attribute, attr}; +use rustc_ast::{AttrItemKind, Attribute, attr}; use rustc_errors::codes::*; use rustc_errors::{Diag, PResult}; use rustc_span::{BytePos, Span}; @@ -313,7 +313,7 @@ impl<'a> Parser<'a> { this.expect(exp!(CloseParen))?; } Ok(( - ast::AttrItem { unsafety, path, args, tokens: None }, + ast::AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None }, Trailing::No, UsePreAttrPos::No, )) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index fae269bfdcf13..6c7725317691b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -225,6 +225,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect | AttributeKind::MacroTransparency(_) + | AttributeKind::CfgTrace(..) | AttributeKind::Pointee(..) | AttributeKind::Dummy | AttributeKind::RustcBuiltinMacro { .. } @@ -297,6 +298,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) | AttributeKind::PinV2(..) | AttributeKind::WindowsSubsystem(..) + | AttributeKind::CfgAttrTrace | AttributeKind::ThreadLocal | AttributeKind::CfiEncoding { .. } ) => { /* do nothing */ } @@ -334,8 +336,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::forbid | sym::cfg | sym::cfg_attr - | sym::cfg_trace - | sym::cfg_attr_trace // need to be fixed | sym::instruction_set // broken on stable!!! | sym::patchable_function_entry // FIXME(patchable_function_entry) @@ -1902,12 +1902,10 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`) // in where clauses. After that, only `self.check_attributes` should be enough. - const ATTRS_ALLOWED: &[Symbol] = &[sym::cfg_trace, sym::cfg_attr_trace]; let spans = self .tcx .hir_attrs(where_predicate.hir_id) .iter() - .filter(|attr| !ATTRS_ALLOWED.iter().any(|&sym| attr.has_name(sym))) // FIXME: We shouldn't need to special-case `doc`! .filter(|attr| { matches!( diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 71bbd64ddc6ce..3bc51e6fedd32 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -133,7 +133,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { &self.resolver.tcx.sess, self.resolver.tcx.features(), Vec::new(), - Early { emit_errors: ShouldEmit::Nothing }, + Early, + ShouldEmit::Nothing, ); let attrs = parser.parse_attribute_list( &i.attrs, diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 7ab2a72d75b5c..485af5ab1d016 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -7,7 +7,6 @@ use std::sync::Arc; use std::{fmt, mem, ops}; use itertools::Either; -use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir as hir; @@ -29,12 +28,6 @@ mod tests; #[cfg_attr(test, derive(PartialEq))] pub(crate) struct Cfg(CfgEntry); -#[derive(PartialEq, Debug)] -pub(crate) struct InvalidCfgError { - pub(crate) msg: &'static str, - pub(crate) span: Span, -} - /// Whether the configuration consists of just `Cfg` or `Not`. fn is_simple_cfg(cfg: &CfgEntry) -> bool { match cfg { @@ -105,106 +98,6 @@ fn should_capitalize_first_letter(cfg: &CfgEntry) -> bool { } impl Cfg { - /// Parses a `MetaItemInner` into a `Cfg`. - fn parse_nested( - nested_cfg: &MetaItemInner, - exclude: &FxHashSet, - ) -> Result, InvalidCfgError> { - match nested_cfg { - MetaItemInner::MetaItem(cfg) => Cfg::parse_without(cfg, exclude), - MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { - Ok(Some(Cfg(CfgEntry::Bool(*b, DUMMY_SP)))) - } - MetaItemInner::Lit(lit) => { - Err(InvalidCfgError { msg: "unexpected literal", span: lit.span }) - } - } - } - - fn parse_without( - cfg: &MetaItem, - exclude: &FxHashSet, - ) -> Result, InvalidCfgError> { - let name = match cfg.ident() { - Some(ident) => ident.name, - None => { - return Err(InvalidCfgError { - msg: "expected a single identifier", - span: cfg.span, - }); - } - }; - match cfg.kind { - MetaItemKind::Word => { - if exclude.contains(&NameValueCfg::new(name)) { - Ok(None) - } else { - Ok(Some(Cfg(CfgEntry::NameValue { name, value: None, span: DUMMY_SP }))) - } - } - MetaItemKind::NameValue(ref lit) => match lit.kind { - LitKind::Str(value, _) => { - if exclude.contains(&NameValueCfg::new_value(name, value)) { - Ok(None) - } else { - Ok(Some(Cfg(CfgEntry::NameValue { - name, - value: Some(value), - span: DUMMY_SP, - }))) - } - } - _ => Err(InvalidCfgError { - // FIXME: if the main #[cfg] syntax decided to support non-string literals, - // this should be changed as well. - msg: "value of cfg option should be a string literal", - span: lit.span, - }), - }, - MetaItemKind::List(ref items) => { - let orig_len = items.len(); - let mut sub_cfgs = - items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose()); - let ret = match name { - sym::all => { - sub_cfgs.try_fold(Cfg(CfgEntry::Bool(true, DUMMY_SP)), |x, y| Ok(x & y?)) - } - sym::any => { - sub_cfgs.try_fold(Cfg(CfgEntry::Bool(false, DUMMY_SP)), |x, y| Ok(x | y?)) - } - sym::not => { - if orig_len == 1 { - let mut sub_cfgs = sub_cfgs.collect::>(); - if sub_cfgs.len() == 1 { - Ok(!sub_cfgs.pop().unwrap()?) - } else { - return Ok(None); - } - } else { - Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span }) - } - } - _ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }), - }; - match ret { - Ok(c) => Ok(Some(c)), - Err(e) => Err(e), - } - } - } - } - - /// Parses a `MetaItem` into a `Cfg`. - /// - /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or - /// `target_os = "redox"`. - /// - /// If the content is not properly formatted, it will return an error indicating what and where - /// the error is. - pub(crate) fn parse(cfg: &MetaItemInner) -> Result { - Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap()) - } - /// Renders the configuration for human display, as a short HTML description. pub(crate) fn render_short_html(&self) -> String { let mut msg = Display(&self.0, Format::ShortHtml).to_string(); @@ -644,10 +537,6 @@ impl NameValueCfg { fn new(name: Symbol) -> Self { Self { name, value: None } } - - fn new_value(name: Symbol, value: Symbol) -> Self { - Self { name, value: Some(value) } - } } impl<'a> From<&'a CfgEntry> for NameValueCfg { @@ -751,15 +640,6 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator tcx: TyCtxt<'_>, cfg_info: &mut CfgInfo, ) -> Option> { - fn single(it: T) -> Option { - let mut iter = it.into_iter(); - let item = iter.next()?; - if iter.next().is_some() { - return None; - } - Some(item) - } - fn check_changed_auto_active_status( changed_auto_active_status: &mut Option, attr_span: Span, @@ -859,12 +739,11 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator } continue; } else if !cfg_info.parent_is_doc_cfg - && let Some(ident) = attr.ident() - && matches!(ident.name, sym::cfg | sym::cfg_trace) - && let Some(attr) = single(attr.meta_item_list()?) - && let Ok(new_cfg) = Cfg::parse(&attr) + && let hir::Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) = attr { - cfg_info.current_cfg &= new_cfg; + for (new_cfg, _) in cfgs { + cfg_info.current_cfg &= Cfg(new_cfg.clone()); + } } } diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 09316ead76aca..e0c21865d8dff 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -1,8 +1,5 @@ -use rustc_ast::ast::LitIntType; -use rustc_ast::{MetaItemInner, MetaItemLit, Path, Safety, StrStyle}; use rustc_data_structures::thin_vec::thin_vec; use rustc_hir::attrs::CfgEntry; -use rustc_span::symbol::{Ident, kw}; use rustc_span::{DUMMY_SP, create_default_session_globals_then}; use super::*; @@ -28,10 +25,6 @@ fn name_value_cfg_e(name: &str, value: &str) -> CfgEntry { } } -fn dummy_lit(symbol: Symbol, kind: LitKind) -> MetaItemInner { - MetaItemInner::Lit(MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }) -} - fn cfg_all(v: ThinVec) -> Cfg { Cfg(cfg_all_e(v)) } @@ -52,51 +45,6 @@ fn cfg_not(v: CfgEntry) -> Cfg { Cfg(CfgEntry::Not(Box::new(v), DUMMY_SP)) } -fn dummy_meta_item_word(name: &str) -> MetaItemInner { - MetaItemInner::MetaItem(MetaItem { - unsafety: Safety::Default, - path: Path::from_ident(Ident::from_str(name)), - kind: MetaItemKind::Word, - span: DUMMY_SP, - }) -} - -fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItemInner { - let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP }; - MetaItemInner::MetaItem(MetaItem { - unsafety: Safety::Default, - path: Path::from_ident(Ident::from_str(name)), - kind: MetaItemKind::NameValue(lit), - span: DUMMY_SP, - }) -} - -macro_rules! dummy_meta_item_list { - ($name:ident, [$($list:ident),* $(,)?]) => { - MetaItemInner::MetaItem(MetaItem { - unsafety: Safety::Default, - path: Path::from_ident(Ident::from_str(stringify!($name))), - kind: MetaItemKind::List(thin_vec![ - $( - dummy_meta_item_word(stringify!($list)), - )* - ]), - span: DUMMY_SP, - }) - }; - - ($name:ident, [$($list:expr),* $(,)?]) => { - MetaItemInner::MetaItem(MetaItem { - unsafety: Safety::Default, - path: Path::from_ident(Ident::from_str(stringify!($name))), - kind: MetaItemKind::List(thin_vec![ - $($list,)* - ]), - span: DUMMY_SP, - }) - }; -} - fn cfg_true() -> Cfg { Cfg(CfgEntry::Bool(true, DUMMY_SP)) } @@ -303,87 +251,6 @@ fn test_cfg_or() { }) } -#[test] -fn test_parse_ok() { - create_default_session_globals_then(|| { - let r#true = Symbol::intern("true"); - let mi = dummy_lit(r#true, LitKind::Bool(true)); - assert_eq!(Cfg::parse(&mi), Ok(cfg_true())); - - let r#false = Symbol::intern("false"); - let mi = dummy_lit(r#false, LitKind::Bool(false)); - assert_eq!(Cfg::parse(&mi), Ok(cfg_false())); - - let mi = dummy_meta_item_word("all"); - assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all"))); - - let done = Symbol::intern("done"); - let mi = dummy_meta_item_name_value("all", done, LitKind::Str(done, StrStyle::Cooked)); - assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done"))); - - let mi = dummy_meta_item_list!(all, [a, b]); - assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b"))); - - let mi = dummy_meta_item_list!(any, [a, b]); - assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b"))); - - let mi = dummy_meta_item_list!(not, [a]); - assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a"))); - - let mi = dummy_meta_item_list!( - not, - [dummy_meta_item_list!( - any, - [dummy_meta_item_word("a"), dummy_meta_item_list!(all, [b, c]),] - ),] - ); - assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c"))))); - - let mi = dummy_meta_item_list!(all, [a, b, c]); - assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c"))); - }) -} - -#[test] -fn test_parse_err() { - create_default_session_globals_then(|| { - let mi = dummy_meta_item_name_value("foo", kw::False, LitKind::Bool(false)); - assert!(Cfg::parse(&mi).is_err()); - - let mi = dummy_meta_item_list!(not, [a, b]); - assert!(Cfg::parse(&mi).is_err()); - - let mi = dummy_meta_item_list!(not, []); - assert!(Cfg::parse(&mi).is_err()); - - let mi = dummy_meta_item_list!(foo, []); - assert!(Cfg::parse(&mi).is_err()); - - let mi = dummy_meta_item_list!( - all, - [dummy_meta_item_list!(foo, []), dummy_meta_item_word("b"),] - ); - assert!(Cfg::parse(&mi).is_err()); - - let mi = dummy_meta_item_list!( - any, - [dummy_meta_item_word("a"), dummy_meta_item_list!(foo, []),] - ); - assert!(Cfg::parse(&mi).is_err()); - - let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(foo, []),]); - assert!(Cfg::parse(&mi).is_err()); - - let c = Symbol::intern("e"); - let mi = dummy_lit(c, LitKind::Char('e')); - assert!(Cfg::parse(&mi).is_err()); - - let five = Symbol::intern("5"); - let mi = dummy_lit(five, LitKind::Int(5.into(), LitIntType::Unsuffixed)); - assert!(Cfg::parse(&mi).is_err()); - }) -} - #[test] fn test_render_short_html() { create_default_session_globals_then(|| { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 764b3a0acdb6f..c410cb1b6d2fd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -51,7 +51,7 @@ use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeVisitableE use rustc_middle::{bug, span_bug}; use rustc_span::ExpnKind; use rustc_span::hygiene::{AstPass, MacroKind}; -use rustc_span::symbol::{Ident, Symbol, kw, sym}; +use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_trait_selection::traits::wf::object_region_bounds; use tracing::{debug, instrument}; use utils::*; @@ -2673,17 +2673,11 @@ fn add_without_unwanted_attributes<'hir>( import_parent, )); } - hir::Attribute::Unparsed(normal) if let [ident] = &*normal.path.segments => { - if is_inline || ident.name != sym::cfg_trace { - // If it's not a `cfg()` attribute, we keep it. - attrs.push((Cow::Borrowed(attr), import_parent)); - } - } - // FIXME: make sure to exclude `#[cfg_trace]` here when it is ported to the new parsers - hir::Attribute::Parsed(..) => { + // If it's not a `cfg()` attribute, we keep it. + hir::Attribute::Parsed(AttributeKind::CfgTrace(..)) if !is_inline => {} + _ => { attrs.push((Cow::Borrowed(attr), import_parent)); } - _ => {} } } } diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 95f5537f394c0..54da158d4d39c 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -2,7 +2,6 @@ use rustc_hir::Attribute; use rustc_hir::attrs::{AttributeKind, DocAttribute}; -use rustc_span::symbol::sym; use crate::clean::inline::{load_attrs, merge_attrs}; use crate::clean::{CfgInfo, Crate, Item, ItemKind}; @@ -39,10 +38,7 @@ fn add_only_cfg_attributes(attrs: &mut Vec, new_attrs: &[Attribute]) let mut new_attr = DocAttribute::default(); new_attr.cfg = d.cfg.clone(); attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr)))); - } else if let Attribute::Unparsed(normal) = attr - && let [ident] = &*normal.path.segments - && ident.name == sym::cfg_trace - { + } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr { // If it's a `cfg()` attribute, we keep it. attrs.push(attr.clone()); } diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index c3bc9048c23a8..40e74e5ef7138 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -9,6 +9,8 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{ExpnKind, Span}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; declare_clippy_lint! { /// ### What it does @@ -268,11 +270,6 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { /// attribute. fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool { cx.tcx.hir_parent_id_iter(hir_id).any(|id| { - cx.tcx.hir_attrs(id).iter().any(|attr| { - matches!( - attr.ident().map(|ident| ident.name), - Some(sym::cfg_trace | sym::cfg_attr_trace) - ) - }) + find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace(..)) }) } diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index add01b6a08376..834456ff6668b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -5,7 +5,8 @@ use clippy_utils::res::MaybeResPath; use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context}; use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; -use rustc_span::sym; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use super::CONST_IS_EMPTY; @@ -40,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_ fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { cx.tcx .hir_parent_id_iter(id) - .any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace))) + .any(|id| find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..))) } /// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index 6fc034b6fc5d2..67493d54b5525 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -9,6 +9,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocKind; use rustc_session::impl_lint_pass; use rustc_span::sym; +use rustc_hir::Attribute; +use rustc_hir::attrs::AttributeKind; declare_clippy_lint! { /// ### What it does @@ -121,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { let attrs_sugg = { let mut sugg = String::new(); for attr in cx.tcx.hir_attrs(assoc_item_hir_id) { - if !attr.has_name(sym::cfg_trace) { + let Attribute::Parsed(AttributeKind::CfgTrace(attrs)) = attr else { // This might be some other attribute that the `impl Default` ought to inherit. // But it could also be one of the many attributes that: // - can't be put on an impl block -- like `#[inline]` @@ -131,10 +133,13 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // reduce the applicability app = Applicability::MaybeIncorrect; continue; + }; + + for (_, attr_span) in attrs { + sugg.push_str(&snippet_with_applicability(cx.sess(), *attr_span, "_", &mut app)); + sugg.push('\n'); } - sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app)); - sugg.push('\n'); } sugg }; diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 409f130134891..2d7b8115ee173 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -121,6 +121,7 @@ use rustc_middle::ty::{ self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, }; +use rustc_hir::attrs::CfgEntry; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; @@ -2401,17 +2402,12 @@ pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool { /// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent /// use [`is_in_cfg_test`] pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { - tcx.hir_attrs(id).iter().any(|attr| { - if attr.has_name(sym::cfg_trace) - && let Some(items) = attr.meta_item_list() - && let [item] = &*items - && item.has_name(sym::test) - { - true - } else { - false - } - }) + if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), AttributeKind::CfgTrace(cfgs) => cfgs) + && cfgs.iter().any(|(cfg, _)| { matches!(cfg, CfgEntry::NameValue { name: sym::test, ..})}) { + true + } else { + false + } } /// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied @@ -2426,11 +2422,10 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - tcx.has_attr(def_id, sym::cfg_trace) - || tcx + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::CfgTrace(..)) + || find_attr!(tcx .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) - .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)) - .any(|attr| attr.has_name(sym::cfg_trace)) + .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)), AttributeKind::CfgTrace(..)) } /// Walks up the HIR tree from the given expression in an attempt to find where the value is From 563f3a5c7204c401f7e0c96d5b6471a6a547e5bc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 23 Dec 2025 17:23:23 +0100 Subject: [PATCH 2/5] Wip --- .../clippy/clippy_lints/src/attrs/mod.rs | 4 +- .../src/attrs/should_panic_without_expect.rs | 6 +- .../clippy/clippy_lints/src/cfg_not_test.rs | 62 ++++++++++++------- .../src/doc/include_in_doc_without_cfg.rs | 4 +- .../clippy_lints/src/incompatible_msrv.rs | 2 +- .../clippy_lints/src/large_include_file.rs | 3 +- .../clippy/clippy_utils/src/ast_utils/mod.rs | 10 ++- 7 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 91c2dc7f3dc6c..b571b183bf797 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -16,7 +16,7 @@ mod utils; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; -use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind}; +use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind, AttrItemKind}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -604,7 +604,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if attr.has_name(sym::ignore) && match &attr.kind { - AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }), + AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })), AttrKind::DocComment(..) => true, } { 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 fd27e30a67f3b..b854a3070bef7 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, AttrKind}; +use rustc_ast::{AttrArgs, AttrKind, AttrItemKind}; 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 AttrArgs::Eq { .. } = &normal_attr.item.args { + if let AttrItemKind::Unparsed(AttrArgs::Eq { .. }) = &normal_attr.item.args { // `#[should_panic = ".."]` found, good return; } - if let AttrArgs::Delimited(args) = &normal_attr.item.args + if let AttrItemKind::Unparsed(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 7590fe96fd214..28d3241fcffe5 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -1,7 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::MetaItemInner; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; +use rustc_ast::AttrItemKind; +use rustc_ast::EarlyParsedAttribute; +use rustc_span::sym; +use rustc_ast::attr::data_structures::CfgEntry; declare_clippy_lint! { /// ### What it does @@ -32,29 +35,44 @@ 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(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), 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 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"); + }, + ); + } } } } -fn contains_not_test(list: Option<&[MetaItemInner]>, not: bool) -> bool { - list.is_some_and(|list| { - list.iter().any(|item| { - item.ident().is_some_and(|ident| match ident.name { - rustc_span::sym::not => contains_not_test(item.meta_item_list(), !not), - rustc_span::sym::test => not, - _ => contains_not_test(item.meta_item_list(), not), - }) - }) - }) +fn contains_not_test(cfg: &CfgEntry, not: bool) -> bool { + match cfg { + CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| { + contains_not_test(item, not) + }), + CfgEntry::Not(sub, _) => contains_not_test(sub, !not), + CfgEntry::NameValue { name: sym::test, .. } => not, + _ => false + } + + // list.is_some_and(|list| { + // list.iter().any(|item| { + // item.ident().is_some_and(|ident| match ident.name { + // rustc_span::sym::not => contains_not_test(item.meta_item_list(), !not), + // rustc_span::sym::test => not, + // _ => contains_not_test(item.meta_item_list(), not), + // }) + // }) + // }) } 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 bca1cd03bb7ee..f8e9e870f6291 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, AttrKind, AttrStyle, Attribute}; +use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute, AttrItemKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; @@ -11,7 +11,7 @@ pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { if !attr.span.from_expansion() && let AttrKind::Normal(ref item) = attr.kind && attr.doc_str().is_some() - && let AttrArgs::Eq { expr: meta, .. } = &item.item.args + && let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.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/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 40e74e5ef7138..e0149a23fccfe 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -270,6 +270,6 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { /// attribute. fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool { cx.tcx.hir_parent_id_iter(hir_id).any(|id| { - find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace(..)) + find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace) }) } 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 48ce1afc6e69b..5c37747b8c9ba 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -1,3 +1,4 @@ +use rustc_ast::AttrItemKind; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; @@ -92,7 +93,7 @@ impl EarlyLintPass for LargeIncludeFile { && let AttrKind::Normal(ref item) = attr.kind && let Some(doc) = attr.doc_str() && doc.as_str().len() as u64 > self.max_file_size - && let AttrArgs::Eq { expr: meta, .. } = &item.item.args + && let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.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 432d7a251a217..618719286e8f9 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -976,11 +976,19 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { l.style == r.style && 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_args(&l.item.args, &r.item.args), + (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args), _ => false, } } +pub 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, + } +} + pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool { use AttrArgs::*; match (l, r) { From c8ffd4fcdb7dc2a9da5926fb2a22ea2ba87c5721 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 23 Dec 2025 17:23:41 +0100 Subject: [PATCH 3/5] Wip --- src/tools/clippy/clippy_lints/src/cfg_not_test.rs | 10 ---------- 1 file changed, 10 deletions(-) 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 28d3241fcffe5..ec543d02c9dd6 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -65,14 +65,4 @@ fn contains_not_test(cfg: &CfgEntry, not: bool) -> bool { CfgEntry::NameValue { name: sym::test, .. } => not, _ => false } - - // list.is_some_and(|list| { - // list.iter().any(|item| { - // item.ident().is_some_and(|ident| match ident.name { - // rustc_span::sym::not => contains_not_test(item.meta_item_list(), !not), - // rustc_span::sym::test => not, - // _ => contains_not_test(item.meta_item_list(), not), - // }) - // }) - // }) } From 2e6b17b49aea40adb20eca546c742062f8e79bb7 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 23 Dec 2025 17:32:25 +0100 Subject: [PATCH 4/5] Wip --- .../src/attributes/cfg_trace.rs | 33 ------------------- .../rustc_attr_parsing/src/attributes/mod.rs | 1 - compiler/rustc_attr_parsing/src/context.rs | 2 -- compiler/rustc_attr_parsing/src/interface.rs | 4 +-- 4 files changed, 2 insertions(+), 38 deletions(-) delete mode 100644 compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs deleted file mode 100644 index 7c4f0916e6d69..0000000000000 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs +++ /dev/null @@ -1,33 +0,0 @@ -use rustc_feature::AttributeTemplate; -use rustc_hir::attrs::{AttributeKind, CfgEntry}; -use rustc_span::{Span, Symbol, sym}; - -use crate::attributes::{CombineAttributeParser, ConvertFn}; -use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; -use crate::target_checking::{ALL_TARGETS, AllowedTargets}; -use crate::{CFG_TEMPLATE, parse_cfg_entry}; - -pub(crate) struct CfgTraceParser; - -impl CombineAttributeParser for CfgTraceParser { - const PATH: &[Symbol] = &[sym::cfg_trace]; - type Item = (CfgEntry, Span); - const CONVERT: ConvertFn = |c, _| AttributeKind::CfgTrace(c); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); - const TEMPLATE: AttributeTemplate = CFG_TEMPLATE; - - fn extend( - cx: &mut AcceptContext<'_, '_, S>, - args: &ArgParser, - ) -> impl IntoIterator { - let Some(list) = args.list() else { - return None; - }; - let Some(entry) = list.single() else { - return None; - }; - - Some((parse_cfg_entry(cx, entry).ok()?, cx.attr_span)) - } -} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 2ea53205d401b..c39c60ea7e391 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -33,7 +33,6 @@ pub(crate) mod allow_unstable; pub(crate) mod body; pub(crate) mod cfg; pub(crate) mod cfg_select; -pub(crate) mod cfg_trace; pub(crate) mod cfi_encoding; pub(crate) mod codegen_attrs; pub(crate) mod confusables; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index aecdc5f2b9ebd..26d089548f544 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -18,7 +18,6 @@ use crate::attributes::allow_unstable::{ AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, }; use crate::attributes::body::CoroutineParser; -use crate::attributes::cfg_trace::CfgTraceParser; use crate::attributes::cfi_encoding::CfiEncodingParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser, @@ -179,7 +178,6 @@ attribute_parsers!( // tidy-alphabetical-start Combine, Combine, - Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 01590d88e2720..50c83fa09416b 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -324,7 +324,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { match parsed { EarlyParsedAttribute::CfgTrace(cfg) => { attributes.push(Attribute::Parsed(AttributeKind::CfgTrace( - [(cfg.clone(), n.item.span())].into(), + [(cfg.clone(), attr.span)].into(), ))); } EarlyParsedAttribute::CfgAttrTrace => { @@ -462,7 +462,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { /// Returns whether there is a parser for an attribute with this name pub fn is_parsed_attribute(path: &[Symbol]) -> bool { - Late::parsers().accepters.contains_key(path) || path == &[sym::cfg_attr_trace] // FIXME + Late::parsers().accepters.contains_key(path) || path == &[sym::cfg_attr_trace] || path == &[sym::cfg_trace] // FIXME } fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs { From 303067c01ece83f4c7e307042890bfdf856445f0 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 23 Dec 2025 17:33:17 +0100 Subject: [PATCH 5/5] Wip --- compiler/rustc_attr_parsing/src/interface.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 50c83fa09416b..a96b591893eb2 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -462,7 +462,9 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { /// Returns whether there is a parser for an attribute with this name pub fn is_parsed_attribute(path: &[Symbol]) -> bool { - Late::parsers().accepters.contains_key(path) || path == &[sym::cfg_attr_trace] || path == &[sym::cfg_trace] // FIXME + Late::parsers().accepters.contains_key(path) + || path == &[sym::cfg_attr_trace] + || path == &[sym::cfg_trace] // FIXME } fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {