Skip to content

Commit 5b8512e

Browse files
Parse cfg_attr and cfg specially
This allows us to simplify expansion of cfg_attr in `expand_cfg_attr()`, and also fixes a bunch of bugs like expansion of `doc = macro!()` inside `cfg_attr`.
1 parent 49094db commit 5b8512e

File tree

97 files changed

+1954
-1343
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+1954
-1343
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cfg/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ tracing.workspace = true
1919
# locals deps
2020
tt = { workspace = true, optional = true }
2121
syntax = { workspace = true, optional = true }
22-
span = { path = "../span", version = "0.0", optional = true }
2322
intern.workspace = true
2423

2524
[dev-dependencies]
@@ -36,7 +35,7 @@ cfg = { path = ".", default-features = false, features = ["tt"] }
3635

3736
[features]
3837
default = []
39-
syntax = ["dep:syntax", "dep:span"]
38+
syntax = ["dep:syntax"]
4039
tt = ["dep:tt"]
4140
in-rust-tree = []
4241

crates/cfg/src/cfg_expr.rs

Lines changed: 48 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,54 @@ impl CfgExpr {
106106
}
107107

108108
#[cfg(feature = "syntax")]
109-
pub fn parse_from_ast(
110-
ast: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>,
111-
) -> CfgExpr {
112-
next_cfg_expr_from_ast(ast).unwrap_or(CfgExpr::Invalid)
109+
pub fn parse_from_ast(ast: syntax::ast::CfgPredicate) -> CfgExpr {
110+
use intern::sym;
111+
use syntax::ast::{self, AstToken};
112+
113+
match ast {
114+
ast::CfgPredicate::CfgAtom(atom) => {
115+
let atom = match atom.key() {
116+
Some(ast::CfgAtomKey::True) => CfgAtom::Flag(sym::true_),
117+
Some(ast::CfgAtomKey::False) => CfgAtom::Flag(sym::false_),
118+
Some(ast::CfgAtomKey::Ident(key)) => {
119+
let key = Symbol::intern(key.text());
120+
match atom.string_token().and_then(ast::String::cast) {
121+
Some(value) => {
122+
if let Ok(value) = value.value() {
123+
CfgAtom::KeyValue { key, value: Symbol::intern(&value) }
124+
} else {
125+
return CfgExpr::Invalid;
126+
}
127+
}
128+
None => CfgAtom::Flag(key),
129+
}
130+
}
131+
None => return CfgExpr::Invalid,
132+
};
133+
CfgExpr::Atom(atom)
134+
}
135+
ast::CfgPredicate::CfgComposite(composite) => {
136+
let Some(keyword) = composite.keyword() else {
137+
return CfgExpr::Invalid;
138+
};
139+
match keyword.text() {
140+
"all" => CfgExpr::All(
141+
composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(),
142+
),
143+
"any" => CfgExpr::Any(
144+
composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(),
145+
),
146+
"not" => {
147+
let mut inner = composite.cfg_predicates();
148+
let (Some(inner), None) = (inner.next(), inner.next()) else {
149+
return CfgExpr::Invalid;
150+
};
151+
CfgExpr::Not(Box::new(CfgExpr::parse_from_ast(inner)))
152+
}
153+
_ => CfgExpr::Invalid,
154+
}
155+
}
156+
}
113157
}
114158

115159
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
@@ -128,65 +172,6 @@ impl CfgExpr {
128172
}
129173
}
130174

131-
#[cfg(feature = "syntax")]
132-
fn next_cfg_expr_from_ast(
133-
it: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>,
134-
) -> Option<CfgExpr> {
135-
use intern::sym;
136-
use syntax::{NodeOrToken, SyntaxKind, T, ast};
137-
138-
let name = match it.next() {
139-
None => return None,
140-
Some(NodeOrToken::Token(ident)) if ident.kind().is_any_identifier() => {
141-
Symbol::intern(ident.text())
142-
}
143-
Some(_) => return Some(CfgExpr::Invalid),
144-
};
145-
146-
let ret = match it.peek() {
147-
Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
148-
it.next();
149-
if let Some(NodeOrToken::Token(literal)) = it.peek()
150-
&& matches!(literal.kind(), SyntaxKind::STRING)
151-
{
152-
let dummy_span = span::Span {
153-
range: span::TextRange::empty(span::TextSize::new(0)),
154-
anchor: span::SpanAnchor {
155-
file_id: span::EditionedFileId::from_raw(0),
156-
ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER,
157-
},
158-
ctx: span::SyntaxContext::root(span::Edition::Edition2015),
159-
};
160-
let literal =
161-
Symbol::intern(tt::token_to_literal(literal.text(), dummy_span).text());
162-
it.next();
163-
CfgAtom::KeyValue { key: name, value: literal.clone() }.into()
164-
} else {
165-
return Some(CfgExpr::Invalid);
166-
}
167-
}
168-
Some(NodeOrToken::Node(subtree)) => {
169-
let mut subtree_iter = ast::TokenTreeChildren::new(subtree).peekable();
170-
it.next();
171-
let mut subs = std::iter::from_fn(|| next_cfg_expr_from_ast(&mut subtree_iter));
172-
match name {
173-
s if s == sym::all => CfgExpr::All(subs.collect()),
174-
s if s == sym::any => CfgExpr::Any(subs.collect()),
175-
s if s == sym::not => {
176-
CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
177-
}
178-
_ => CfgExpr::Invalid,
179-
}
180-
}
181-
_ => CfgAtom::Flag(name).into(),
182-
};
183-
184-
// Eat comma separator
185-
while it.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {}
186-
187-
Some(ret)
188-
}
189-
190175
#[cfg(feature = "tt")]
191176
fn next_cfg_expr(it: &mut tt::iter::TtIter<'_>) -> Option<CfgExpr> {
192177
use intern::sym;

crates/cfg/src/tests.rs

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
use arbitrary::{Arbitrary, Unstructured};
22
use expect_test::{Expect, expect};
33
use intern::Symbol;
4-
use syntax::{
5-
AstNode, Edition,
6-
ast::{self, TokenTreeChildren},
7-
};
4+
use syntax::{AstNode, Edition, ast};
85
use syntax_bridge::{
96
DocCommentDesugarMode,
107
dummy_test_span_utils::{DUMMY, DummyTestSpanMap},
@@ -14,50 +11,50 @@ use syntax_bridge::{
1411
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
1512

1613
#[track_caller]
17-
fn parse_ast_cfg(tt: &ast::TokenTree) -> CfgExpr {
18-
CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable())
14+
fn parse_ast_cfg(pred: ast::CfgPredicate) -> CfgExpr {
15+
CfgExpr::parse_from_ast(pred)
1916
}
2017

2118
#[track_caller]
2219
fn assert_parse_result(input: &str, expected: CfgExpr) {
23-
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
24-
let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
20+
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).syntax_node();
21+
let pred_ast = source_file.descendants().find_map(ast::CfgPredicate::cast).unwrap();
2522
let tt = syntax_node_to_token_tree(
26-
tt_ast.syntax(),
23+
pred_ast.syntax(),
2724
DummyTestSpanMap,
2825
DUMMY,
2926
DocCommentDesugarMode::ProcMacro,
3027
);
3128
let cfg = CfgExpr::parse(&tt);
3229
assert_eq!(cfg, expected);
33-
let cfg = parse_ast_cfg(&tt_ast);
30+
let cfg = parse_ast_cfg(pred_ast);
3431
assert_eq!(cfg, expected);
3532
}
3633

3734
#[track_caller]
3835
fn check_dnf(input: &str, expect: Expect) {
3936
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
40-
let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
37+
let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
4138
let tt = syntax_node_to_token_tree(
42-
tt_ast.syntax(),
39+
pred_ast.syntax(),
4340
DummyTestSpanMap,
4441
DUMMY,
4542
DocCommentDesugarMode::ProcMacro,
4643
);
4744
let cfg = CfgExpr::parse(&tt);
4845
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
4946
expect.assert_eq(&actual);
50-
let cfg = parse_ast_cfg(&tt_ast);
47+
let cfg = parse_ast_cfg(pred_ast);
5148
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
5249
expect.assert_eq(&actual);
5350
}
5451

5552
#[track_caller]
5653
fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
5754
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
58-
let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
55+
let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
5956
let tt = syntax_node_to_token_tree(
60-
tt_ast.syntax(),
57+
pred_ast.syntax(),
6158
DummyTestSpanMap,
6259
DUMMY,
6360
DocCommentDesugarMode::ProcMacro,
@@ -66,7 +63,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
6663
let dnf = DnfExpr::new(&cfg);
6764
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
6865
expect.assert_eq(&why_inactive);
69-
let cfg = parse_ast_cfg(&tt_ast);
66+
let cfg = parse_ast_cfg(pred_ast);
7067
let dnf = DnfExpr::new(&cfg);
7168
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
7269
expect.assert_eq(&why_inactive);
@@ -75,9 +72,9 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
7572
#[track_caller]
7673
fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
7774
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
78-
let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
75+
let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
7976
let tt = syntax_node_to_token_tree(
80-
tt_ast.syntax(),
77+
pred_ast.syntax(),
8178
DummyTestSpanMap,
8279
DUMMY,
8380
DocCommentDesugarMode::ProcMacro,
@@ -86,7 +83,7 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
8683
let dnf = DnfExpr::new(&cfg);
8784
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
8885
assert_eq!(hints, expected_hints);
89-
let cfg = parse_ast_cfg(&tt_ast);
86+
let cfg = parse_ast_cfg(pred_ast);
9087
let dnf = DnfExpr::new(&cfg);
9188
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
9289
assert_eq!(hints, expected_hints);
@@ -119,20 +116,6 @@ fn test_cfg_expr_parser() {
119116
.into_boxed_slice(),
120117
),
121118
);
122-
123-
assert_parse_result(
124-
r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
125-
CfgExpr::Any(
126-
vec![
127-
CfgExpr::Not(Box::new(CfgExpr::Invalid)),
128-
CfgExpr::All(Box::new([])),
129-
CfgExpr::Invalid,
130-
CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }
131-
.into(),
132-
]
133-
.into_boxed_slice(),
134-
),
135-
);
136119
}
137120

138121
#[test]

0 commit comments

Comments
 (0)