Skip to content

Commit 2eaba02

Browse files
authored
Merge pull request #20265 from ChayimFriedman2/cfg-select
feat: Support `cfg_select!`
2 parents e49e107 + 8b6559b commit 2eaba02

File tree

5 files changed

+139
-1
lines changed

5 files changed

+139
-1
lines changed

src/tools/rust-analyzer/crates/cfg/src/cfg_expr.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ impl CfgExpr {
6868
next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid)
6969
}
7070

71+
#[cfg(feature = "tt")]
72+
pub fn parse_from_iter<S: Copy>(tt: &mut tt::iter::TtIter<'_, S>) -> CfgExpr {
73+
next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid)
74+
}
75+
7176
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
7277
pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
7378
match self {
@@ -96,7 +101,14 @@ fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> {
96101
};
97102

98103
let ret = match it.peek() {
99-
Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
104+
Some(TtElement::Leaf(tt::Leaf::Punct(punct)))
105+
// Don't consume on e.g. `=>`.
106+
if punct.char == '='
107+
&& (punct.spacing == tt::Spacing::Alone
108+
|| it.remaining().flat_tokens().get(1).is_none_or(|peek2| {
109+
!matches!(peek2, tt::TokenTree::Leaf(tt::Leaf::Punct(_)))
110+
})) =>
111+
{
100112
match it.remaining().flat_tokens().get(1) {
101113
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
102114
it.next();

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,3 +550,51 @@ fn main() { "\"hello\""; }
550550
"##]],
551551
);
552552
}
553+
554+
#[test]
555+
fn cfg_select() {
556+
check(
557+
r#"
558+
#[rustc_builtin_macro]
559+
pub macro cfg_select($($tt:tt)*) {}
560+
561+
cfg_select! {
562+
false => { fn false_1() {} }
563+
any(false, true) => { fn true_1() {} }
564+
}
565+
566+
cfg_select! {
567+
false => { fn false_2() {} }
568+
_ => { fn true_2() {} }
569+
}
570+
571+
cfg_select! {
572+
false => { fn false_3() {} }
573+
}
574+
575+
cfg_select! {
576+
false
577+
}
578+
579+
cfg_select! {
580+
false =>
581+
}
582+
583+
"#,
584+
expect![[r#"
585+
#[rustc_builtin_macro]
586+
pub macro cfg_select($($tt:tt)*) {}
587+
588+
fn true_1() {}
589+
590+
fn true_2() {}
591+
592+
/* error: none of the predicates in this `cfg_select` evaluated to true */
593+
594+
/* error: expected `=>` after cfg expression */
595+
596+
/* error: expected a token tree after `=>` */
597+
598+
"#]],
599+
);
600+
}

src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ register_builtin! {
127127
(asm, Asm) => asm_expand,
128128
(global_asm, GlobalAsm) => global_asm_expand,
129129
(naked_asm, NakedAsm) => naked_asm_expand,
130+
(cfg_select, CfgSelect) => cfg_select_expand,
130131
(cfg, Cfg) => cfg_expand,
131132
(core_panic, CorePanic) => panic_expand,
132133
(std_panic, StdPanic) => panic_expand,
@@ -355,6 +356,71 @@ fn naked_asm_expand(
355356
ExpandResult::ok(expanded)
356357
}
357358

359+
fn cfg_select_expand(
360+
db: &dyn ExpandDatabase,
361+
id: MacroCallId,
362+
tt: &tt::TopSubtree,
363+
span: Span,
364+
) -> ExpandResult<tt::TopSubtree> {
365+
let loc = db.lookup_intern_macro_call(id);
366+
let cfg_options = loc.krate.cfg_options(db);
367+
368+
let mut iter = tt.iter();
369+
let mut expand_to = None;
370+
while let Some(next) = iter.peek() {
371+
let active = if let tt::TtElement::Leaf(tt::Leaf::Ident(ident)) = next
372+
&& ident.sym == sym::underscore
373+
{
374+
iter.next();
375+
true
376+
} else {
377+
cfg_options.check(&CfgExpr::parse_from_iter(&mut iter)) != Some(false)
378+
};
379+
match iter.expect_glued_punct() {
380+
Ok(it) if it.len() == 2 && it[0].char == '=' && it[1].char == '>' => {}
381+
_ => {
382+
let err_span = iter.peek().map(|it| it.first_span()).unwrap_or(span);
383+
return ExpandResult::new(
384+
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
385+
ExpandError::other(err_span, "expected `=>` after cfg expression"),
386+
);
387+
}
388+
}
389+
let expand_to_if_active = match iter.next() {
390+
Some(tt::TtElement::Subtree(_, tt)) => tt.remaining(),
391+
_ => {
392+
let err_span = iter.peek().map(|it| it.first_span()).unwrap_or(span);
393+
return ExpandResult::new(
394+
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
395+
ExpandError::other(err_span, "expected a token tree after `=>`"),
396+
);
397+
}
398+
};
399+
400+
if expand_to.is_none() && active {
401+
expand_to = Some(expand_to_if_active);
402+
}
403+
}
404+
match expand_to {
405+
Some(expand_to) => {
406+
let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter {
407+
kind: tt::DelimiterKind::Invisible,
408+
open: span,
409+
close: span,
410+
});
411+
builder.extend_with_tt(expand_to);
412+
ExpandResult::ok(builder.build())
413+
}
414+
None => ExpandResult::new(
415+
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
416+
ExpandError::other(
417+
span,
418+
"none of the predicates in this `cfg_select` evaluated to true",
419+
),
420+
),
421+
}
422+
}
423+
358424
fn cfg_expand(
359425
db: &dyn ExpandDatabase,
360426
id: MacroCallId,

src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ define_symbols! {
156156
cfg_attr,
157157
cfg_eval,
158158
cfg,
159+
cfg_select,
159160
char,
160161
clone,
161162
Clone,

src/tools/rust-analyzer/crates/tt/src/iter.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,17 @@ pub enum TtElement<'a, S> {
217217
Subtree(&'a Subtree<S>, TtIter<'a, S>),
218218
}
219219

220+
impl<S: Copy + fmt::Debug> fmt::Debug for TtElement<'_, S> {
221+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222+
match self {
223+
Self::Leaf(leaf) => f.debug_tuple("Leaf").field(leaf).finish(),
224+
Self::Subtree(subtree, inner) => {
225+
f.debug_tuple("Subtree").field(subtree).field(inner).finish()
226+
}
227+
}
228+
}
229+
}
230+
220231
impl<S: Copy> TtElement<'_, S> {
221232
#[inline]
222233
pub fn first_span(&self) -> S {

0 commit comments

Comments
 (0)