]> git.lizzy.rs Git - rust.git/blob - crates/cfg/src/tests.rs
Make `ast_to_token_tree` infallible
[rust.git] / crates / cfg / src / tests.rs
1 use expect_test::{expect, Expect};
2 use mbe::ast_to_token_tree;
3 use syntax::{ast, AstNode};
4
5 use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
6
7 fn 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)
12     };
13     let cfg = CfgExpr::parse(&tt);
14     assert_eq!(cfg, expected);
15 }
16
17 fn 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)
22     };
23     let cfg = CfgExpr::parse(&tt);
24     let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
25     expect.assert_eq(&actual);
26 }
27
28 fn 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)
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]
41 fn 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)
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]
54 fn 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]
86 fn 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]
98 fn 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]
115 fn 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]
124 fn 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]
134 fn 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]
153 fn 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]
164 fn 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 }