diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index cdff6501ec1c4..4dc836f19fdd8 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3514,7 +3514,7 @@ pub enum SyntheticAttr { /// because they are not needed. /// /// The attribute is used by some clippy lints. - CfgAttrTrace, + CfgAttrTrace(CfgEntry), } impl AttrItem { diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 39c4f403bf855..40a1b4bd32218 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -106,7 +106,7 @@ impl AttributeExt for Attribute { use SyntheticAttr::*; match &self.kind { AttrKind::Normal(normal) => normal.item.name(), - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => None, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, AttrKind::DocComment(..) => None, } } @@ -117,7 +117,7 @@ impl AttributeExt for Attribute { AttrKind::Normal(normal) => { Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect()) } - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => None, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, AttrKind::DocComment(_, _) => None, } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index fe1da820cf74f..1ebddc1c57d3d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -525,7 +525,7 @@ impl<'a> AstValidator<'a> { [sym::allow, sym::deny, sym::expect, sym::forbid, sym::splat, sym::warn]; !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(&normal.item) } - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => false, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => false, AttrKind::DocComment(..) => true, }) .for_each(|attr| { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 42d01a92eebbf..3b0c90264e32d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -665,7 +665,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool { use ast::SyntheticAttr::*; match attr.kind { - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => { + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => { // These are internal synthetic attributes with no syntax, so avoid printing them // to keep the printed code reasonably parse-able. return false; diff --git a/compiler/rustc_attr_parsing/src/synthetic.rs b/compiler/rustc_attr_parsing/src/synthetic.rs index 5e633e6ee3685..290730aaee132 100644 --- a/compiler/rustc_attr_parsing/src/synthetic.rs +++ b/compiler/rustc_attr_parsing/src/synthetic.rs @@ -15,9 +15,7 @@ pub(crate) struct SyntheticAttrState { cfg_trace: ThinVec<(CfgEntry, Span)>, /// Attribute state for `SyntheticAttr::CfgAttrTrace` attributes. - /// The arguments of these attributes is no longer relevant for any later passes, only their - /// presence. So we discard the arguments here. - cfg_attr_trace: bool, + cfg_attr_trace: ThinVec<(CfgEntry, Span)>, } impl SyntheticAttrState { @@ -33,8 +31,10 @@ impl SyntheticAttrState { cfg.lower_spans(lower_span); self.cfg_trace.push((cfg, attr_span)); } - SyntheticAttr::CfgAttrTrace => { - self.cfg_attr_trace = true; + SyntheticAttr::CfgAttrTrace(cfg) => { + let mut cfg = cfg.clone(); + cfg.lower_spans(lower_span); + self.cfg_attr_trace.push((cfg, attr_span)); } } } @@ -43,8 +43,8 @@ impl SyntheticAttrState { if !self.cfg_trace.is_empty() { attributes.push(Attribute::Parsed(AttributeKind::CfgTrace(self.cfg_trace))); } - if self.cfg_attr_trace { - attributes.push(Attribute::Parsed(AttributeKind::CfgAttrTrace)); + if !self.cfg_attr_trace.is_empty() { + attributes.push(Attribute::Parsed(AttributeKind::CfgAttrTrace(self.cfg_attr_trace))); } } } diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 90380c08fdba1..08c73352f27eb 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -24,7 +24,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { use ast::SyntheticAttr::*; match &attr.kind { AttrKind::Normal(_) => {} - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) | AttrKind::DocComment(..) => return, + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) | AttrKind::DocComment(..) => return, } let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)); diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 030fa96a9fa68..29f78ff5769a5 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -2,6 +2,7 @@ use std::iter; +use rustc_ast::attr::data_structures::CfgEntry; use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, WithTokens, @@ -248,16 +249,15 @@ impl<'a> StripUnconfigured<'a> { /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec { - // A synthetic trace attribute left in AST in place of the original `cfg_attr` attribute. - // It can later be used by lints or other diagnostics. - let trace_attr = cfg_attr.clone().convert_normal_to_synthetic(SyntheticAttr::CfgAttrTrace); - let Some((cfg_predicate, expanded_attrs)) = rustc_attr_parsing::parse_cfg_attr( cfg_attr, self.sess, self.features, self.lint_node_id, ) else { + let trace_attr = cfg_attr.clone().convert_normal_to_synthetic( + SyntheticAttr::CfgAttrTrace(CfgEntry::Bool(true, cfg_attr.span)), + ); return vec![trace_attr]; }; @@ -271,7 +271,15 @@ impl<'a> StripUnconfigured<'a> { ); } - if !attr::eval_config_entry(self.sess, &cfg_predicate).as_bool() { + let cfg_eval = attr::eval_config_entry(self.sess, &cfg_predicate).as_bool(); + + // A synthetic trace attribute left in AST in place of the original `cfg_attr` attribute. + // It can later be used by lints or other diagnostics. + let trace_attr = cfg_attr + .clone() + .convert_normal_to_synthetic(SyntheticAttr::CfgAttrTrace(cfg_predicate)); + + if !cfg_eval { return vec![trace_attr]; } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 8b897aa3c5f96..dacd7c47e91e7 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2276,7 +2276,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ); } AttrKind::Normal(_) => {} - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => {} + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => {} AttrKind::DocComment(..) => unreachable!(), // handled above } } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index bbec92786f047..db492475a3aa4 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1011,7 +1011,7 @@ pub enum AttributeKind { AutomaticallyDerived, /// Represents the trace attribute of `#[cfg_attr]` - CfgAttrTrace, + CfgAttrTrace(ThinVec<(CfgEntry, Span)>), /// Represents the trace attribute of `#[cfg]` CfgTrace(ThinVec<(CfgEntry, Span)>), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index f703aebdbc6f1..1e634513fdce2 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -21,7 +21,7 @@ impl AttributeKind { AllowInternalUnsafe(..) => Yes, AllowInternalUnstable(..) => Yes, AutomaticallyDerived => Yes, - CfgAttrTrace => Yes, + CfgAttrTrace(..) => Yes, CfgTrace(..) => Yes, CfiEncoding { .. } => Yes, Cold => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 032cbcbc1e794..f46c983c7c2d8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -237,7 +237,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // All of the following attributes have no specific checks. // tidy-alphabetical-start AttributeKind::AutomaticallyDerived => (), - AttributeKind::CfgAttrTrace => (), + AttributeKind::CfgAttrTrace(..) => (), AttributeKind::CfgTrace(..) => (), AttributeKind::CfiEncoding { .. } => (), AttributeKind::Cold => (), diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 1ef2414b0af49..0b54b265fcee7 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -565,7 +565,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { .push((normal.item.path.segments[0].ident, self.parent_scope)); } } - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace) => {} + AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => {} AttrKind::DocComment(..) => {} } visit::walk_attribute(self, attr); diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 213b8060a8512..e15bb657b7867 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -1,8 +1,9 @@ //! Propagates [`#[doc(cfg(...))]`](https://github.com/rust-lang/rust/issues/43781) to child items. use rustc_data_structures::fx::FxHashMap; -use rustc_hir::Attribute; use rustc_hir::attrs::{AttributeKind, DocAttribute}; +use rustc_hir::{Attribute, find_attr}; +use rustc_span::{ExpnKind, MacroKind}; use crate::clean::inline::{load_attrs, merge_attrs}; use crate::clean::{CfgInfo, Crate, Item, ItemId, ItemKind}; @@ -133,8 +134,40 @@ impl DocFolder for CfgPropagator<'_, '_> { { self.cfg_info = cfg_info; } - if let ItemKind::PlaceholderImplItem = item.kind { + if let Some(impl_def_id) = item.item_id.as_def_id() { + let tcx = self.cx.tcx; + let expn_data = tcx.expn_that_defined(impl_def_id).expn_data(); + if matches!(expn_data.kind, ExpnKind::Macro(MacroKind::Derive, _)) + // This impl block comes from a `derive` expansion, so we want to retrieve + // the `cfg_attr` if any. + && let Some(self_ty_def_id) = tcx + .type_of(impl_def_id) + .instantiate_identity() + .skip_norm_wip() + .ty_adt_def() + .map(|adt| adt.did()) + && let self_ty_attrs = load_attrs(tcx, self_ty_def_id) + && let Some(cfgs_attr_trace) = + find_attr!(self_ty_attrs, CfgAttrTrace(cfgs) => cfgs) + && !cfgs_attr_trace.is_empty() + { + // We retrieve the `cfg_attr` of the `derive` this `impl` comes from. + let derive_span = expn_data.call_site; + let attrs_iter = Attribute::Parsed(AttributeKind::CfgTrace( + cfgs_attr_trace + .iter() + .filter(|(_, span)| span.contains(derive_span)) + .cloned() + .collect(), + )); + crate::clean::extract_cfg_from_attrs( + std::iter::once(&attrs_iter), + tcx, + &mut self.cfg_info, + ); + } + } // If we have a placeholder impl, we store the current `cfg` "context" to be used // on the actual impl later on (the impls are generated after we go through the whole // AST so they're stored in the `krate` object at the end). diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 3e82ff4a69545..851d605c36be5 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -32,7 +32,7 @@ impl From<&AttrKind> for SimpleAttrKind { AttrKind::Synthetic(synthetic) => { match &**synthetic { SyntheticAttr::CfgTrace(_) => Self::CfgTrace, - SyntheticAttr::CfgAttrTrace => Self::CfgAttrTrace, + SyntheticAttr::CfgAttrTrace(_) => Self::CfgAttrTrace, } } AttrKind::DocComment(..) => Self::Doc, diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 99e7d202fe46f..cefb48046be47 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -270,5 +270,5 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { 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, id, CfgTrace(..) | CfgAttrTrace)) + .any(|id| find_attr!(cx.tcx, id, CfgTrace(..) | CfgAttrTrace(..))) } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 426102e149d3f..82752b3d73766 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2344,7 +2344,7 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind && let Res::Def(_, def_id) = path.res { - return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace); + return find_attr!(cx.tcx, def_id, CfgTrace(..) | CfgAttrTrace(..)); } false } diff --git a/tests/rustdoc-html/doc-cfg/auxiliary/cfg-attr-proc-macro.rs b/tests/rustdoc-html/doc-cfg/auxiliary/cfg-attr-proc-macro.rs new file mode 100644 index 0000000000000..21db277833e05 --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/auxiliary/cfg-attr-proc-macro.rs @@ -0,0 +1,24 @@ +//@ no-prefer-dynamic +//@ edition: 2024 + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree}; + +#[proc_macro_derive(Yop)] +pub fn derive_yop(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + + while let Some(token) = iter.next() { + if let TokenTree::Ident(ident) = token && + matches!(ident.to_string().as_str(), "struct" | "enum" | "union") + { + // Next token is the name. That's all we need! + let Some(TokenTree::Ident(ident)) = iter.next() else { panic!() }; + return format!("impl Trait for {ident} {{}}").parse().unwrap(); + } + } + panic!() +} diff --git a/tests/rustdoc-html/doc-cfg/cfg-attr-proc-macro.rs b/tests/rustdoc-html/doc-cfg/cfg-attr-proc-macro.rs new file mode 100644 index 0000000000000..d4f44a333bd25 --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/cfg-attr-proc-macro.rs @@ -0,0 +1,14 @@ +//@ aux-build: cfg-attr-proc-macro.rs + +#![crate_name = "foo"] +#![feature(doc_cfg)] + +extern crate cfg_attr_proc_macro; + +pub trait Trait {} + +//@ has 'foo/struct.B.html' +//@ has - '//*[@id="impl-Trait-for-B"]/*[@class="item-info"]/*[@class="stab portability"]' \ +// 'Available on non-crate feature boop only.' +#[cfg_attr(not(feature = "boop"), derive(cfg_attr_proc_macro::Yop))] +pub struct B; diff --git a/tests/rustdoc-html/doc-cfg/cfg-attr.rs b/tests/rustdoc-html/doc-cfg/cfg-attr.rs new file mode 100644 index 0000000000000..96338d3c40c0e --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/cfg-attr.rs @@ -0,0 +1,14 @@ +// This test ensures that the `cfg_attr` cfg predicates are correctly kept to be used +// by the `doc_cfg` feature. + +#![crate_name = "foo"] +#![feature(doc_cfg)] + +//@ has 'foo/struct.Test.html' +//@ has - '//*[@id="impl-Debug-for-Test"]/*[@class="item-info"]/*[@class="stab portability"]' \ +// 'Available on non-crate feature debug only.' +//@ has - '//*[@id="impl-Clone-for-Test"]/*[@class="item-info"]/*[@class="stab portability"]' \ +// 'Available on non-crate feature aa and non-crate feature bb only.' +#[cfg_attr(not(feature = "debug"), derive(Debug))] +#[cfg_attr(not(feature = "aa"), cfg_attr(not(feature = "bb"), derive(Clone)))] +pub struct Test;