Skip to content
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand All @@ -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,
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + 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;
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_attr_parsing/src/synthetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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));
}
}
}
Expand All @@ -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)));
}
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/validate_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
18 changes: 13 additions & 5 deletions compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Attribute> {
// A synthetic trace attribute left in AST in place of the original `cfg_attr` attribute.
// It can later be used by lints or other diagnostics.
let 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];
};

Expand All @@ -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];
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)>),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl AttributeKind {
AllowInternalUnsafe(..) => Yes,
AllowInternalUnstable(..) => Yes,
AutomaticallyDerived => Yes,
CfgAttrTrace => Yes,
CfgAttrTrace(..) => Yes,
CfgTrace(..) => Yes,
CfiEncoding { .. } => Yes,
Cold => No,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => (),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
37 changes: 35 additions & 2 deletions src/librustdoc/passes/propagate_doc_cfg.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..)))
}
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
24 changes: 24 additions & 0 deletions tests/rustdoc-html/doc-cfg/auxiliary/cfg-attr-proc-macro.rs
Original file line number Diff line number Diff line change
@@ -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!()
}
14 changes: 14 additions & 0 deletions tests/rustdoc-html/doc-cfg/cfg-attr-proc-macro.rs
Original file line number Diff line number Diff line change
@@ -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;
14 changes: 14 additions & 0 deletions tests/rustdoc-html/doc-cfg/cfg-attr.rs
Original file line number Diff line number Diff line change
@@ -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;
Loading