aboutsummaryrefslogtreecommitdiff
path: root/crates/cfg
diff options
context:
space:
mode:
Diffstat (limited to 'crates/cfg')
-rw-r--r--crates/cfg/src/cfg_expr.rs50
-rw-r--r--crates/cfg/src/dnf.rs158
-rw-r--r--crates/cfg/src/lib.rs2
-rw-r--r--crates/cfg/src/tests.rs193
4 files changed, 195 insertions, 208 deletions
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs
index 283ff968f..42327f1e1 100644
--- a/crates/cfg/src/cfg_expr.rs
+++ b/crates/cfg/src/cfg_expr.rs
@@ -128,53 +128,3 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
128 } 128 }
129 Some(ret) 129 Some(ret)
130} 130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 use mbe::ast_to_token_tree;
137 use syntax::ast::{self, AstNode};
138
139 fn assert_parse_result(input: &str, expected: CfgExpr) {
140 let (tt, _) = {
141 let source_file = ast::SourceFile::parse(input).ok().unwrap();
142 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
143 ast_to_token_tree(&tt).unwrap()
144 };
145 let cfg = CfgExpr::parse(&tt);
146 assert_eq!(cfg, expected);
147 }
148
149 #[test]
150 fn test_cfg_expr_parser() {
151 assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into());
152 assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into());
153 assert_parse_result(
154 "#![cfg(not(foo))]",
155 CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())),
156 );
157 assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid);
158
159 // Only take the first
160 assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into());
161
162 assert_parse_result(
163 r#"#![cfg(all(foo, bar = "baz"))]"#,
164 CfgExpr::All(vec![
165 CfgAtom::Flag("foo".into()).into(),
166 CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
167 ]),
168 );
169
170 assert_parse_result(
171 r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
172 CfgExpr::Any(vec![
173 CfgExpr::Not(Box::new(CfgExpr::Invalid)),
174 CfgExpr::All(vec![]),
175 CfgExpr::Invalid,
176 CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
177 ]),
178 );
179 }
180}
diff --git a/crates/cfg/src/dnf.rs b/crates/cfg/src/dnf.rs
index 7ee7b0062..580c9a9a2 100644
--- a/crates/cfg/src/dnf.rs
+++ b/crates/cfg/src/dnf.rs
@@ -318,161 +318,3 @@ fn make_nnf(expr: CfgExpr) -> CfgExpr {
318 }, 318 },
319 } 319 }
320} 320}
321
322#[cfg(test)]
323mod test {
324 use expect_test::{expect, Expect};
325 use mbe::ast_to_token_tree;
326 use syntax::{ast, AstNode};
327
328 use super::*;
329
330 fn check_dnf(input: &str, expect: Expect) {
331 let (tt, _) = {
332 let source_file = ast::SourceFile::parse(input).ok().unwrap();
333 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
334 ast_to_token_tree(&tt).unwrap()
335 };
336 let cfg = CfgExpr::parse(&tt);
337 let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
338 expect.assert_eq(&actual);
339 }
340
341 fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
342 let (tt, _) = {
343 let source_file = ast::SourceFile::parse(input).ok().unwrap();
344 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
345 ast_to_token_tree(&tt).unwrap()
346 };
347 let cfg = CfgExpr::parse(&tt);
348 let dnf = DnfExpr::new(cfg);
349 let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
350 expect.assert_eq(&why_inactive);
351 }
352
353 #[track_caller]
354 fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
355 let (tt, _) = {
356 let source_file = ast::SourceFile::parse(input).ok().unwrap();
357 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
358 ast_to_token_tree(&tt).unwrap()
359 };
360 let cfg = CfgExpr::parse(&tt);
361 let dnf = DnfExpr::new(cfg);
362 let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
363 assert_eq!(hints, expected_hints);
364 }
365
366 #[test]
367 fn smoke() {
368 check_dnf("#![cfg(test)]", expect![[r#"#![cfg(test)]"#]]);
369 check_dnf("#![cfg(not(test))]", expect![[r#"#![cfg(not(test))]"#]]);
370 check_dnf("#![cfg(not(not(test)))]", expect![[r#"#![cfg(test)]"#]]);
371
372 check_dnf("#![cfg(all(a, b))]", expect![[r#"#![cfg(all(a, b))]"#]]);
373 check_dnf("#![cfg(any(a, b))]", expect![[r#"#![cfg(any(a, b))]"#]]);
374
375 check_dnf("#![cfg(not(a))]", expect![[r#"#![cfg(not(a))]"#]]);
376 }
377
378 #[test]
379 fn distribute() {
380 check_dnf("#![cfg(all(any(a, b), c))]", expect![[r#"#![cfg(any(all(a, c), all(b, c)))]"#]]);
381 check_dnf("#![cfg(all(c, any(a, b)))]", expect![[r#"#![cfg(any(all(c, a), all(c, b)))]"#]]);
382 check_dnf(
383 "#![cfg(all(any(a, b), any(c, d)))]",
384 expect![[r#"#![cfg(any(all(a, c), all(a, d), all(b, c), all(b, d)))]"#]],
385 );
386
387 check_dnf(
388 "#![cfg(all(any(a, b, c), any(d, e, f), g))]",
389 expect![[
390 r#"#![cfg(any(all(a, d, g), all(a, e, g), all(a, f, g), all(b, d, g), all(b, e, g), all(b, f, g), all(c, d, g), all(c, e, g), all(c, f, g)))]"#
391 ]],
392 );
393 }
394
395 #[test]
396 fn demorgan() {
397 check_dnf("#![cfg(not(all(a, b)))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]);
398 check_dnf("#![cfg(not(any(a, b)))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]);
399
400 check_dnf("#![cfg(not(all(not(a), b)))]", expect![[r#"#![cfg(any(a, not(b)))]"#]]);
401 check_dnf("#![cfg(not(any(a, not(b))))]", expect![[r#"#![cfg(all(not(a), b))]"#]]);
402 }
403
404 #[test]
405 fn nested() {
406 check_dnf(
407 "#![cfg(all(any(a), not(all(any(b)))))]",
408 expect![[r#"#![cfg(all(a, not(b)))]"#]],
409 );
410
411 check_dnf("#![cfg(any(any(a, b)))]", expect![[r#"#![cfg(any(a, b))]"#]]);
412 check_dnf("#![cfg(not(any(any(a, b))))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]);
413 check_dnf("#![cfg(all(all(a, b)))]", expect![[r#"#![cfg(all(a, b))]"#]]);
414 check_dnf("#![cfg(not(all(all(a, b))))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]);
415 }
416
417 #[test]
418 fn hints() {
419 let mut opts = CfgOptions::default();
420
421 check_enable_hints("#![cfg(test)]", &opts, &["enable test"]);
422 check_enable_hints("#![cfg(not(test))]", &opts, &[]);
423
424 check_enable_hints("#![cfg(any(a, b))]", &opts, &["enable a", "enable b"]);
425 check_enable_hints("#![cfg(any(b, a))]", &opts, &["enable b", "enable a"]);
426
427 check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]);
428
429 opts.insert_atom("test".into());
430
431 check_enable_hints("#![cfg(test)]", &opts, &[]);
432 check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]);
433 }
434
435 /// Tests that we don't suggest hints for cfgs that express an inconsistent formula.
436 #[test]
437 fn hints_impossible() {
438 let mut opts = CfgOptions::default();
439
440 check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
441
442 opts.insert_atom("test".into());
443
444 check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
445 }
446
447 #[test]
448 fn why_inactive() {
449 let mut opts = CfgOptions::default();
450 opts.insert_atom("test".into());
451 opts.insert_atom("test2".into());
452
453 check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]);
454 check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]);
455
456 check_why_inactive(
457 "#![cfg(all(not(test), not(test2)))]",
458 &opts,
459 expect![["test and test2 are enabled"]],
460 );
461 check_why_inactive("#![cfg(all(a, b))]", &opts, expect![["a and b are disabled"]]);
462 check_why_inactive(
463 "#![cfg(all(not(test), a))]",
464 &opts,
465 expect![["test is enabled and a is disabled"]],
466 );
467 check_why_inactive(
468 "#![cfg(all(not(test), test2, a))]",
469 &opts,
470 expect![["test is enabled and a is disabled"]],
471 );
472 check_why_inactive(
473 "#![cfg(all(not(test), not(test2), a))]",
474 &opts,
475 expect![["test and test2 are enabled and a is disabled"]],
476 );
477 }
478}
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index 28a40a082..d0e08cf5f 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -2,6 +2,8 @@
2 2
3mod cfg_expr; 3mod cfg_expr;
4mod dnf; 4mod dnf;
5#[cfg(test)]
6mod tests;
5 7
6use std::fmt; 8use std::fmt;
7 9
diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs
new file mode 100644
index 000000000..bd0f9ec48
--- /dev/null
+++ b/crates/cfg/src/tests.rs
@@ -0,0 +1,193 @@
1use expect_test::{expect, Expect};
2use mbe::ast_to_token_tree;
3use syntax::{ast, AstNode};
4
5use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
6
7fn assert_parse_result(input: &str, expected: CfgExpr) {
8 let (tt, _) = {
9 let source_file = ast::SourceFile::parse(input).ok().unwrap();
10 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
11 ast_to_token_tree(&tt).unwrap()
12 };
13 let cfg = CfgExpr::parse(&tt);
14 assert_eq!(cfg, expected);
15}
16
17fn check_dnf(input: &str, expect: Expect) {
18 let (tt, _) = {
19 let source_file = ast::SourceFile::parse(input).ok().unwrap();
20 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
21 ast_to_token_tree(&tt).unwrap()
22 };
23 let cfg = CfgExpr::parse(&tt);
24 let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
25 expect.assert_eq(&actual);
26}
27
28fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
29 let (tt, _) = {
30 let source_file = ast::SourceFile::parse(input).ok().unwrap();
31 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
32 ast_to_token_tree(&tt).unwrap()
33 };
34 let cfg = CfgExpr::parse(&tt);
35 let dnf = DnfExpr::new(cfg);
36 let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
37 expect.assert_eq(&why_inactive);
38}
39
40#[track_caller]
41fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
42 let (tt, _) = {
43 let source_file = ast::SourceFile::parse(input).ok().unwrap();
44 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
45 ast_to_token_tree(&tt).unwrap()
46 };
47 let cfg = CfgExpr::parse(&tt);
48 let dnf = DnfExpr::new(cfg);
49 let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
50 assert_eq!(hints, expected_hints);
51}
52
53#[test]
54fn test_cfg_expr_parser() {
55 assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into());
56 assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into());
57 assert_parse_result(
58 "#![cfg(not(foo))]",
59 CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())),
60 );
61 assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid);
62
63 // Only take the first
64 assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into());
65
66 assert_parse_result(
67 r#"#![cfg(all(foo, bar = "baz"))]"#,
68 CfgExpr::All(vec![
69 CfgAtom::Flag("foo".into()).into(),
70 CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
71 ]),
72 );
73
74 assert_parse_result(
75 r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
76 CfgExpr::Any(vec![
77 CfgExpr::Not(Box::new(CfgExpr::Invalid)),
78 CfgExpr::All(vec![]),
79 CfgExpr::Invalid,
80 CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
81 ]),
82 );
83}
84
85#[test]
86fn smoke() {
87 check_dnf("#![cfg(test)]", expect![[r#"#![cfg(test)]"#]]);
88 check_dnf("#![cfg(not(test))]", expect![[r#"#![cfg(not(test))]"#]]);
89 check_dnf("#![cfg(not(not(test)))]", expect![[r#"#![cfg(test)]"#]]);
90
91 check_dnf("#![cfg(all(a, b))]", expect![[r#"#![cfg(all(a, b))]"#]]);
92 check_dnf("#![cfg(any(a, b))]", expect![[r#"#![cfg(any(a, b))]"#]]);
93
94 check_dnf("#![cfg(not(a))]", expect![[r#"#![cfg(not(a))]"#]]);
95}
96
97#[test]
98fn distribute() {
99 check_dnf("#![cfg(all(any(a, b), c))]", expect![[r#"#![cfg(any(all(a, c), all(b, c)))]"#]]);
100 check_dnf("#![cfg(all(c, any(a, b)))]", expect![[r#"#![cfg(any(all(c, a), all(c, b)))]"#]]);
101 check_dnf(
102 "#![cfg(all(any(a, b), any(c, d)))]",
103 expect![[r#"#![cfg(any(all(a, c), all(a, d), all(b, c), all(b, d)))]"#]],
104 );
105
106 check_dnf(
107 "#![cfg(all(any(a, b, c), any(d, e, f), g))]",
108 expect![[
109 r#"#![cfg(any(all(a, d, g), all(a, e, g), all(a, f, g), all(b, d, g), all(b, e, g), all(b, f, g), all(c, d, g), all(c, e, g), all(c, f, g)))]"#
110 ]],
111 );
112}
113
114#[test]
115fn demorgan() {
116 check_dnf("#![cfg(not(all(a, b)))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]);
117 check_dnf("#![cfg(not(any(a, b)))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]);
118
119 check_dnf("#![cfg(not(all(not(a), b)))]", expect![[r#"#![cfg(any(a, not(b)))]"#]]);
120 check_dnf("#![cfg(not(any(a, not(b))))]", expect![[r#"#![cfg(all(not(a), b))]"#]]);
121}
122
123#[test]
124fn nested() {
125 check_dnf("#![cfg(all(any(a), not(all(any(b)))))]", expect![[r#"#![cfg(all(a, not(b)))]"#]]);
126
127 check_dnf("#![cfg(any(any(a, b)))]", expect![[r#"#![cfg(any(a, b))]"#]]);
128 check_dnf("#![cfg(not(any(any(a, b))))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]);
129 check_dnf("#![cfg(all(all(a, b)))]", expect![[r#"#![cfg(all(a, b))]"#]]);
130 check_dnf("#![cfg(not(all(all(a, b))))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]);
131}
132
133#[test]
134fn hints() {
135 let mut opts = CfgOptions::default();
136
137 check_enable_hints("#![cfg(test)]", &opts, &["enable test"]);
138 check_enable_hints("#![cfg(not(test))]", &opts, &[]);
139
140 check_enable_hints("#![cfg(any(a, b))]", &opts, &["enable a", "enable b"]);
141 check_enable_hints("#![cfg(any(b, a))]", &opts, &["enable b", "enable a"]);
142
143 check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]);
144
145 opts.insert_atom("test".into());
146
147 check_enable_hints("#![cfg(test)]", &opts, &[]);
148 check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]);
149}
150
151/// Tests that we don't suggest hints for cfgs that express an inconsistent formula.
152#[test]
153fn hints_impossible() {
154 let mut opts = CfgOptions::default();
155
156 check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
157
158 opts.insert_atom("test".into());
159
160 check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
161}
162
163#[test]
164fn why_inactive() {
165 let mut opts = CfgOptions::default();
166 opts.insert_atom("test".into());
167 opts.insert_atom("test2".into());
168
169 check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]);
170 check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]);
171
172 check_why_inactive(
173 "#![cfg(all(not(test), not(test2)))]",
174 &opts,
175 expect![["test and test2 are enabled"]],
176 );
177 check_why_inactive("#![cfg(all(a, b))]", &opts, expect![["a and b are disabled"]]);
178 check_why_inactive(
179 "#![cfg(all(not(test), a))]",
180 &opts,
181 expect![["test is enabled and a is disabled"]],
182 );
183 check_why_inactive(
184 "#![cfg(all(not(test), test2, a))]",
185 &opts,
186 expect![["test is enabled and a is disabled"]],
187 );
188 check_why_inactive(
189 "#![cfg(all(not(test), not(test2), a))]",
190 &opts,
191 expect![["test and test2 are enabled and a is disabled"]],
192 );
193}