Skip to content

Commit 0006ea2

Browse files
authored
Rollup merge of rust-lang#82682 - petrochenkov:cfgeval, r=Aaron1011
Implement built-in attribute macro `#[cfg_eval]` + some refactoring This PR implements a built-in attribute macro `#[cfg_eval]` as it was suggested in rust-lang#79078 to avoid `#[derive()]` without arguments being abused as a way to configure input for other attributes. The macro is used for eagerly expanding all `#[cfg]` and `#[cfg_attr]` attributes in its input ("fully configuring" the input). The effect is identical to effect of `#[derive(Foo, Bar)]` which also fully configures its input before passing it to macros `Foo` and `Bar`, but unlike `#[derive]` `#[cfg_eval]` can be applied to any syntax nodes supporting macro attributes, not only certain items. `cfg_eval` was the first name suggested in rust-lang#79078, but other alternatives are also possible, e.g. `cfg_expand`. ```rust #[cfg_eval] #[my_attr] // Receives `struct S {}` as input, the field is configured away by `#[cfg_eval]` struct S { #[cfg(FALSE)] field: u8, } ``` Tracking issue: rust-lang#82679
2 parents d153d8c + 5d27728 commit 0006ea2

File tree

17 files changed

+453
-259
lines changed

17 files changed

+453
-259
lines changed

compiler/rustc_ast/src/ast.rs

-10
Original file line numberDiff line numberDiff line change
@@ -915,16 +915,6 @@ impl Stmt {
915915
}
916916
}
917917

918-
pub fn tokens_mut(&mut self) -> Option<&mut LazyTokenStream> {
919-
match self.kind {
920-
StmtKind::Local(ref mut local) => local.tokens.as_mut(),
921-
StmtKind::Item(ref mut item) => item.tokens.as_mut(),
922-
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens.as_mut(),
923-
StmtKind::Empty => None,
924-
StmtKind::MacCall(ref mut mac) => mac.tokens.as_mut(),
925-
}
926-
}
927-
928918
pub fn has_trailing_semicolon(&self) -> bool {
929919
match &self.kind {
930920
StmtKind::Semi(_) => true,

compiler/rustc_ast/src/ast_like.rs

+34-54
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,7 @@ use super::{AttrVec, Attribute, Stmt, StmtKind};
1111
pub trait AstLike: Sized {
1212
fn attrs(&self) -> &[Attribute];
1313
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>));
14-
/// Called by `Parser::collect_tokens` to store the collected
15-
/// tokens inside an AST node
16-
fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {
17-
// This default impl makes this trait easier to implement
18-
// in tools like `rust-analyzer`
19-
panic!("`finalize_tokens` is not supported!")
20-
}
14+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>;
2115
}
2216

2317
impl<T: AstLike + 'static> AstLike for P<T> {
@@ -27,8 +21,8 @@ impl<T: AstLike + 'static> AstLike for P<T> {
2721
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
2822
(**self).visit_attrs(f);
2923
}
30-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
31-
(**self).finalize_tokens(tokens)
24+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
25+
(**self).tokens_mut()
3226
}
3327
}
3428

@@ -42,12 +36,12 @@ fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) {
4236

4337
impl AstLike for StmtKind {
4438
fn attrs(&self) -> &[Attribute] {
45-
match *self {
46-
StmtKind::Local(ref local) => local.attrs(),
47-
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
48-
StmtKind::Item(ref item) => item.attrs(),
39+
match self {
40+
StmtKind::Local(local) => local.attrs(),
41+
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(),
42+
StmtKind::Item(item) => item.attrs(),
4943
StmtKind::Empty => &[],
50-
StmtKind::MacCall(ref mac) => &*mac.attrs,
44+
StmtKind::MacCall(mac) => &mac.attrs,
5145
}
5246
}
5347

@@ -60,17 +54,14 @@ impl AstLike for StmtKind {
6054
StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f),
6155
}
6256
}
63-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
64-
let stmt_tokens = match self {
65-
StmtKind::Local(ref mut local) => &mut local.tokens,
66-
StmtKind::Item(ref mut item) => &mut item.tokens,
67-
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens,
68-
StmtKind::Empty => return,
69-
StmtKind::MacCall(ref mut mac) => &mut mac.tokens,
70-
};
71-
if stmt_tokens.is_none() {
72-
*stmt_tokens = Some(tokens);
73-
}
57+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
58+
Some(match self {
59+
StmtKind::Local(local) => &mut local.tokens,
60+
StmtKind::Item(item) => &mut item.tokens,
61+
StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens,
62+
StmtKind::Empty => return None,
63+
StmtKind::MacCall(mac) => &mut mac.tokens,
64+
})
7465
}
7566
}
7667

@@ -82,8 +73,8 @@ impl AstLike for Stmt {
8273
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
8374
self.kind.visit_attrs(f);
8475
}
85-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
86-
self.kind.finalize_tokens(tokens)
76+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
77+
self.kind.tokens_mut()
8778
}
8879
}
8980

@@ -92,17 +83,13 @@ impl AstLike for Attribute {
9283
&[]
9384
}
9485
fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
95-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
96-
match &mut self.kind {
97-
AttrKind::Normal(_, attr_tokens) => {
98-
if attr_tokens.is_none() {
99-
*attr_tokens = Some(tokens);
100-
}
86+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
87+
Some(match &mut self.kind {
88+
AttrKind::Normal(_, tokens) => tokens,
89+
kind @ AttrKind::DocComment(..) => {
90+
panic!("Called tokens_mut on doc comment attr {:?}", kind)
10191
}
102-
AttrKind::DocComment(..) => {
103-
panic!("Called finalize_tokens on doc comment attr {:?}", self)
104-
}
105-
}
92+
})
10693
}
10794
}
10895

@@ -115,10 +102,8 @@ impl<T: AstLike> AstLike for Option<T> {
115102
inner.visit_attrs(f);
116103
}
117104
}
118-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
119-
if let Some(inner) = self {
120-
inner.finalize_tokens(tokens);
121-
}
105+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
106+
self.as_mut().and_then(|inner| inner.tokens_mut())
122107
}
123108
}
124109

@@ -152,11 +137,8 @@ macro_rules! derive_has_tokens_and_attrs {
152137
VecOrAttrVec::visit(&mut self.attrs, f)
153138
}
154139

155-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
156-
if self.tokens.is_none() {
157-
self.tokens = Some(tokens);
158-
}
159-
140+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
141+
Some(&mut self.tokens)
160142
}
161143
}
162144
)* }
@@ -173,7 +155,9 @@ macro_rules! derive_has_attrs_no_tokens {
173155
VecOrAttrVec::visit(&mut self.attrs, f)
174156
}
175157

176-
fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {}
158+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
159+
None
160+
}
177161
}
178162
)* }
179163
}
@@ -185,14 +169,10 @@ macro_rules! derive_has_tokens_no_attrs {
185169
&[]
186170
}
187171

188-
fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {
189-
}
190-
191-
fn finalize_tokens(&mut self, tokens: LazyTokenStream) {
192-
if self.tokens.is_none() {
193-
self.tokens = Some(tokens);
194-
}
172+
fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {}
195173

174+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
175+
Some(&mut self.tokens)
196176
}
197177
}
198178
)* }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
use crate::util::check_builtin_macro_attribute;
2+
3+
use rustc_ast::mut_visit::{self, MutVisitor};
4+
use rustc_ast::ptr::P;
5+
use rustc_ast::{self as ast, AstLike};
6+
use rustc_expand::base::{Annotatable, ExtCtxt};
7+
use rustc_expand::config::StripUnconfigured;
8+
use rustc_expand::configure;
9+
use rustc_span::symbol::sym;
10+
use rustc_span::Span;
11+
use smallvec::SmallVec;
12+
13+
crate fn expand(
14+
ecx: &mut ExtCtxt<'_>,
15+
_span: Span,
16+
meta_item: &ast::MetaItem,
17+
annotatable: Annotatable,
18+
) -> Vec<Annotatable> {
19+
check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval);
20+
cfg_eval(ecx, annotatable)
21+
}
22+
23+
crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatable> {
24+
let mut visitor = CfgEval {
25+
cfg: StripUnconfigured { sess: ecx.sess, features: ecx.ecfg.features, modified: false },
26+
};
27+
let mut annotatable = visitor.configure_annotatable(annotatable);
28+
if visitor.cfg.modified {
29+
// Erase the tokens if cfg-stripping modified the item
30+
// This will cause us to synthesize fake tokens
31+
// when `nt_to_tokenstream` is called on this item.
32+
if let Some(tokens) = annotatable.tokens_mut() {
33+
*tokens = None;
34+
}
35+
}
36+
vec![annotatable]
37+
}
38+
39+
struct CfgEval<'a> {
40+
cfg: StripUnconfigured<'a>,
41+
}
42+
43+
impl CfgEval<'_> {
44+
fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
45+
self.cfg.configure(node)
46+
}
47+
48+
fn configure_annotatable(&mut self, annotatable: Annotatable) -> Annotatable {
49+
// Since the item itself has already been configured by the InvocationCollector,
50+
// we know that fold result vector will contain exactly one element
51+
match annotatable {
52+
Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()),
53+
Annotatable::TraitItem(item) => {
54+
Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap())
55+
}
56+
Annotatable::ImplItem(item) => {
57+
Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap())
58+
}
59+
Annotatable::ForeignItem(item) => {
60+
Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap())
61+
}
62+
Annotatable::Stmt(stmt) => {
63+
Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap()))
64+
}
65+
Annotatable::Expr(mut expr) => Annotatable::Expr({
66+
self.visit_expr(&mut expr);
67+
expr
68+
}),
69+
Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()),
70+
Annotatable::Field(field) => {
71+
Annotatable::Field(self.flat_map_field(field).pop().unwrap())
72+
}
73+
Annotatable::FieldPat(fp) => {
74+
Annotatable::FieldPat(self.flat_map_field_pattern(fp).pop().unwrap())
75+
}
76+
Annotatable::GenericParam(param) => {
77+
Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap())
78+
}
79+
Annotatable::Param(param) => {
80+
Annotatable::Param(self.flat_map_param(param).pop().unwrap())
81+
}
82+
Annotatable::StructField(sf) => {
83+
Annotatable::StructField(self.flat_map_struct_field(sf).pop().unwrap())
84+
}
85+
Annotatable::Variant(v) => {
86+
Annotatable::Variant(self.flat_map_variant(v).pop().unwrap())
87+
}
88+
}
89+
}
90+
}
91+
92+
impl MutVisitor for CfgEval<'_> {
93+
fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
94+
self.cfg.configure_expr(expr);
95+
mut_visit::noop_visit_expr(expr, self);
96+
}
97+
98+
fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
99+
let mut expr = configure!(self, expr);
100+
mut_visit::noop_visit_expr(&mut expr, self);
101+
Some(expr)
102+
}
103+
104+
fn flat_map_generic_param(
105+
&mut self,
106+
param: ast::GenericParam,
107+
) -> SmallVec<[ast::GenericParam; 1]> {
108+
mut_visit::noop_flat_map_generic_param(configure!(self, param), self)
109+
}
110+
111+
fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
112+
mut_visit::noop_flat_map_stmt(configure!(self, stmt), self)
113+
}
114+
115+
fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
116+
mut_visit::noop_flat_map_item(configure!(self, item), self)
117+
}
118+
119+
fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
120+
mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
121+
}
122+
123+
fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
124+
mut_visit::noop_flat_map_assoc_item(configure!(self, item), self)
125+
}
126+
127+
fn flat_map_foreign_item(
128+
&mut self,
129+
foreign_item: P<ast::ForeignItem>,
130+
) -> SmallVec<[P<ast::ForeignItem>; 1]> {
131+
mut_visit::noop_flat_map_foreign_item(configure!(self, foreign_item), self)
132+
}
133+
134+
fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
135+
mut_visit::noop_flat_map_arm(configure!(self, arm), self)
136+
}
137+
138+
fn flat_map_field(&mut self, field: ast::Field) -> SmallVec<[ast::Field; 1]> {
139+
mut_visit::noop_flat_map_field(configure!(self, field), self)
140+
}
141+
142+
fn flat_map_field_pattern(&mut self, fp: ast::FieldPat) -> SmallVec<[ast::FieldPat; 1]> {
143+
mut_visit::noop_flat_map_field_pattern(configure!(self, fp), self)
144+
}
145+
146+
fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> {
147+
mut_visit::noop_flat_map_param(configure!(self, p), self)
148+
}
149+
150+
fn flat_map_struct_field(&mut self, sf: ast::StructField) -> SmallVec<[ast::StructField; 1]> {
151+
mut_visit::noop_flat_map_struct_field(configure!(self, sf), self)
152+
}
153+
154+
fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
155+
mut_visit::noop_flat_map_variant(configure!(self, variant), self)
156+
}
157+
}

compiler/rustc_builtin_macros/src/derive.rs

+3-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
use crate::cfg_eval::cfg_eval;
2+
13
use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
24
use rustc_errors::{struct_span_err, Applicability};
35
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
4-
use rustc_expand::config::StripUnconfigured;
56
use rustc_feature::AttributeTemplate;
67
use rustc_parse::validate_attr;
78
use rustc_session::Session;
@@ -51,26 +52,7 @@ impl MultiItemModifier for Expander {
5152

5253
// FIXME: Try to cache intermediate results to avoid collecting same paths multiple times.
5354
match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) {
54-
Ok(()) => {
55-
let mut visitor =
56-
StripUnconfigured { sess, features: ecx.ecfg.features, modified: false };
57-
let mut item = visitor.fully_configure(item);
58-
if visitor.modified {
59-
// Erase the tokens if cfg-stripping modified the item
60-
// This will cause us to synthesize fake tokens
61-
// when `nt_to_tokenstream` is called on this item.
62-
match &mut item {
63-
Annotatable::Item(item) => item,
64-
Annotatable::Stmt(stmt) => match &mut stmt.kind {
65-
StmtKind::Item(item) => item,
66-
_ => unreachable!(),
67-
},
68-
_ => unreachable!(),
69-
}
70-
.tokens = None;
71-
}
72-
ExpandResult::Ready(vec![item])
73-
}
55+
Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)),
7456
Err(Indeterminate) => ExpandResult::Retry(item),
7557
}
7658
}

compiler/rustc_builtin_macros/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod asm;
2424
mod assert;
2525
mod cfg;
2626
mod cfg_accessible;
27+
mod cfg_eval;
2728
mod compile_error;
2829
mod concat;
2930
mod concat_idents;
@@ -89,6 +90,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
8990
register_attr! {
9091
bench: test::expand_bench,
9192
cfg_accessible: cfg_accessible::Expander,
93+
cfg_eval: cfg_eval::expand,
9294
derive: derive::Expander,
9395
global_allocator: global_allocator::expand,
9496
test: test::expand_test,

0 commit comments

Comments
 (0)