aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2020-10-21 12:57:12 +0100
committerJonas Schievink <[email protected]>2020-10-21 12:57:12 +0100
commit2bc4c1ff31b6ef0ca1848a7ce12f981b93dcded0 (patch)
treee4a775098e38cf46b48a6daad58566e652d97536
parent20d369a826e8f333cba1988325480a49a730f00e (diff)
Simplify cfg representation
-rw-r--r--crates/cfg/src/cfg_expr.rs44
-rw-r--r--crates/cfg/src/lib.rs22
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs6
3 files changed, 41 insertions, 31 deletions
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs
index 336fe25bc..db3655b74 100644
--- a/crates/cfg/src/cfg_expr.rs
+++ b/crates/cfg/src/cfg_expr.rs
@@ -6,26 +6,42 @@ use std::slice::Iter as SliceIter;
6 6
7use tt::SmolStr; 7use tt::SmolStr;
8 8
9/// A simple configuration value passed in from the outside.
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub enum CfgAtom {
12 /// eg. `#[cfg(test)]`
13 Flag(SmolStr),
14 /// eg. `#[cfg(target_os = "linux")]`
15 ///
16 /// Note that a key can have multiple values that are all considered "active" at the same time.
17 /// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`.
18 KeyValue { key: SmolStr, value: SmolStr },
19}
20
9#[derive(Debug, Clone, PartialEq, Eq)] 21#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum CfgExpr { 22pub enum CfgExpr {
11 Invalid, 23 Invalid,
12 Atom(SmolStr), 24 Atom(CfgAtom),
13 KeyValue { key: SmolStr, value: SmolStr },
14 All(Vec<CfgExpr>), 25 All(Vec<CfgExpr>),
15 Any(Vec<CfgExpr>), 26 Any(Vec<CfgExpr>),
16 Not(Box<CfgExpr>), 27 Not(Box<CfgExpr>),
17} 28}
18 29
30impl From<CfgAtom> for CfgExpr {
31 fn from(atom: CfgAtom) -> Self {
32 CfgExpr::Atom(atom)
33 }
34}
35
19impl CfgExpr { 36impl CfgExpr {
20 pub fn parse(tt: &tt::Subtree) -> CfgExpr { 37 pub fn parse(tt: &tt::Subtree) -> CfgExpr {
21 next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) 38 next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
22 } 39 }
23 /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates. 40 /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
24 pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> { 41 pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
25 match self { 42 match self {
26 CfgExpr::Invalid => None, 43 CfgExpr::Invalid => None,
27 CfgExpr::Atom(name) => Some(query(name, None)), 44 CfgExpr::Atom(atom) => Some(query(atom)),
28 CfgExpr::KeyValue { key, value } => Some(query(key, Some(value))),
29 CfgExpr::All(preds) => { 45 CfgExpr::All(preds) => {
30 preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?)) 46 preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?))
31 } 47 }
@@ -54,7 +70,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
54 // FIXME: escape? raw string? 70 // FIXME: escape? raw string?
55 let value = 71 let value =
56 SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); 72 SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
57 CfgExpr::KeyValue { key: name, value } 73 CfgAtom::KeyValue { key: name, value }.into()
58 } 74 }
59 _ => return Some(CfgExpr::Invalid), 75 _ => return Some(CfgExpr::Invalid),
60 } 76 }
@@ -70,7 +86,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
70 _ => CfgExpr::Invalid, 86 _ => CfgExpr::Invalid,
71 } 87 }
72 } 88 }
73 _ => CfgExpr::Atom(name), 89 _ => CfgAtom::Flag(name).into(),
74 }; 90 };
75 91
76 // Eat comma separator 92 // Eat comma separator
@@ -101,22 +117,22 @@ mod tests {
101 117
102 #[test] 118 #[test]
103 fn test_cfg_expr_parser() { 119 fn test_cfg_expr_parser() {
104 assert_parse_result("#![cfg(foo)]", CfgExpr::Atom("foo".into())); 120 assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into());
105 assert_parse_result("#![cfg(foo,)]", CfgExpr::Atom("foo".into())); 121 assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into());
106 assert_parse_result( 122 assert_parse_result(
107 "#![cfg(not(foo))]", 123 "#![cfg(not(foo))]",
108 CfgExpr::Not(Box::new(CfgExpr::Atom("foo".into()))), 124 CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())),
109 ); 125 );
110 assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); 126 assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid);
111 127
112 // Only take the first 128 // Only take the first
113 assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgExpr::Atom("foo".into())); 129 assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into());
114 130
115 assert_parse_result( 131 assert_parse_result(
116 r#"#![cfg(all(foo, bar = "baz"))]"#, 132 r#"#![cfg(all(foo, bar = "baz"))]"#,
117 CfgExpr::All(vec![ 133 CfgExpr::All(vec![
118 CfgExpr::Atom("foo".into()), 134 CfgAtom::Flag("foo".into()).into(),
119 CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, 135 CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
120 ]), 136 ]),
121 ); 137 );
122 138
@@ -126,7 +142,7 @@ mod tests {
126 CfgExpr::Not(Box::new(CfgExpr::Invalid)), 142 CfgExpr::Not(Box::new(CfgExpr::Invalid)),
127 CfgExpr::All(vec![]), 143 CfgExpr::All(vec![]),
128 CfgExpr::Invalid, 144 CfgExpr::Invalid,
129 CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() }, 145 CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
130 ]), 146 ]),
131 ); 147 );
132 } 148 }
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index a9d50e698..35f540ac3 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -5,7 +5,7 @@ mod cfg_expr;
5use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
6use tt::SmolStr; 6use tt::SmolStr;
7 7
8pub use cfg_expr::CfgExpr; 8pub use cfg_expr::{CfgAtom, CfgExpr};
9 9
10/// Configuration options used for conditional compilition on items with `cfg` attributes. 10/// Configuration options used for conditional compilition on items with `cfg` attributes.
11/// We have two kind of options in different namespaces: atomic options like `unix`, and 11/// We have two kind of options in different namespaces: atomic options like `unix`, and
@@ -19,33 +19,25 @@ pub use cfg_expr::CfgExpr;
19/// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options 19/// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options
20#[derive(Debug, Clone, PartialEq, Eq, Default)] 20#[derive(Debug, Clone, PartialEq, Eq, Default)]
21pub struct CfgOptions { 21pub struct CfgOptions {
22 atoms: FxHashSet<SmolStr>, 22 enabled: FxHashSet<CfgAtom>,
23 key_values: FxHashSet<(SmolStr, SmolStr)>,
24} 23}
25 24
26impl CfgOptions { 25impl CfgOptions {
27 pub fn check(&self, cfg: &CfgExpr) -> Option<bool> { 26 pub fn check(&self, cfg: &CfgExpr) -> Option<bool> {
28 cfg.fold(&|key, value| match value { 27 cfg.fold(&|atom| self.enabled.contains(atom))
29 None => self.atoms.contains(key),
30 Some(value) => self.key_values.contains(&(key.clone(), value.clone())),
31 })
32 } 28 }
33 29
34 pub fn insert_atom(&mut self, key: SmolStr) { 30 pub fn insert_atom(&mut self, key: SmolStr) {
35 self.atoms.insert(key); 31 self.enabled.insert(CfgAtom::Flag(key));
36 } 32 }
37 33
38 pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { 34 pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) {
39 self.key_values.insert((key, value)); 35 self.enabled.insert(CfgAtom::KeyValue { key, value });
40 } 36 }
41 37
42 pub fn append(&mut self, other: &CfgOptions) { 38 pub fn append(&mut self, other: &CfgOptions) {
43 for atom in &other.atoms { 39 for atom in &other.enabled {
44 self.atoms.insert(atom.clone()); 40 self.enabled.insert(atom.clone());
45 }
46
47 for (key, value) in &other.key_values {
48 self.key_values.insert((key.clone(), value.clone()));
49 } 41 }
50 } 42 }
51} 43}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index ddc028148..7da935464 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -1,6 +1,6 @@
1//! See `CargoTargetSpec` 1//! See `CargoTargetSpec`
2 2
3use cfg::CfgExpr; 3use cfg::{CfgAtom, CfgExpr};
4use ide::{FileId, RunnableKind, TestId}; 4use ide::{FileId, RunnableKind, TestId};
5use project_model::{self, TargetKind}; 5use project_model::{self, TargetKind};
6use vfs::AbsPathBuf; 6use vfs::AbsPathBuf;
@@ -160,7 +160,9 @@ impl CargoTargetSpec {
160/// Fill minimal features needed 160/// Fill minimal features needed
161fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) { 161fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
162 match cfg_expr { 162 match cfg_expr {
163 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()), 163 CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if key == "feature" => {
164 features.push(value.to_string())
165 }
164 CfgExpr::All(preds) => { 166 CfgExpr::All(preds) => {
165 preds.iter().for_each(|cfg| required_features(cfg, features)); 167 preds.iter().for_each(|cfg| required_features(cfg, features));
166 } 168 }