Skip to content

Commit b5ad702

Browse files
Censor cfg_attr for attribute macros
This is not documented (and I discovered that from experimenting and looking at the compiler's source code), but cfg_attrs *on the same level* as the attribute macro should be processed before it is expanded. cfg_attrs *below* should not (and this is contrary to what happens with derive macros, where both should be processed).
1 parent a7e9739 commit b5ad702

File tree

3 files changed

+96
-24
lines changed

3 files changed

+96
-24
lines changed

src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! in-memory macros.
66
use expect_test::expect;
77

8-
use crate::macro_expansion_tests::check;
8+
use crate::macro_expansion_tests::{check, check_errors};
99

1010
#[test]
1111
fn attribute_macro_attr_censoring() {
@@ -216,3 +216,21 @@ struct S;
216216
#[doc = "doc attr"] struct S;"##]],
217217
);
218218
}
219+
220+
#[test]
221+
fn cfg_evaluated_before_attr_macros() {
222+
check_errors(
223+
r#"
224+
//- proc_macros: disallow_cfg
225+
226+
use proc_macros::disallow_cfg;
227+
228+
#[disallow_cfg] #[cfg(false)] fn foo() {}
229+
// True cfg are kept.
230+
// #[disallow_cfg] #[cfg(true)] fn bar() {}
231+
#[disallow_cfg] #[cfg_attr(false, inline)] fn baz() {}
232+
#[disallow_cfg] #[cfg_attr(true, inline)] fn qux() {}
233+
"#,
234+
expect![[r#""#]],
235+
);
236+
}

src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,6 @@ pub(crate) fn process_cfg_attrs(
201201
MacroDefKind::BuiltInAttr(_, expander) => expander.is_derive(),
202202
_ => false,
203203
};
204-
if !is_derive {
205-
return None;
206-
}
207204
let mut remove = FxHashSet::default();
208205

209206
let item = ast::Item::cast(node.clone())?;
@@ -220,28 +217,43 @@ pub(crate) fn process_cfg_attrs(
220217
}
221218
}
222219
}
223-
match item {
224-
ast::Item::Struct(it) => match it.field_list()? {
225-
ast::FieldList::RecordFieldList(fields) => {
226-
process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
220+
221+
if is_derive {
222+
// Only derives get their code cfg-clean, normal attribute macros process only the cfg at their level
223+
// (cfg_attr is handled above, cfg is handled in the def map).
224+
match item {
225+
ast::Item::Struct(it) => match it.field_list()? {
226+
ast::FieldList::RecordFieldList(fields) => {
227+
process_has_attrs_with_possible_comma(
228+
db,
229+
fields.fields(),
230+
loc.krate,
231+
&mut remove,
232+
)?;
233+
}
234+
ast::FieldList::TupleFieldList(fields) => {
235+
process_has_attrs_with_possible_comma(
236+
db,
237+
fields.fields(),
238+
loc.krate,
239+
&mut remove,
240+
)?;
241+
}
242+
},
243+
ast::Item::Enum(it) => {
244+
process_enum(db, it.variant_list()?, loc.krate, &mut remove)?;
227245
}
228-
ast::FieldList::TupleFieldList(fields) => {
229-
process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
246+
ast::Item::Union(it) => {
247+
process_has_attrs_with_possible_comma(
248+
db,
249+
it.record_field_list()?.fields(),
250+
loc.krate,
251+
&mut remove,
252+
)?;
230253
}
231-
},
232-
ast::Item::Enum(it) => {
233-
process_enum(db, it.variant_list()?, loc.krate, &mut remove)?;
234-
}
235-
ast::Item::Union(it) => {
236-
process_has_attrs_with_possible_comma(
237-
db,
238-
it.record_field_list()?.fields(),
239-
loc.krate,
240-
&mut remove,
241-
)?;
254+
// FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now
255+
_ => {}
242256
}
243-
// FIXME: Implement for other items if necessary. As we do not support #[cfg_eval] yet, we do not need to implement it for now
244-
_ => {}
245257
}
246258
Some(remove)
247259
}

src/tools/rust-analyzer/crates/test-fixture/src/lib.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use hir_expand::{
1717
tt::{Leaf, TokenTree, TopSubtree, TopSubtreeBuilder, TtElement, TtIter},
1818
FileRange,
1919
};
20-
use intern::Symbol;
20+
use intern::{sym, Symbol};
2121
use rustc_hash::FxHashMap;
2222
use span::{Edition, EditionedFileId, FileId, Span};
2323
use stdx::itertools::Itertools;
@@ -511,6 +511,21 @@ pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream {
511511
disabled: false,
512512
},
513513
),
514+
(
515+
r#"
516+
#[proc_macro_attribute]
517+
pub fn disallow_cfg(_attr: TokenStream, input: TokenStream) -> TokenStream {
518+
input
519+
}
520+
"#
521+
.into(),
522+
ProcMacro {
523+
name: Symbol::intern("disallow_cfg"),
524+
kind: ProcMacroKind::Attr,
525+
expander: sync::Arc::new(DisallowCfgProcMacroExpander),
526+
disabled: false,
527+
},
528+
),
514529
])
515530
}
516531

@@ -865,3 +880,30 @@ impl ProcMacroExpander for Issue18898ProcMacroExpander {
865880
})
866881
}
867882
}
883+
884+
// Reads ident type within string quotes, for issue #17479.
885+
#[derive(Debug)]
886+
struct DisallowCfgProcMacroExpander;
887+
impl ProcMacroExpander for DisallowCfgProcMacroExpander {
888+
fn expand(
889+
&self,
890+
subtree: &TopSubtree,
891+
_: Option<&TopSubtree>,
892+
_: &Env,
893+
_: Span,
894+
_: Span,
895+
_: Span,
896+
_: Option<String>,
897+
) -> Result<TopSubtree, ProcMacroExpansionError> {
898+
for tt in subtree.token_trees().flat_tokens() {
899+
if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt {
900+
if ident.sym == sym::cfg || ident.sym == sym::cfg_attr {
901+
return Err(ProcMacroExpansionError::Panic(
902+
"cfg or cfg_attr found in DisallowCfgProcMacroExpander".to_owned(),
903+
));
904+
}
905+
}
906+
}
907+
Ok(subtree.clone())
908+
}
909+
}

0 commit comments

Comments
 (0)