Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3490,7 +3490,8 @@ impl AttrItemKind {
#[derive(Clone, Encodable, Decodable, Debug, StableHash)]
pub enum EarlyParsedAttribute {
CfgTrace(CfgEntry),
CfgAttrTrace,
/// The value is the `cfg` predicate of this `cfg_attr`.
CfgAttrTrace(CfgEntry),
}

impl AttrItem {
Expand Down Expand Up @@ -4407,7 +4408,7 @@ mod size_asserts {
static_assert_size!(MetaItem, 80);
static_assert_size!(MetaItemKind, 40);
static_assert_size!(MetaItemLit, 40);
static_assert_size!(NormalAttr, 72);
static_assert_size!(NormalAttr, 80);
static_assert_size!(Param, 40);
static_assert_size!(Pat, 64);
static_assert_size!(PatKind, 48);
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_attr_parsing/src/early_parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ pub(crate) struct EarlyParsedState {
cfg_trace: ThinVec<(CfgEntry, Span)>,

/// Attribute state for `#[cfg_attr]` trace attributes
/// The arguments of these attributes is no longer relevant for any later passes, only their presence.
/// So we discard the arguments here.
cfg_attr_trace: bool,
cfg_attr_trace: ThinVec<(CfgEntry, Span)>,
}

impl EarlyParsedState {
Expand All @@ -36,8 +34,10 @@ impl EarlyParsedState {
cfg.lower_spans(lower_span);
self.cfg_trace.push((cfg, attr_span));
}
EarlyParsedAttribute::CfgAttrTrace => {
self.cfg_attr_trace = true;
EarlyParsedAttribute::CfgAttrTrace(cfg) => {
let mut cfg = cfg.clone();
cfg.lower_spans(lower_span);
self.cfg_attr_trace.push((cfg, attr_span));
}
}
}
Expand All @@ -46,7 +46,7 @@ impl EarlyParsedState {
if !self.cfg_trace.is_empty() {
attributes.push(Attribute::Parsed(AttributeKind::CfgTrace(self.cfg_trace)));
}
if self.cfg_attr_trace {
if !self.cfg_attr_trace.is_empty() {
attributes.push(Attribute::Parsed(AttributeKind::CfgAttrTrace));
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ impl<'a> TraitDef<'a> {
let rustc_const_unstable =
cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span));

let mut attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span),];
let mut attrs = thin_vec![cx.attr_word(sym::automatically_derived, self.span)];

// Only add `rustc_const_unstable` attributes if `derive_const` is used within libcore/libstd,
// Other crates don't need stability attributes, so adding them is not useful, but libcore needs them
Expand Down
18 changes: 14 additions & 4 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 @@ -264,16 +265,17 @@ impl<'a> StripUnconfigured<'a> {
// A trace attribute left in AST in place of the original `cfg_attr` attribute.
// It can later be used by lints or other diagnostics.
let mut trace_attr = cfg_attr.clone();
trace_attr.replace_args(AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace));
let trace_attr = attr_into_trace(trace_attr, sym::cfg_attr_trace);

let Some((cfg_predicate, expanded_attrs)) = rustc_attr_parsing::parse_cfg_attr(
cfg_attr,
self.sess,
self.features,
self.lint_node_id,
) else {
return vec![trace_attr];
trace_attr.replace_args(AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace(
CfgEntry::Bool(false, cfg_attr.span),
)));
return vec![attr_into_trace(trace_attr, sym::cfg_attr_trace)];
};

// Lint on zero attributes in source.
Expand All @@ -287,9 +289,17 @@ impl<'a> StripUnconfigured<'a> {
}

if !attr::eval_config_entry(self.sess, &cfg_predicate).as_bool() {
return vec![trace_attr];
trace_attr.replace_args(AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace(
CfgEntry::Bool(false, cfg_attr.span),
)));
return vec![attr_into_trace(trace_attr, sym::cfg_attr_trace)];
}

// We add the `cfg` predicate into the trace.
trace_attr
.replace_args(AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace(cfg_predicate)));
let trace_attr = attr_into_trace(trace_attr, sym::cfg_attr_trace);

if recursive {
// We call `process_cfg_attr` recursively in case there's a
// `cfg_attr` inside of another `cfg_attr`. E.g.
Expand Down
90 changes: 89 additions & 1 deletion compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
span,
path,
};
let items = match expander.expand(self.cx, span, &meta, item, is_const) {
let cfg_attrs = self.get_item_cfg_attr_traces(&item);
let mut items = match expander.expand(self.cx, span, &meta, item, is_const) {
ExpandResult::Ready(items) => items,
ExpandResult::Retry(item) => {
// Reassemble the original invocation for retrying.
Expand All @@ -920,6 +921,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
});
}
};
self.add_cfg_attrs_to_items(&mut items, cfg_attrs);

let fragment = fragment_kind.expect_from_annotatables(items);
if macro_stats {
update_derive_macro_stats(
Expand Down Expand Up @@ -1002,6 +1005,91 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
})
}

fn add_cfg_attrs_to_items(&self, items: &mut Vec<Annotatable>, cfg_attrs: Vec<ast::Attribute>) {
if cfg_attrs.is_empty() {
return;
}
for item in items {
match item {
Annotatable::Item(item) => {
item.attrs.extend(cfg_attrs.clone());
}
Annotatable::Stmt(stmt) => {
if let StmtKind::Item(item) = &mut stmt.kind {
item.attrs.extend(cfg_attrs.clone());
}
}
Annotatable::GenericParam(_) => {
self.cx.dcx().span_delayed_bug(
item.span(),
"trying to get derive attributes on a `GenericParam`",
);
}
_ => unreachable!(),
}
}
}

fn get_item_cfg_attr_traces(&self, item: &Annotatable) -> Vec<ast::Attribute> {
match item {
Annotatable::Item(item) => self.cfg_attr_traces_for_derive(item),
Annotatable::Stmt(stmt) if let StmtKind::Item(ref item) = stmt.kind => {
self.cfg_attr_traces_for_derive(item)
}
Annotatable::GenericParam(_) => {
self.cx.dcx().span_delayed_bug(
item.span(),
"trying to get derive attributes on a `GenericParam`",
);
Vec::new()
}
_ => unreachable!(),
}
}

fn cfg_attr_traces_for_derive(&self, item: &ast::Item) -> Vec<ast::Attribute> {
item.attrs
.iter()
.filter(|attr| {
// FIXME(GuillaumeGomez): Find a better way to propagate `cfg_attr` to expanded items
// as it doesn't work nicely with `include!` and equivalents...
// We need to filter to ensure only the `cfg_attr`s from which the item was expanded
// are taken into account.
attr.name().is_some_and(|name| name == sym::cfg_attr_trace)
&& attr.span.contains(self.cx.call_site())
})
.filter_map(|attr| {
let mut attr = attr.clone();
let ast::AttrKind::Normal(normal_attr) = &mut attr.kind else { unreachable!() };
let cfg = if let ast::ast::AttrItemKind::Parsed(
EarlyParsedAttribute::CfgAttrTrace(cfg),
) = &mut normal_attr.item.args
{
std::mem::replace(
cfg,
rustc_ast::attr::data_structures::CfgEntry::Bool(
false,
rustc_span::DUMMY_SP,
),
)
} else {
unreachable!()
};
normal_attr.item.args =
ast::ast::AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg));
// We also need to change the attribute because `rustdoc-json-types` is using the
// already processed attributes. So if we don't make this change, we will have an
// attribute with the name `cfg_attr_trace` but of type
// `AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace())`, entering the
// `unreachable!()`.
if let Some(name) = normal_attr.item.path.segments.first_mut() {
name.ident.name = sym::cfg_trace;
}
Some(attr)
})
.collect::<Vec<_>>()
}

fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
let kind = match item {
Annotatable::Item(_)
Expand Down
15 changes: 15 additions & 0 deletions tests/rustdoc-html/doc-cfg/cfg-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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