diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index c53188a22aedd..ede2ba5d3c4dd 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -62,6 +62,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, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d0871b0b98f45..8ce32ba226940 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}; @@ -208,6 +208,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { tcx.features(), registered_tools, Late, + ShouldEmit::ErrorsAndLints, ), delayed_lints: Vec::new(), } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 798cc10765415..fe24e780cbd40 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, @@ -413,10 +414,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..cd0830e6a7fda --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_trace.rs @@ -0,0 +1,50 @@ +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)) + } +} + +pub(crate) struct CfgAttrTraceParser; + +impl CombineAttributeParser for CfgAttrTraceParser { + const PATH: &[Symbol] = &[sym::cfg_attr_trace]; + type Item = (); + const CONVERT: ConvertFn = AttributeKind::CfgAttrTrace; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const TEMPLATE: AttributeTemplate = CFG_TEMPLATE; + + fn extend( + _cx: &mut AcceptContext<'_, '_, S>, + _args: &ArgParser, + ) -> impl IntoIterator { + Some(()) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index f7290bd7e6f25..832539b373ef4 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 codegen_attrs; pub(crate) mod confusables; pub(crate) mod crate_level; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 9e83ea4114937..a3804744df298 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::{CfgAttrTraceParser, CfgTraceParser}; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, @@ -175,6 +175,8 @@ attribute_parsers!( // tidy-alphabetical-start Combine, Combine, + Combine, + Combine, Combine, Combine, Combine, @@ -279,14 +281,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; } @@ -298,17 +292,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 @@ -323,17 +306,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 @@ -341,12 +313,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; @@ -383,16 +351,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..d4f4ecbaa0341 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -3,14 +3,14 @@ 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_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, @@ -179,7 +180,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 +190,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 +234,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 +255,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 +270,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 +314,11 @@ 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); + // 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()), @@ -321,7 +335,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { &n.item.args, &parts, &self.sess.psess, - self.stage.should_emit(), + self.should_emit, ) else { continue; }; @@ -374,9 +388,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 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_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 492c845df1710..90e3cd08c309d 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, AttrArgs, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, + MetaItemInner, NodeId, NormalAttr, }; use rustc_attr_parsing as attr; use rustc_attr_parsing::{ @@ -288,7 +288,10 @@ 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 = AttrArgs::Empty; + 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..e38324fd427dd 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2188,21 +2188,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, + }, + ); } } } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fb3d9a6368ab0..cc6ab464d2eb5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -647,6 +647,14 @@ pub enum AttributeKind { span: Span, }, + /// Represents `#[cfg_attr]` trace attributes + /// These are attributes left behind in the HIR after `#[cfg_attr]` attributes are processed + CfgAttrTrace(ThinVec<()>, Span), + + /// Represents `#[cfg]` trace attributes + /// These are attributes left behind in the HIR after `#[cfg]` attributes are processed + CfgTrace(ThinVec<(CfgEntry, Span)>), + /// Represents `#[rustc_coinductive]`. Coinductive(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 56446e8959c10..13acb479a5057 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, Coinductive(..) => No, Cold(..) => No, Confusables { .. } => Yes, 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_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d8ecbbc491388..b51b11db5da40 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -225,6 +225,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect | AttributeKind::MacroTransparency(_) + | AttributeKind::CfgTrace(..) + | AttributeKind::CfgAttrTrace(..) | AttributeKind::Pointee(..) | AttributeKind::Dummy | AttributeKind::RustcBuiltinMacro { .. } @@ -345,8 +347,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::forbid | sym::cfg | sym::cfg_attr - | sym::cfg_trace - | sym::cfg_attr_trace // need to be fixed | sym::cfi_encoding // FIXME(cfi_encoding) | sym::instruction_set // broken on stable!!! @@ -1968,12 +1968,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