mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-14 09:46:00 +00:00
Support cfg_select!
This commit is contained in:
parent
ed193af369
commit
b1914e420f
5 changed files with 139 additions and 1 deletions
|
|
@ -68,6 +68,11 @@ impl CfgExpr {
|
||||||
next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid)
|
next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tt")]
|
||||||
|
pub fn parse_from_iter<S: Copy>(tt: &mut tt::iter::TtIter<'_, S>) -> CfgExpr {
|
||||||
|
next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
|
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
|
||||||
pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
|
pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -96,7 +101,14 @@ fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let ret = match it.peek() {
|
let ret = match it.peek() {
|
||||||
Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
|
Some(TtElement::Leaf(tt::Leaf::Punct(punct)))
|
||||||
|
// Don't consume on e.g. `=>`.
|
||||||
|
if punct.char == '='
|
||||||
|
&& (punct.spacing == tt::Spacing::Alone
|
||||||
|
|| it.remaining().flat_tokens().get(1).is_none_or(|peek2| {
|
||||||
|
!matches!(peek2, tt::TokenTree::Leaf(tt::Leaf::Punct(_)))
|
||||||
|
})) =>
|
||||||
|
{
|
||||||
match it.remaining().flat_tokens().get(1) {
|
match it.remaining().flat_tokens().get(1) {
|
||||||
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
|
||||||
it.next();
|
it.next();
|
||||||
|
|
|
||||||
|
|
@ -550,3 +550,51 @@ fn main() { "\"hello\""; }
|
||||||
"##]],
|
"##]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cfg_select() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
pub macro cfg_select($($tt:tt)*) {}
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
false => { fn false_1() {} }
|
||||||
|
any(false, true) => { fn true_1() {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
false => { fn false_2() {} }
|
||||||
|
_ => { fn true_2() {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
false => { fn false_3() {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_select! {
|
||||||
|
false =>
|
||||||
|
}
|
||||||
|
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
pub macro cfg_select($($tt:tt)*) {}
|
||||||
|
|
||||||
|
fn true_1() {}
|
||||||
|
|
||||||
|
fn true_2() {}
|
||||||
|
|
||||||
|
/* error: none of the predicates in this `cfg_select` evaluated to true */
|
||||||
|
|
||||||
|
/* error: expected `=>` after cfg expression */
|
||||||
|
|
||||||
|
/* error: expected a token tree after `=>` */
|
||||||
|
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ register_builtin! {
|
||||||
(asm, Asm) => asm_expand,
|
(asm, Asm) => asm_expand,
|
||||||
(global_asm, GlobalAsm) => global_asm_expand,
|
(global_asm, GlobalAsm) => global_asm_expand,
|
||||||
(naked_asm, NakedAsm) => naked_asm_expand,
|
(naked_asm, NakedAsm) => naked_asm_expand,
|
||||||
|
(cfg_select, CfgSelect) => cfg_select_expand,
|
||||||
(cfg, Cfg) => cfg_expand,
|
(cfg, Cfg) => cfg_expand,
|
||||||
(core_panic, CorePanic) => panic_expand,
|
(core_panic, CorePanic) => panic_expand,
|
||||||
(std_panic, StdPanic) => panic_expand,
|
(std_panic, StdPanic) => panic_expand,
|
||||||
|
|
@ -355,6 +356,71 @@ fn naked_asm_expand(
|
||||||
ExpandResult::ok(expanded)
|
ExpandResult::ok(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cfg_select_expand(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
id: MacroCallId,
|
||||||
|
tt: &tt::TopSubtree,
|
||||||
|
span: Span,
|
||||||
|
) -> ExpandResult<tt::TopSubtree> {
|
||||||
|
let loc = db.lookup_intern_macro_call(id);
|
||||||
|
let cfg_options = loc.krate.cfg_options(db);
|
||||||
|
|
||||||
|
let mut iter = tt.iter();
|
||||||
|
let mut expand_to = None;
|
||||||
|
while let Some(next) = iter.peek() {
|
||||||
|
let active = if let tt::TtElement::Leaf(tt::Leaf::Ident(ident)) = next
|
||||||
|
&& ident.sym == sym::underscore
|
||||||
|
{
|
||||||
|
iter.next();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
cfg_options.check(&CfgExpr::parse_from_iter(&mut iter)) != Some(false)
|
||||||
|
};
|
||||||
|
match iter.expect_glued_punct() {
|
||||||
|
Ok(it) if it.len() == 2 && it[0].char == '=' && it[1].char == '>' => {}
|
||||||
|
_ => {
|
||||||
|
let err_span = iter.peek().map(|it| it.first_span()).unwrap_or(span);
|
||||||
|
return ExpandResult::new(
|
||||||
|
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
|
||||||
|
ExpandError::other(err_span, "expected `=>` after cfg expression"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let expand_to_if_active = match iter.next() {
|
||||||
|
Some(tt::TtElement::Subtree(_, tt)) => tt.remaining(),
|
||||||
|
_ => {
|
||||||
|
let err_span = iter.peek().map(|it| it.first_span()).unwrap_or(span);
|
||||||
|
return ExpandResult::new(
|
||||||
|
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
|
||||||
|
ExpandError::other(err_span, "expected a token tree after `=>`"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if expand_to.is_none() && active {
|
||||||
|
expand_to = Some(expand_to_if_active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match expand_to {
|
||||||
|
Some(expand_to) => {
|
||||||
|
let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter {
|
||||||
|
kind: tt::DelimiterKind::Invisible,
|
||||||
|
open: span,
|
||||||
|
close: span,
|
||||||
|
});
|
||||||
|
builder.extend_with_tt(expand_to);
|
||||||
|
ExpandResult::ok(builder.build())
|
||||||
|
}
|
||||||
|
None => ExpandResult::new(
|
||||||
|
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
|
||||||
|
ExpandError::other(
|
||||||
|
span,
|
||||||
|
"none of the predicates in this `cfg_select` evaluated to true",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn cfg_expand(
|
fn cfg_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ define_symbols! {
|
||||||
cfg_attr,
|
cfg_attr,
|
||||||
cfg_eval,
|
cfg_eval,
|
||||||
cfg,
|
cfg,
|
||||||
|
cfg_select,
|
||||||
char,
|
char,
|
||||||
clone,
|
clone,
|
||||||
Clone,
|
Clone,
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,17 @@ pub enum TtElement<'a, S> {
|
||||||
Subtree(&'a Subtree<S>, TtIter<'a, S>),
|
Subtree(&'a Subtree<S>, TtIter<'a, S>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S: Copy + fmt::Debug> fmt::Debug for TtElement<'_, S> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Leaf(leaf) => f.debug_tuple("Leaf").field(leaf).finish(),
|
||||||
|
Self::Subtree(subtree, inner) => {
|
||||||
|
f.debug_tuple("Subtree").field(subtree).field(inner).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: Copy> TtElement<'_, S> {
|
impl<S: Copy> TtElement<'_, S> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_span(&self) -> S {
|
pub fn first_span(&self) -> S {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue