Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 79f2651

Browse files
committed
Add cfg_attr and cleanup code
1 parent f45b080 commit 79f2651

File tree

8 files changed

+233
-198
lines changed

8 files changed

+233
-198
lines changed

crates/cfg/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ rustc-hash.workspace = true
1616

1717
# locals deps
1818
tt.workspace = true
19+
syntax.workspace = true
1920

2021
[dev-dependencies]
2122
expect-test = "1.4.1"
@@ -28,7 +29,6 @@ derive_arbitrary = "1.3.2"
2829

2930
# local deps
3031
mbe.workspace = true
31-
syntax.workspace = true
3232

3333
[lints]
34-
workspace = true
34+
workspace = true

crates/cfg/src/cfg_attr.rs

Lines changed: 0 additions & 70 deletions
This file was deleted.

crates/cfg/src/cfg_expr.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
//!
33
//! See: <https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation>
44
5-
use std::{fmt, slice::Iter as SliceIter};
5+
use std::{fmt, iter::Peekable, slice::Iter as SliceIter};
66

7+
use syntax::{
8+
ast::{self, Meta},
9+
NodeOrToken,
10+
};
711
use tt::SmolStr;
812

913
/// A simple configuration value passed in from the outside.
@@ -47,6 +51,12 @@ impl CfgExpr {
4751
pub fn parse<S>(tt: &tt::Subtree<S>) -> CfgExpr {
4852
next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
4953
}
54+
/// Parses a `cfg` attribute from the meta
55+
pub fn parse_from_attr_meta(meta: Meta) -> Option<CfgExpr> {
56+
let tt = meta.token_tree()?;
57+
let mut iter = tt.token_trees_and_tokens().skip(1).peekable();
58+
next_cfg_expr_from_syntax(&mut iter)
59+
}
5060
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
5161
pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
5262
match self {
@@ -62,8 +72,71 @@ impl CfgExpr {
6272
}
6373
}
6474
}
75+
fn next_cfg_expr_from_syntax<I>(iter: &mut Peekable<I>) -> Option<CfgExpr>
76+
where
77+
I: Iterator<Item = NodeOrToken<ast::TokenTree, syntax::SyntaxToken>>,
78+
{
79+
let name = match iter.next() {
80+
None => return None,
81+
Some(NodeOrToken::Token(element)) => match element.kind() {
82+
syntax::T![ident] => SmolStr::new(element.text()),
83+
_ => return Some(CfgExpr::Invalid),
84+
},
85+
Some(_) => return Some(CfgExpr::Invalid),
86+
};
87+
let result = match name.as_str() {
88+
"all" | "any" | "not" => {
89+
let mut preds = Vec::new();
90+
let Some(NodeOrToken::Node(tree)) = iter.next() else {
91+
return Some(CfgExpr::Invalid);
92+
};
93+
let mut tree_iter = tree.token_trees_and_tokens().skip(1).peekable();
94+
while tree_iter
95+
.peek()
96+
.filter(
97+
|element| matches!(element, NodeOrToken::Token(token) if (token.kind() != syntax::T![')'])),
98+
)
99+
.is_some()
100+
{
101+
let pred = next_cfg_expr_from_syntax(&mut tree_iter);
102+
if let Some(pred) = pred {
103+
preds.push(pred);
104+
}
105+
}
106+
let group = match name.as_str() {
107+
"all" => CfgExpr::All(preds),
108+
"any" => CfgExpr::Any(preds),
109+
"not" => CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))),
110+
_ => unreachable!(),
111+
};
112+
Some(group)
113+
}
114+
_ => match iter.peek() {
115+
Some(NodeOrToken::Token(element)) if (element.kind() == syntax::T![=]) => {
116+
iter.next();
117+
match iter.next() {
118+
Some(NodeOrToken::Token(value_token))
119+
if (value_token.kind() == syntax::SyntaxKind::STRING) =>
120+
{
121+
let value = value_token.text();
122+
let value = SmolStr::new(value.trim_matches('"'));
123+
Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name, value }))
124+
}
125+
_ => None,
126+
}
127+
}
128+
_ => Some(CfgExpr::Atom(CfgAtom::Flag(name))),
129+
},
130+
};
131+
if let Some(NodeOrToken::Token(element)) = iter.peek() {
132+
if element.kind() == syntax::T![,] {
133+
iter.next();
134+
}
135+
}
136+
result
137+
}
65138

66-
pub(crate) fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
139+
fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
67140
let name = match it.next() {
68141
None => return None,
69142
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),

crates/cfg/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
33
#![warn(rust_2018_idioms, unused_lifetimes)]
44

5-
mod cfg_attr;
6-
pub(crate) mod cfg_expr;
5+
mod cfg_expr;
76
mod dnf;
87
#[cfg(test)]
98
mod tests;
@@ -13,7 +12,6 @@ use std::fmt;
1312
use rustc_hash::FxHashSet;
1413
use tt::SmolStr;
1514

16-
pub use cfg_attr::CfgAttr;
1715
pub use cfg_expr::{CfgAtom, CfgExpr};
1816
pub use dnf::DnfExpr;
1917

crates/cfg/src/tests.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use arbitrary::{Arbitrary, Unstructured};
22
use expect_test::{expect, Expect};
33
use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
4-
use syntax::{ast, AstNode};
4+
use syntax::{
5+
ast::{self, Attr},
6+
AstNode, SourceFile,
7+
};
58

69
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
710

@@ -12,6 +15,22 @@ fn assert_parse_result(input: &str, expected: CfgExpr) {
1215
let cfg = CfgExpr::parse(&tt);
1316
assert_eq!(cfg, expected);
1417
}
18+
fn check_dnf_from_syntax(input: &str, expect: Expect) {
19+
let parse = SourceFile::parse(input);
20+
let node = match parse.tree().syntax().descendants().find_map(Attr::cast) {
21+
Some(it) => it,
22+
None => {
23+
let node = std::any::type_name::<Attr>();
24+
panic!("Failed to make ast node `{node}` from text {input}")
25+
}
26+
};
27+
let node = node.clone_subtree();
28+
assert_eq!(node.syntax().text_range().start(), 0.into());
29+
30+
let cfg = CfgExpr::parse_from_attr_meta(node.meta().unwrap()).unwrap();
31+
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
32+
expect.assert_eq(&actual);
33+
}
1534

1635
fn check_dnf(input: &str, expect: Expect) {
1736
let source_file = ast::SourceFile::parse(input).ok().unwrap();
@@ -86,6 +105,11 @@ fn smoke() {
86105

87106
check_dnf("#![cfg(not(a))]", expect![[r#"#![cfg(not(a))]"#]]);
88107
}
108+
#[test]
109+
fn cfg_from_attr() {
110+
check_dnf_from_syntax(r#"#[cfg(test)]"#, expect![[r#"#![cfg(test)]"#]]);
111+
check_dnf_from_syntax(r#"#[cfg(not(never))]"#, expect![[r#"#![cfg(not(never))]"#]]);
112+
}
89113

90114
#[test]
91115
fn distribute() {

0 commit comments

Comments
 (0)